Repository: statisticsnorway/datadoc-editor (language: python)

Issue:
Take Pydantic models into use
Use the Pydantic models for validating all data received from the user. These are also used as type hints where possible. Implement reading from existing metadata file and writing to a new metadata file.
Refactor DatasetSchema
Make class, and subclasses for each filetype


Discussion / hints:

Fix patch:
diff --git a/DataDoc.ipynb b/DataDoc.ipynb
index a31304b2..66ffe245 100644
--- a/DataDoc.ipynb
+++ b/DataDoc.ipynb
@@ -2,176 +2,20 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "id": "5ef8c5e7-6637-4bbb-b79b-f11063f62d1b",
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "JUPYTERHUB_USER env variable not set, using default for current_user\n"
-     ]
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "9119e7c078774f1f85d2bacfa37c34fd",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "HTML(value=\"<h1 style='font-size:20px'>DataDoc -\\n            dokumentasjon av datasett og variabler</h1>\")"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "19c0692e54584f87b0dd60272581714c",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "HBox(children=(Button(description='Lagre', style=ButtonStyle(), tooltip='Lagre datadokumentasjon til fil (JSON…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "ed9e8d1abbeb4b63b1607549696b46f2",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "HTML(value=\"<h1 style='font-size:16px'>Datasett</h1>\")"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "2ffa0bbfe68d456ca7f80b5a72ef9a26",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Text(value='person_data_v1', description='Kortnavn:', placeholder='Oppgi datasett kortnavn')"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "8caa82d8159f48be92eb8f99c584845a",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Text(value='', description='Navn:', layout=Layout(height='auto', width='50%'), placeholder='Oppgi datasett nav…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "e46650c2ce6e4fb98f42c58d5d824722",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Textarea(value='', description='Beskrivelse:', layout=Layout(height='auto', width='50%'), placeholder='Oppgi d…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "228ce0f6bc344cd18cdd563540285f29",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Accordion(children=(VBox(children=(Dropdown(description='Datatilstand:', index=2, options=(('Kildedata', 'SOUR…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "ba5fdcb617604b0980b7b45f45f2d244",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Accordion(children=(Sheet(cells=(Cell(column_end=0, column_start=0, row_end=0, row_start=0, type='text', value…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "48dc41cefe2c481fa4b072bc7a25441c",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "HTML(value=\"<a style='font-size:16px;color:blue' href=https://www.ssb.no/a/metadata/definisjoner/variabler/mai…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "text/plain": [
-       "<datadoc.DataDoc.DataDocGui at 0x109673940>"
-      ]
-     },
-     "execution_count": 1,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
+   "outputs": [],
    "source": [
-    "import datadoc.DataDoc as dd\n",
-    "dd.DataDocGui('./klargjorte_data/person_data_v1.parquet')"
+    "%run -i datadoc/DataDocDash.py"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "a81dcc0a",
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
-  "interpreter": {
-   "hash": "d59f6e9384a094d52d30bff20975cbd1f3d4c53c13e2f48c405d3fc75a05a1f6"
-  },
   "kernelspec": {
-   "display_name": "datadoc",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
-   "name": "datadoc"
+   "name": "python3"
   },
   "language_info": {
    "codemirror_mode": {
@@ -184,6 +28,11 @@
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
    "version": "3.8.12"
+  },
+  "vscode": {
+   "interpreter": {
+    "hash": "d59f6e9384a094d52d30bff20975cbd1f3d4c53c13e2f48c405d3fc75a05a1f6"
+   }
   }
  },
  "nbformat": 4,
diff --git a/DataDocDash.py b/DataDocDash.py
deleted file mode 100644
index 96f28c6f..00000000
--- a/DataDocDash.py
+++ /dev/null
@@ -1,349 +0,0 @@
-import itertools
-import os
-import re
-from jupyter_dash import JupyterDash
-from dash import Dash, dash_table, html, Input, Output, dcc
-import dash_bootstrap_components as dbc
-from pydantic import ValidationError
-import pandas as pd
-from datadoc.DataDocMetadata import DataDocMetadata
-from datadoc.DisplayVariables import DISPLAY_VARIABLES, VariableIdentifiers
-from datadoc.Model import DataDocVariable, Datatype, VariableRole
-
-# metadata = DataDocMetadata("./klargjorte_data/person_data_v1.parquet").meta
-metadata = DataDocMetadata("./datadoc/tests/resources/sasdata.sas7bdat").meta
-variables = metadata["variables"]
-df = pd.DataFrame(variables)
-
-app = Dash(
-    name="DataDoc", external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP]
-)
-
-COLORS = {"dark_1": "#F0F8F9", "green_1": "#ECFEED", "green_4": "#00824D"}
-
-dataset_details_inputs = [
-    {
-        "name": "Kort Navn",
-        "input_component": dcc.Input(
-            placeholder="Et teknisk navn, ofte lik filnavnet",
-            style={"width": "100%"},
-            value=metadata["shortName"],
-            className="ssb-input",
-        ),
-    },
-    {
-        "name": "Navn",
-        "input_component": dcc.Input(
-            placeholder="Beskrivende navn for datasettet",
-            style={"width": "100%"},
-            className="ssb-input",
-        ),
-    },
-    {
-        "name": "Beskrivelse",
-        "input_component": dcc.Textarea(
-            placeholder="Besrive egenskaper av datasettet",
-            style={"width": "100%"},
-            className="ssb-input",
-        ),
-    },
-    {
-        "name": "Tilstand",
-        "input_component": dcc.Dropdown(
-            placeholder="Velg fra listen",
-            options=[
-                {"label": o, "value": o}
-                for o in [
-                    "Kildedata",
-                    "Inndata",
-                    "Klargjorte data",
-                    "Utdata",
-                    "Statistikk",
-                ]
-            ],
-            style={"width": "100%"},
-            className="ssb-dropdown",
-        ),
-    },
-    {
-        "name": "Versjon",
-        "input_component": dcc.Input(
-            placeholder=1,
-            type="number",
-            style={"width": "100%"},
-            value=metadata["version"],
-            className="ssb-input",
-        ),
-    },
-    {
-        "name": "Datasett sti",
-        "input_component": dcc.Input(
-            placeholder="Sti til datasett fil",
-            style={"width": "100%"},
-            value=metadata["dataSourcePath"],
-            className="ssb-input",
-        ),
-    },
-    {
-        "name": "Opprettet av",
-        "input_component": dcc.Input(
-            placeholder="kari.nordman@ssb.no",
-            type="email",
-            style={"width": "100%"},
-            value=metadata["createdBy"],
-            className="ssb-input",
-        ),
-    },
-    {
-        "name": "Opprettet dato",
-        "input_component": dcc.Input(
-            style={"width": "100%"},
-            value=metadata["createdDate"],
-            className="ssb-input",
-        ),
-    },
-]
-
-
-def make_ssb_styled_tab(label: str, content: dbc.Container) -> dbc.Tab:
-    return dbc.Tab(
-        label=label,
-        # Replace all whitespace with dashes
-        tab_id=re.sub(r"\s+", "-", label.lower()),
-        label_class_name="ssb-tabs navigation-item",
-        label_style={"margin-left": "10px", "margin-right": "10px"},
-        style={"backgroundColor": COLORS["green_1"], "padding": "4px"},
-        children=content,
-    )
-
-
-dataset_details = make_ssb_styled_tab(
-    "Datasett",
-    dbc.Container(
-        [
-            dbc.Row(html.H2("Datasett detaljer", className="ssb-title")),
-            dbc.Row(
-                [
-                    dbc.Row(
-                        [
-                            dbc.Col(html.Label(input["name"])),
-                            dbc.Col(input["input_component"], width=4),
-                            dbc.Col(width=6),
-                        ]
-                    )
-                    for input in dataset_details_inputs
-                ]
-            ),
-        ],
-    ),
-)
-
-variables_table = make_ssb_styled_tab(
-    "Variabler",
-    dbc.Container(
-        children=[
-            dbc.Row(html.H2("Variabel detaljer", className="ssb-title")),
-            dbc.Row(
-                dash_table.DataTable(
-                    id="variables-table",
-                    # Populate fields with known values
-                    data=df[
-                        [
-                            VariableIdentifiers.SHORT_NAME.value,
-                            VariableIdentifiers.DATA_TYPE.value,
-                        ]
-                    ].to_dict("records"),
-                    # Define columns based on the information in DISPLAY_VARIABLES
-                    columns=[
-                        {
-                            "name": variable.display_name,
-                            "id": variable.identifier,
-                            "editable": variable.editable,
-                            "presentation": variable.presentation,
-                            "hideable": variable.editable,
-                        }
-                        for variable in DISPLAY_VARIABLES.values()
-                    ],
-                    # Non-obligatory variables are hidden by default
-                    hidden_columns=[
-                        v.identifier
-                        for v in DISPLAY_VARIABLES.values()
-                        if v.obligatory is False
-                    ],
-                    # Include tooltips for all columns
-                    tooltip_header={
-                        v.identifier: v.description for v in DISPLAY_VARIABLES.values()
-                    },
-                    editable=True,
-                    # Enable sorting and pagination
-                    sort_action="native",
-                    page_action="native",
-                    # Populate the options for all dropdown values
-                    dropdown={
-                        v.identifier: v.options
-                        for v in DISPLAY_VARIABLES.values()
-                        if v.options
-                    },
-                )
-            ),
-        ],
-    ),
-)
-
-header = dbc.CardBody(
-    dbc.Row(
-        children=[
-            html.Link(rel="stylesheet", href="/assets/bundle.css"),
-            html.H1("DataDoc", className="ssb-title", style={"color": "white"}),
-        ],
-    ),
-    style={"backgroundColor": COLORS["green_4"]},
-)
-
-controls_bar = dbc.CardBody(
-    style={"padding": "4px"},
-    children=[
-        dbc.Row(
-            [
-                dbc.Col(
-                    dbc.Button(
-                        [
-                            html.I(
-                                className="bi bi-save",
-                                style={"padding-right": "10px"},
-                            ),
-                            "   Lagre",
-                        ],
-                        class_name="ssb-btn primary-btn",
-                    ),
-                ),
-                dbc.Col(
-                    dcc.Dropdown(
-                        placeholder="Velg språk",
-                        className="ssb-dropdown",
-                        options=["Norsk Bokmål", "Nynorsk", "English"],
-                        value="Norsk Bokmål",
-                        disabled=True,
-                    ),
-                    align="end",
-                    width="auto",
-                ),
-            ]
-        )
-    ],
-)
-
-validation_error = dbc.Alert(
-    id="validation-error",
-    is_open=False,
-    dismissable=True,
-    fade=True,
-    class_name="ssb-dialog warning",
-    children=[
-        dbc.Row(
-            [
-                dbc.Col(
-                    width=1,
-                    children=[
-                        html.Div(
-                            className="ssb-dialog warning icon-panel",
-                            children=[
-                                html.I(
-                                    className="bi bi-exclamation-triangle",
-                                ),
-                            ],
-                        )
-                    ],
-                ),
-                dbc.Col(
-                    [
-                        html.H5(
-                            "Failed validation",
-                        ),
-                        dcc.Markdown(
-                            id="validation-explanation",
-                        ),
-                    ]
-                ),
-            ]
-        )
-    ],
-    color="danger",
-)
-
-app.layout = dbc.Container(
-    style={"padding": "4px"},
-    children=[
-        header,
-        controls_bar,
-        dbc.CardBody(
-            style={"padding": "4px"},
-            children=[
-                dbc.Tabs(
-                    id="tabs",
-                    class_name="ssb-tabs",
-                    children=[
-                        dataset_details,
-                        variables_table,
-                    ],
-                ),
-            ],
-        ),
-        validation_error,
-    ],
-)
-
-
-@app.callback(
-    Output("variables-table", "data"),
-    Output("validation-error", "is_open"),
-    Output("validation-explanation", "children"),
-    Input("variables-table", "data"),
-    Input("variables-table", "data_previous"),
-)
-def validate_input(data, data_previous):
-    updated_row_id = None
-    updated_column_id = None
-    new_value = None
-    show_error = False
-    error_explanation = ""
-    output_data = []
-    # What has changed?
-    if data is not None and data_previous is not None:
-        for i in range(len(data)):
-            update_diff = list(data[i].items() - data_previous[i].items())
-            if update_diff:
-                print(update_diff)
-                updated_row_id = data[i]["shortName"]
-                updated_column_id = update_diff[-1][0]
-                new_value = update_diff[-1][-1]
-                print(
-                    f"Row: {updated_row_id} Column: {updated_column_id} New value: {new_value}"
-                )
-    # Is the change valid?
-    try:
-        if updated_column_id == "name":
-            validated_data = DataDocVariable(name=new_value)
-        elif updated_column_id == "dataType":
-            validated_data = DataDocVariable(datatype=Datatype[new_value])
-        elif updated_column_id == "definitionUri":
-            validated_data = DataDocVariable(definition_uri=new_value)
-        else:
-            validated_data = None
-            print("Unexpected type")
-    except ValidationError as e:
-        show_error = True
-        error_explanation = f"`{e}`"
-        output_data = data_previous
-        print(error_explanation)
-    else:
-        output_data = data
-        print(f"Success: {validated_data}")
-
-    # IF NOT: Return in which way it is not valid
-    # IF IT IS: Return the input data unchanged
-    return output_data, show_error, error_explanation
-
-
-if __name__ == "__main__":
-    app.run_server(debug=True)
diff --git a/README.md b/README.md
index b95958b6..90d5642f 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,16 @@
-# datadoc-prototype
+# DataDoc
+
+Document datasets in SSB
 
 ## Usage example
 
-Data Documentation (DataDoc) Prototyp for Jupyter
-![DataDoc](./doc/DataDoc_prototype_001.png)
+1. Clone this repo to your Jupyter instance
+1. Open the `DataDoc.ipynb` Notebook and run the cell to see the example dataset
+1. Run `%run -i datadoc/DataDocDash.py --dataset-path ./path/to/your/dataset` to run Datadoc on a dataset of your choosing.
+1. Complete metadata as you desire
+1. Click `Lagre` to save your metadata together with your dataset
+
+![DataDoc](./doc/datadoc.png)
 
 ## Contributing
 
diff --git a/datadoc/DataDoc.py b/datadoc/DataDoc.py
deleted file mode 100644
index cbe42e55..00000000
--- a/datadoc/DataDoc.py
+++ /dev/null
@@ -1,295 +0,0 @@
-from xml.dom.minidom import parseString
-
-import ipysheet
-import requests
-from ipysheet import sheet
-from IPython.display import display
-from ipywidgets import Layout, widgets
-
-from .DataDocMetadata import DataDocMetadata
-from .VariableDefinition import VariableDefinition
-
-# Example usage:
-# --------------
-# import data_doc as datadoc
-# dd = datadoc.DataDocGui(dataset="./my_directory/my_dataset.parquet")
-
-
-class DataDocGui:
-    def __init__(self, dataset):
-        self.dataset = dataset
-        self.datadoc_meta = DataDocMetadata(self.dataset)
-        self.meta = self.datadoc_meta.meta
-
-        # Dataset GUI elements
-        self.datadoc_header_text = widgets.HTML(
-            value="""<h1 style='font-size:20px'>DataDoc -
-            dokumentasjon av datasett og variabler</h1>"""
-        )
-        self.button_save = widgets.Button(
-            description="Lagre", tooltip="Lagre datadokumentasjon til fil (JSON-fil)"
-        )
-        self.button_save.on_click(self.save_button_clicked)
-
-        self.dropdown_language_code = widgets.Dropdown(
-            options=[
-                ("Norsk (no)", "no"),
-                ("Norsk bokmål (nb)", "nb"),
-                ("Nynorsk (nn)", "nn"),
-                ("Engelsk (en)", "en"),
-            ],
-            value="no",
-            description="Språk:",
-        )
-        self.datadoc_header_hbox = widgets.HBox(
-            children=[self.button_save, self.dropdown_language_code],
-            layout=Layout(border="1px solid grey"),
-        )
-
-        self.ds_header2 = widgets.HTML(value="<h1 style='font-size:16px'>Datasett</h1>")
-        self.ds_short_name = widgets.Text(
-            value=self.meta["shortName"],
-            placeholder="Oppgi datasett kortnavn",
-            description="Kortnavn:",
-            disabled=False,
-        )
-        self.ds_name = widgets.Text(
-            value=self.get_language_value("name", self.dropdown_language_code.value),
-            placeholder="Oppgi datasett navn (tittel)",
-            description="Navn:",
-            disabled=False,
-            layout={"height": "auto", "width": "50%"},
-        )
-        self.ds_description = widgets.Textarea(
-            value=self.get_language_value(
-                "description", self.dropdown_language_code.value
-            ),
-            placeholder="Oppgi datasett beskrivelse",
-            description="Beskrivelse:",
-            disabled=False,
-            layout={"height": "auto", "width": "50%"},
-        )
-        self.ds_data_set_state = widgets.Dropdown(
-            options=[
-                ("Kildedata", "SOURCE_DATA"),
-                ("Inndata", "INPUT_DATA"),
-                ("Klargjorte data", "PROCESSED_DATA"),
-            ],
-            value=self.meta["dataSetState"],
-            description="Datatilstand:",
-        )
-        self.ds_version = widgets.Text(
-            value=self.meta["version"],
-            placeholder="Oppgi versjon",
-            description="Versjon:",
-            disabled=False,
-        )
-        self.ds_data_source_path = widgets.Text(
-            value=self.meta["dataSourcePath"],
-            placeholder="filsti",
-            description="Datasett sti:",
-            disabled=True,
-            layout=Layout(width="50%"),
-        )
-        self.ds_created_by = widgets.Text(
-            value=self.meta["createdBy"],
-            placeholder="initialer",
-            description="Opprettet av:",
-            disabled=True,
-        )
-        self.ds_created_date = widgets.Text(
-            value=self.meta["createdDate"],
-            placeholder="dato",
-            description="Oppr. dato:",
-            disabled=True,
-        )
-
-        self.ds_vbox_details = widgets.VBox(
-            children=[
-                self.ds_data_set_state,
-                self.ds_version,
-                self.ds_data_source_path,
-                self.ds_created_by,
-                self.ds_created_date,
-            ]
-        )
-        # Link to VarDef
-        self.vardef_link = widgets.HTML(
-            value=(
-                "<a style='font-size:16px;color:blue'"
-                " href=https://www.ssb.no/a/metadata/definisjoner/variabler/main.html"
-                " target='_blank'>Gå til VarDef (variabeldefinisjoner)</a>"
-            )
-        )
-
-        # Varible GUI elements
-        self.variable_sheet = ipysheet.sheet(
-            key="variable_sheet",
-            column_headers=[
-                "Kortnavn",
-                "Navn",
-                "Datatype",
-                "Variabelelens rolle i datasettet",
-                "VarDef ID",
-                "Kommentar",
-            ],
-            row_headers=False,
-            rows=len(self.meta["variables"]),
-            columns=6,  # TODO må justeres hvis flere kolonner skal inn
-        )
-        sheet("variable_sheet")  # Activate the wanted sheet with "sheet key"
-        self.col_num_short_name = 0
-        self.col_num_name = 1
-        self.col_num_datatype = 2
-        self.col_num_variable_role = 3
-        self.col_num_definition_uri = 4
-        self.col_num_comment = 5
-
-        # Set initial values in each column for each variable
-        for row_num, variable in enumerate(self.meta["variables"]):
-            ipysheet.cell(
-                row_num, self.col_num_short_name, value=str(variable["shortName"])
-            )
-            ipysheet.cell(row_num, self.col_num_name, value=variable["name"])
-            ipysheet.cell(
-                row_num,
-                self.col_num_datatype,
-                value=variable["dataType"],
-                choice=["STRING", "INTEGER", "FLOAT", "DATETIME", "BOOLEAN"],
-            )
-            ipysheet.cell(
-                row_num,
-                self.col_num_variable_role,
-                value=variable["variableRole"],
-                choice=[
-                    "IDENTIFIER",
-                    "MEASURE",
-                    "START_TIME",
-                    "STOP_TIME",
-                    "ATTRIBUTE",
-                ],
-            )
-            ipysheet.cell(row_num, self.col_num_comment, value=variable["comment"])
-            try:
-                # This field isn't set for all variables, if it doesn't exist we
-                # use None for the value
-                existing_definition_uri = variable["definitionUri"]
-            except KeyError:
-                existing_definition_uri = None
-            tmp_definition_uri = ipysheet.cell(
-                row_num, self.col_num_definition_uri, value=existing_definition_uri
-            )
-            tmp_definition_uri.observe(self.cell_definition_uri_changed, "value")
-
-        self.ds_accordion = widgets.Accordion(
-            children=[self.variable_sheet], layout=Layout(display="flex")
-        )
-        self.ds_accordion.set_title(0, "Variabler som inngår i datasettet")
-        self.ds_accordion.selected_index = 0
-
-        self.ds_accordion2 = widgets.Accordion(children=[self.ds_vbox_details])
-        self.ds_accordion2.set_title(0, "Datasett detaljer")
-        # Dispaly GUI
-        self.setup_gui_dataset_elements()
-
-    def get_language_value(self, language_element, language_code):
-        # print(self.meta[language_element])
-        if language_element in self.meta:
-            for language in self.meta[language_element]:
-                if language["languageCode"] == language_code:
-                    return language["value"]
-        return ""  # Element or language not found
-
-    def create_or_update_language_value(
-        self, language_element, language_code, language_value
-    ):
-        if language_element in self.meta:
-            for language in self.meta[language_element]:
-                if language["languageCode"] == language_code:
-                    # Update existing
-                    language.update({"value": language_value})
-                    return
-        # Not found, create new
-        self.meta[language_element].append(
-            {"languageCode": language_code, "value": language_value}
-        )
-
-    def update_dataset_metadata_from_gui_elements(self):
-        # self.meta['name'].append({'languageCode': 'no', 'value': self.ds_name.value})
-        self.create_or_update_language_value(
-            "name", self.dropdown_language_code.value, self.ds_name.value
-        )
-        self.create_or_update_language_value(
-            "description", self.dropdown_language_code.value, self.ds_description.value
-        )
-        self.update_variable_metadata_from_gui_elements()
-
-    def update_variable_metadata_from_gui_elements(self):
-        # print(self.variable_sheet.cells)
-        self.meta["variables"] = []
-        variable = {}
-        for cell_item in self.variable_sheet.cells:
-            if cell_item.column_start == 0:
-                variable["shortName"] = cell_item.value
-            elif cell_item.column_start == 1:
-                variable["name"] = cell_item.value  # TODO: Støtte flere språk!
-            elif cell_item.column_start == 2:
-                variable["dataType"] = cell_item.value
-            elif cell_item.column_start == 3:
-                variable["variableRole"] = cell_item.value
-            elif cell_item.column_start == 4:
-                variable["definitionUri"] = cell_item.value
-            elif cell_item.column_start == 5:
-                variable["comment"] = cell_item.value  # TODO: Støtte flere språk!
-                # TODO: Mappe disse mot variabel-sheet i GUI også!!!
-                variable["populationDescription"] = None  # TODO: Støtte flere språk!
-                variable["temporalityType"] = None
-                variable["mesurementType"] = None
-                variable["measurementUnit"] = None
-                variable["format"] = None
-                variable["sentinelAndMissingValueUri"] = []
-                variable["unitType"] = None
-                variable["foreignKeyType"] = None
-                self.meta["variables"].append(variable)  # Legger til ny variabel
-                variable = {}  # Nullstiller før neste runde i loopen
-
-    def save_button_clicked(self, b):
-        self.update_dataset_metadata_from_gui_elements()
-        self.datadoc_meta.write_metadata_document()
-        print(
-            "DataDoc metadata saved to file "
-            + str(self.datadoc_meta.metadata_document_full_path)
-        )
-
-    def setup_gui_dataset_elements(self):
-        # Må settes for at widget skal vises i notebook
-        display(
-            self.datadoc_header_text,
-            self.datadoc_header_hbox,
-            self.ds_header2,
-            self.ds_short_name,
-            self.ds_name,
-            self.ds_description,
-            self.ds_accordion2,
-            self.ds_accordion,
-            self.vardef_link,
-        )
-
-    def cell_definition_uri_changed(self, change):
-        # For more information aout observe and events:
-        # https://ipysheet.readthedocs.io/en/stable/index.html?highlight=observe#Events
-        try:
-            vardef_id = change["new"]
-            cell_changed = change["owner"]
-            row_num = cell_changed.row_start
-
-            vardef = VariableDefinition(vardef_id)
-            # TODO: støtte flere språk!
-            ipysheet.cell(row_num, self.col_num_name, value=vardef.vardef_name)
-            ipysheet.cell(row_num, self.col_num_comment, vardef.vardef_definition)
-        except requests.RequestException:
-            print('Fant ikke variabeldefinisjon med VarDef-ID "' + vardef_id + '"')
-
-
-if __name__ == "__main__":
-    DataDocGui("./klargjorte_data/person_data_v1.parquet")
diff --git a/datadoc/DataDocDash.py b/datadoc/DataDocDash.py
new file mode 100644
index 00000000..6b0855a6
--- /dev/null
+++ b/datadoc/DataDocDash.py
@@ -0,0 +1,472 @@
+import re
+import argparse
+from typing import Type
+from dash import Dash, dash_table, html, Input, Output, dcc, State, MATCH, ctx
+import dash_bootstrap_components as dbc
+from pydantic import ValidationError
+
+from datadoc.DataDocMetadata import DataDocMetadata
+from datadoc.DisplayVariables import DISPLAY_VARIABLES, VariableIdentifiers
+from datadoc.Model import DataSetState
+from datadoc.utils import running_in_notebook
+
+
+def main(dash_class: Type[Dash], dataset_path: str) -> Dash:
+
+    datadoc_metadata = DataDocMetadata(dataset_path)
+    metadata = datadoc_metadata.dataset_metadata
+
+    DATASET_METADATA_INPUT = "dataset-metadata-input"
+
+    app = dash_class(
+        name="DataDoc", external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP]
+    )
+
+    COLORS = {"dark_1": "#F0F8F9", "green_1": "#ECFEED", "green_4": "#00824D"}
+
+    dataset_details_inputs = [
+        {
+            "name": "Kort Navn",
+            "input_component": dcc.Input(
+                placeholder="Et teknisk navn, ofte lik filnavnet",
+                debounce=True,
+                style={"width": "100%"},
+                value=metadata.short_name,
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "short_name",
+                },
+                className="ssb-input",
+            ),
+        },
+        {
+            "name": "Navn",
+            "input_component": dcc.Input(
+                placeholder="Beskrivende navn for datasettet",
+                debounce=True,
+                style={"width": "100%"},
+                value=metadata.name,
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "name",
+                },
+                className="ssb-input",
+            ),
+        },
+        {
+            "name": "Beskrivelse",
+            "input_component": dcc.Textarea(
+                placeholder="Besrive egenskaper av datasettet",
+                style={"width": "100%"},
+                value=metadata.description,
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "description",
+                },
+                className="ssb-input",
+            ),
+        },
+        {
+            "name": "Tilstand",
+            "input_component": dcc.Dropdown(
+                placeholder="Velg fra listen",
+                options=[
+                    {"label": label, "value": value}
+                    for label, value in [
+                        ("Kildedata", DataSetState.SOURCE_DATA.name),
+                        ("Inndata", DataSetState.INPUT_DATA.name),
+                        ("Klargjorte data", DataSetState.PROCESSED_DATA.name),
+                        ("Utdata", DataSetState.OUTPUT_DATA.name),
+                        ("Statistikk", DataSetState.STATISTIC.name),
+                    ]
+                ],
+                value=metadata.dataset_state,
+                style={"width": "100%"},
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "dataset_state",
+                },
+                className="ssb-dropdown",
+            ),
+        },
+        {
+            "name": "Versjon",
+            "input_component": dcc.Input(
+                placeholder=1,
+                debounce=True,
+                type="number",
+                style={"width": "100%"},
+                value=metadata.version,
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "version",
+                },
+                className="ssb-input",
+            ),
+        },
+        {
+            "name": "Datasett sti",
+            "input_component": dcc.Input(
+                placeholder="Sti til datasett fil",
+                debounce=True,
+                style={"width": "100%"},
+                value=metadata.data_source_path,
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "data_source_path",
+                },
+                className="ssb-input",
+            ),
+        },
+        {
+            "name": "Opprettet av",
+            "input_component": dcc.Input(
+                placeholder="kari.nordman@ssb.no",
+                debounce=True,
+                type="email",
+                style={"width": "100%"},
+                value=metadata.created_by,
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "created_by",
+                },
+                className="ssb-input",
+            ),
+        },
+        {
+            "name": "Opprettet dato",
+            "input_component": dcc.Input(
+                debounce=True,
+                style={"width": "100%"},
+                value=metadata.created_date,
+                id={
+                    "type": DATASET_METADATA_INPUT,
+                    "id": "created_date",
+                },
+                className="ssb-input",
+            ),
+        },
+    ]
+
+    def make_ssb_styled_tab(label: str, content: dbc.Container) -> dbc.Tab:
+        return dbc.Tab(
+            label=label,
+            # Replace all whitespace with dashes
+            tab_id=re.sub(r"\s+", "-", label.lower()),
+            label_class_name="ssb-tabs navigation-item",
+            label_style={"margin-left": "10px", "margin-right": "10px"},
+            style={"backgroundColor": COLORS["green_1"], "padding": "4px"},
+            children=content,
+        )
+
+    dataset_details = make_ssb_styled_tab(
+        "Datasett",
+        dbc.Container(
+            [
+                dbc.Row(html.H2("Datasett detaljer", className="ssb-title")),
+                dbc.Row(
+                    [
+                        dbc.Row(
+                            [
+                                dbc.Col(html.Label(input["name"])),
+                                dbc.Col(input["input_component"], width=4),
+                                dbc.Col(width=6),
+                            ]
+                        )
+                        for input in dataset_details_inputs
+                    ]
+                ),
+            ],
+        ),
+    )
+
+    variables_table = make_ssb_styled_tab(
+        "Variabler",
+        dbc.Container(
+            children=[
+                dbc.Row(html.H2("Variabel detaljer", className="ssb-title")),
+                dbc.Row(
+                    dash_table.DataTable(
+                        id="variables-table",
+                        # Populate fields with known values
+                        data=[
+                            v.dict()
+                            for v in datadoc_metadata.variables_metadata.values()
+                        ],
+                        # Define columns based on the information in DISPLAY_VARIABLES
+                        columns=[
+                            {
+                                "name": variable.display_name,
+                                "id": variable.identifier,
+                                "editable": variable.editable,
+                                "presentation": variable.presentation,
+                                "hideable": variable.editable,
+                            }
+                            for variable in DISPLAY_VARIABLES.values()
+                        ],
+                        # Non-obligatory variables are hidden by default
+                        hidden_columns=[
+                            v.identifier
+                            for v in DISPLAY_VARIABLES.values()
+                            if v.obligatory is False
+                        ],
+                        # Include tooltips for all columns
+                        tooltip_header={
+                            v.identifier: v.description
+                            for v in DISPLAY_VARIABLES.values()
+                        },
+                        editable=True,
+                        # Enable sorting and pagination
+                        sort_action="native",
+                        page_action="native",
+                        # Populate the options for all dropdown values
+                        dropdown={
+                            v.identifier: v.options
+                            for v in DISPLAY_VARIABLES.values()
+                            if v.options
+                        },
+                    )
+                ),
+            ],
+        ),
+    )
+
+    header = dbc.CardBody(
+        dbc.Row(
+            children=[
+                html.Link(rel="stylesheet", href="/assets/bundle.css"),
+                html.H1("DataDoc", className="ssb-title", style={"color": "white"}),
+            ],
+        ),
+        style={"backgroundColor": COLORS["green_4"]},
+    )
+
+    controls_bar = dbc.CardBody(
+        style={"padding": "4px"},
+        children=[
+            dbc.Row(
+                [
+                    dbc.Col(
+                        dbc.Button(
+                            [
+                                html.I(
+                                    className="bi bi-save",
+                                    style={"padding-right": "10px"},
+                                ),
+                                "   Lagre",
+                            ],
+                            class_name="ssb-btn primary-btn",
+                            id="save-button",
+                        ),
+                    ),
+                    dbc.Col(
+                        dcc.Dropdown(
+                            placeholder="Velg språk",
+                            className="ssb-dropdown",
+                            options=["Norsk Bokmål", "Nynorsk", "English"],
+                            value="Norsk Bokmål",
+                            disabled=True,
+                        ),
+                        align="end",
+                        width="auto",
+                    ),
+                ]
+            )
+        ],
+    )
+
+    validation_error = dbc.Alert(
+        id="validation-error",
+        is_open=False,
+        dismissable=True,
+        fade=True,
+        class_name="ssb-dialog warning",
+        children=[
+            dbc.Row(
+                [
+                    dbc.Col(
+                        width=1,
+                        children=[
+                            html.Div(
+                                className="ssb-dialog warning icon-panel",
+                                children=[
+                                    html.I(
+                                        className="bi bi-exclamation-triangle",
+                                    ),
+                                ],
+                            )
+                        ],
+                    ),
+                    dbc.Col(
+                        [
+                            html.H5(
+                                "Failed validation",
+                            ),
+                            dcc.Markdown(
+                                id="validation-explanation",
+                            ),
+                        ]
+                    ),
+                ]
+            )
+        ],
+        color="danger",
+    )
+
+    success_toast = dbc.Alert(
+        id="success-message",
+        is_open=False,
+        dismissable=True,
+        fade=True,
+        class_name="ssb-dialog",
+        children=[
+            dbc.Row(
+                [
+                    dbc.Col(
+                        width=3,
+                        children=[
+                            html.Div(
+                                className="ssb-dialog icon-panel",
+                                children=[
+                                    html.I(
+                                        className="bi bi-check-circle",
+                                    ),
+                                ],
+                            )
+                        ],
+                    ),
+                    dbc.Col(
+                        [
+                            html.H5(
+                                "Successfuly saved metadata",
+                            ),
+                        ]
+                    ),
+                ],
+                align="center",
+            )
+        ],
+        color="success",
+    )
+
+    app.layout = dbc.Container(
+        style={"padding": "4px"},
+        children=[
+            header,
+            controls_bar,
+            dbc.CardBody(
+                style={"padding": "4px"},
+                children=[
+                    dbc.Tabs(
+                        id="tabs",
+                        class_name="ssb-tabs",
+                        children=[
+                            dataset_details,
+                            variables_table,
+                        ],
+                    ),
+                ],
+            ),
+            validation_error,
+            success_toast,
+        ],
+    )
+
+    @app.callback(
+        Output("success-message", "is_open"),
+        Input("save-button", "n_clicks"),
+        State("variables-table", "data"),
+        prevent_initial_call=True,
+    )
+    def save_metadata_file(n_clicks, data):
+        if n_clicks and n_clicks > 0:
+            print(data)
+            datadoc_metadata.write_metadata_document()
+            return True
+        else:
+            return False
+
+    @app.callback(
+        Output({"type": DATASET_METADATA_INPUT, "id": MATCH}, "value"),
+        Input({"type": DATASET_METADATA_INPUT, "id": MATCH}, "value"),
+        prevent_initial_call=True,
+    )
+    def accept_dataset_metadata_input(value):
+
+        # Get the ID of the input that changed. This MUST match the attribute name defined in DataDocDataSet
+        metadata_identifier = ctx.triggered_id["id"]
+
+        # Update the value in the model
+        setattr(
+            datadoc_metadata.dataset_metadata,
+            metadata_identifier,
+            value,
+        )
+        print(f"Updated value for {metadata_identifier}: {value}")
+        return value
+
+    @app.callback(
+        Output("variables-table", "data"),
+        Output("validation-error", "is_open"),
+        Output("validation-explanation", "children"),
+        Input("variables-table", "data"),
+        Input("variables-table", "data_previous"),
+        prevent_initial_call=True,
+    )
+    def accept_variable_metadata_input(data, data_previous):
+        updated_row_id = None
+        updated_column_id = None
+        new_value = None
+        show_error = False
+        error_explanation = ""
+        output_data = []
+        # What has changed?
+        for i in range(len(data)):
+            update_diff = list(data[i].items() - data_previous[i].items())
+            if update_diff:
+                updated_row_id = data[i][VariableIdentifiers.SHORT_NAME.value]
+                updated_column_id = update_diff[-1][0]
+                new_value = update_diff[-1][-1]
+                print(
+                    f"Row: {updated_row_id} Column: {updated_column_id} New value: {new_value}"
+                )
+
+        try:
+            # Write the value to the variables structure
+            setattr(
+                datadoc_metadata.variables_metadata[updated_row_id],
+                updated_column_id,
+                new_value,
+            )
+        except ValidationError as e:
+            show_error = True
+            error_explanation = f"`{e}`"
+            output_data = data_previous
+            print(error_explanation)
+        else:
+            output_data = data
+            print(f"Successfully updated {updated_row_id} with {new_value}")
+
+        return output_data, show_error, error_explanation
+
+    return app
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--dataset-path", help="Specify the path to a dataset")
+    args = parser.parse_args()
+
+    # Use example dataset if nothing specified
+    dataset = args.dataset_path or "./klargjorte_data/person_data_v1.parquet"
+
+    if running_in_notebook():
+        from jupyter_dash import JupyterDash
+
+        app = main(JupyterDash, dataset)
+        app.run_server(mode="inline")
+    else:
+        # Assume running in server mode is better (largely for development purposes)
+        print("Starting in development mode")
+        app = main(Dash, dataset)
+        app.run_server(debug=True)
diff --git a/datadoc/DataDocMetadata.py b/datadoc/DataDocMetadata.py
index bc1e4e75..526fd71a 100644
--- a/datadoc/DataDocMetadata.py
+++ b/datadoc/DataDocMetadata.py
@@ -1,9 +1,19 @@
+from copy import deepcopy
 import json
 import pathlib
 import datetime
 import os
+from typing import Dict, List, Optional
+from datadoc.DisplayVariables import VariableIdentifiers
 
-from .DatasetSchema import DatasetSchema
+from datadoc.Model import (
+    AdministrativeStatus,
+    DataDocDataSet,
+    DataDocVariable,
+    DataSetState,
+)
+
+from datadoc.DatasetReader import DatasetReader
 
 
 class DataDocMetadata:
@@ -27,25 +37,30 @@ def __init__(self, dataset):
             )
             self.current_user = "default_user@ssb.no"
         self.current_datetime = str(datetime.datetime.now())
-        self.meta = {}
+
+        self.dataset_metadata: DataDocDataSet
+        self.variables_metadata: Dict[str, DataDocVariable] = {}
+
         self.read_metadata_document()
 
-    def get_dataset_state(self, dataset_path: pathlib.Path = None):
+    def get_dataset_state(
+        self, dataset_path: pathlib.Path = None
+    ) -> Optional[DataSetState]:
         """Use the path to attempt to guess the state of the dataset"""
 
         if dataset_path is None:
             dataset_path = self.dataset_full_path
-        dataset_path = list(dataset_path.parts)
-        if "kildedata" in dataset_path:
-            return "SOURCE_DATA"
-        elif "inndata" in dataset_path:
-            return "INPUT_DATA"
-        elif "klargjorte_data" in dataset_path:
-            return "PROCESSED_DATA"
+        dataset_path_parts = list(dataset_path.parts)
+        if "kildedata" in dataset_path_parts:
+            return DataSetState.SOURCE_DATA
+        elif "inndata" in dataset_path_parts:
+            return DataSetState.INPUT_DATA
+        elif "klargjorte_data" in dataset_path_parts:
+            return DataSetState.PROCESSED_DATA
         else:
             return None
 
-    def get_dataset_version(self):
+    def get_dataset_version(self) -> Optional[str]:
         """Find version information if exists in filename,
         eg. 'v1' in filename 'person_data_v1.parquet'"""
         splitted_file_name = str(self.dataset_stem).split("_")
@@ -60,64 +75,54 @@ def get_dataset_version(self):
         return None
 
     def read_metadata_document(self):
+        fresh_metadata = {}
         if self.metadata_document_full_path.exists():
             with open(self.metadata_document_full_path, "r", encoding="utf-8") as JSON:
-                self.meta = json.load(JSON)
+                fresh_metadata = json.load(JSON)
             print(
                 "Opened existing metadata file " + str(self.metadata_document_full_path)
             )
+
+            variables_list = fresh_metadata.pop("variables", None)
+
+            self.variables_metadata = {
+                v[VariableIdentifiers.SHORT_NAME.value]: DataDocVariable(**v)
+                for v in variables_list
+            }
+            self.dataset_metadata = DataDocDataSet(**fresh_metadata)
         else:
             self.generate_new_metadata_document()
 
     def generate_new_metadata_document(self):
-        self.ds_schema = DatasetSchema(self.dataset)
-        self.ds_fields = self.ds_schema.get_fields()
-
-        # Dataset elements
-        self.meta["dataSourcePath"] = str(self.dataset_full_path)
-        self.meta["shortName"] = str(self.dataset_stem)
-        self.meta["name"] = []
-        self.meta["dataSetState"] = self.dataset_state
-        self.meta["description"] = []
-        self.meta["temporalityType"] = None
-        self.meta["spatialCoverageDescription"] = [
-            {"languageCode": "no", "value": "Norge"}
-        ]
-        self.meta["populationDescription"] = []
-        self.meta["id"] = None
-        self.meta["createdDate"] = self.current_datetime
-        self.meta["createdBy"] = self.current_user
-        self.meta["dataOwner"] = None
-        self.meta["lastUpdatedDate"] = None
-        self.meta["lastUpdatedBy"] = None
-        self.meta["version"] = self.dataset_version
-        self.meta["administrativeStatus"] = "DRAFT"
-
-        # Elements for instance variables (dataset fields)
-        self.meta["variables"] = []
-        for field in self.ds_fields:
-            variable = {}
-            variable["shortName"] = field["shortName"]
-            try:
-                variable["name"] = field["name"]
-            except KeyError:
-                variable["name"] = None
-            variable["dataType"] = field["dataType"].name
-            variable["variableRole"] = None
-            # Eksempel VarDok XML, Sivilstand:
-            # https://www.ssb.no/a/xml/metadata/conceptvariable/vardok/91/nb
-            variable["definitionUri"] = None
-            variable["populationDescription"] = None  # TODO: Støtte flere språk!
-            variable["comment"] = None  # TODO: Støtte flere språk!
-            variable["temporalityType"] = None
-            variable["mesurementType"] = None
-            variable["measurementUnit"] = None
-            variable["format"] = None
-            variable["sentinelAndMissingValueUri"] = []
-            variable["unitType"] = None
-            variable["foreignKeyType"] = None
-            self.meta["variables"].append(variable)
-
-    def write_metadata_document(self):
-        json_str = json.dumps(self.meta, indent=4, sort_keys=False)
+        self.ds_schema = DatasetReader.for_file(self.dataset)
+
+        self.dataset_metadata = DataDocDataSet(
+            short_name=self.dataset_stem,
+            assessment=None,
+            dataset_state=self.dataset_state,
+            name=None,
+            data_source=None,
+            population_description=None,
+            administrative_status=AdministrativeStatus.DRAFT,
+            version=self.dataset_version,
+            unit_type=None,
+            temporality_type=None,
+            description=None,
+            spatial_coverage_description=[{"languageCode": "no", "value": "Norge"}],
+            id=None,
+            owner=None,
+            data_source_path=str(self.dataset_full_path),
+            created_date=self.current_datetime,
+            created_by=self.current_user,
+            last_updated_date=None,
+            last_updated_by=None,
+        )
+
+        self.variables_metadata = {v.short_name: v for v in self.ds_schema.get_fields()}
+
+    def write_metadata_document(self) -> None:
+        """Write all currently known metadata to file"""
+        export_dict = self.dataset_metadata.dict()
+        export_dict["variables"] = [v.dict() for v in self.variables_metadata.values()]
+        json_str = json.dumps(export_dict, indent=4, sort_keys=False, default=str)
         self.metadata_document_full_path.write_text(json_str, encoding="utf-8")
diff --git a/datadoc/DatasetReader.py b/datadoc/DatasetReader.py
new file mode 100644
index 00000000..a8ca906c
--- /dev/null
+++ b/datadoc/DatasetReader.py
@@ -0,0 +1,144 @@
+import pathlib
+from abc import ABC, abstractmethod
+import pyarrow.parquet as pq
+from typing import List, Optional, TypeVar
+import pandas as pd
+
+from datadoc.Model import DataDocVariable, Datatype
+
+TDatasetReader = TypeVar("TDatasetReader", bound="DatasetReader")
+
+KNOWN_INTEGER_TYPES = (
+    "int",
+    "int_",
+    "int8",
+    "int16",
+    "int32",
+    "int64",
+    "integer",
+    "long",
+    "uint",
+    "uint8",
+    "uint16",
+    "uint32",
+    "uint64",
+)
+
+KNOWN_FLOAT_TYPES = (
+    "double",
+    "float",
+    "float_",
+    "float16",
+    "float32",
+    "float64",
+    "decimal",
+    "number",
+    "numeric",
+    "num",
+)
+
+KNOWN_STRING_TYPES = (
+    "string",
+    "str",
+    "char",
+    "varchar",
+    "varchar2",
+    "text",
+    "txt",
+    "bytes",
+)
+
+KNOWN_DATETIME_TYPES = (
+    "timestamp",
+    "timestamp[us]",
+    "timestamp[ns]",
+    "datetime64",
+    " datetime64[ns]",
+    " datetime64[us]",
+    "date",
+    "datetime",
+    "time",
+)
+
+KNOWN_BOOLEAN_TYPES = ("bool", "bool_", "boolean")
+
+
+class DatasetReader(ABC):
+    def __init__(self, dataset):
+        self.dataset = dataset
+
+    @staticmethod
+    def for_file(dataset: str) -> TDatasetReader:
+        """Factory method to return the correct subclass based on the given dataset file"""
+        file_type = str(pathlib.Path(dataset)).lower().split(".")[1]
+        if file_type == "parquet":
+            return DatasetReaderParquet(dataset)
+        if file_type == "sas7bdat":
+            return DatasetReaderSas7bdat(dataset)
+        else:
+            # In the future we can potentially support csv, xml or json files
+            raise NotImplementedError
+
+    @staticmethod
+    def transform_data_type(data_type: str) -> Optional[Datatype]:
+        v_data_type = data_type.lower()
+        if v_data_type in KNOWN_INTEGER_TYPES:
+            return Datatype.INTEGER
+        elif v_data_type in KNOWN_FLOAT_TYPES:
+            return Datatype.FLOAT
+        elif v_data_type in KNOWN_STRING_TYPES:
+            return Datatype.STRING
+        elif v_data_type in KNOWN_DATETIME_TYPES:
+            return Datatype.DATETIME
+        elif v_data_type in KNOWN_BOOLEAN_TYPES:
+            return Datatype.BOOLEAN
+        else:
+            return None  # Unknown datatype?
+
+    @abstractmethod
+    def get_fields(self) -> List[DataDocVariable]:
+        """Abstract method, must be implemented by subclasses"""
+        pass
+
+
+class DatasetReaderParquet(DatasetReader):
+    def __init__(self, dataset):
+        super().__init__(dataset)
+
+    def get_fields(self) -> List[DataDocVariable]:
+        fields = []
+        data_table = pq.read_table(self.dataset)
+        for data_field in data_table.schema:
+            fields.append(
+                DataDocVariable(
+                    short_name=data_field.name,
+                    datatype=self.transform_data_type(str(data_field.type)),
+                )
+            )
+        return fields
+
+
+class DatasetReaderSas7bdat(DatasetReader):
+    def __init__(self, dataset):
+        super().__init__(dataset)
+
+    def get_fields(self) -> List[DataDocVariable]:
+        fields = []
+        # Use an iterator to avoid reading in the entire dataset
+        sas_reader = pd.read_sas(self.dataset, iterator=True)
+
+        # Get the first row from the iterator
+        row = next(sas_reader)
+
+        # Get all the values from the row and loop through them
+        for i, v in enumerate(row.values.tolist()[0]):
+            fields.append(
+                DataDocVariable(
+                    short_name=sas_reader.columns[i].name,
+                    name=sas_reader.columns[i].label,
+                    # Access the python type for the value and transform it to a DataDoc Data type
+                    datatype=self.transform_data_type(type(v).__name__.lower()),
+                )
+            )
+
+        return fields
diff --git a/datadoc/DatasetSchema.py b/datadoc/DatasetSchema.py
deleted file mode 100644
index 82247029..00000000
--- a/datadoc/DatasetSchema.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import pathlib
-import pyarrow.parquet as pq
-from typing import Optional
-import pandas as pd
-
-from datadoc.Model import Datatype
-
-KNOWN_INTEGER_TYPES = (
-    "int",
-    "int_",
-    "int8",
-    "int16",
-    "int32",
-    "int64",
-    "integer",
-    "long",
-    "uint",
-    "uint8",
-    "uint16",
-    "uint32",
-    "uint64",
-)
-
-KNOWN_FLOAT_TYPES = (
-    "double",
-    "float",
-    "float_",
-    "float16",
-    "float32",
-    "float64",
-    "decimal",
-    "number",
-    "numeric",
-    "num",
-)
-
-KNOWN_STRING_TYPES = (
-    "string",
-    "str",
-    "char",
-    "varchar",
-    "varchar2",
-    "text",
-    "txt",
-    "bytes",
-)
-
-KNOWN_DATETIME_TYPES = (
-    "timestamp",
-    "timestamp[us]",
-    "timestamp[ns]",
-    "datetime64",
-    " datetime64[ns]",
-    " datetime64[us]",
-    "date",
-    "datetime",
-    "time",
-)
-
-KNOWN_BOOLEAN_TYPES = ("bool", "bool_", "boolean")
-
-
-class DatasetSchema:
-    def __init__(self, dataset):
-        self.dataset = dataset
-        self.dataset_full_path = pathlib.Path(self.dataset)
-        self.dataset_file_type = str(self.dataset_full_path.name).lower().split(".")[1]
-
-    def get_fields(self):
-        fields = []
-        if self.dataset_file_type == "parquet":
-            data_table = pq.read_table(self.dataset)
-            for data_field in data_table.schema:
-                field = {}
-                field["shortName"] = str(data_field.name)
-                field["dataType"] = self.transform_data_type(str(data_field.type))
-                fields.append(field)
-
-        # SAS Data files
-        elif self.dataset_file_type == "sas7bdat":
-            # Use an iterator to avoid reading in the entire dataset
-            sas_reader = pd.read_sas(self.dataset, iterator=True)
-
-            # Get the first row from the iterator
-            row = next(sas_reader)
-
-            # Get all the values from the row and loop through them
-            for i, v in enumerate(row.values.tolist()[0]):
-                field = {}
-                field["shortName"] = sas_reader.columns[i].name
-                field["name"] = sas_reader.columns[i].label
-                # Access the python type for the value and transform it to a DataDoc Data type
-                field["dataType"] = self.transform_data_type(type(v).__name__.lower())
-                fields.append(field)
-
-        elif self.dataset_file_type == "csv":
-            raise NotImplementedError
-        elif self.dataset_file_type == "json":
-            raise NotImplementedError
-        elif self.dataset_file_type == "xml":
-            raise NotImplementedError
-        return fields
-
-    @staticmethod
-    def transform_data_type(data_type: str) -> Optional[Datatype]:
-        v_data_type = data_type.lower()
-        if v_data_type in KNOWN_INTEGER_TYPES:
-            return Datatype.INTEGER
-        elif v_data_type in KNOWN_FLOAT_TYPES:
-            return Datatype.FLOAT
-        elif v_data_type in KNOWN_STRING_TYPES:
-            return Datatype.STRING
-        elif v_data_type in KNOWN_DATETIME_TYPES:
-            return Datatype.DATETIME
-        elif v_data_type in KNOWN_BOOLEAN_TYPES:
-            return Datatype.BOOLEAN
-        else:
-            return None  # Unknown datatype?
diff --git a/datadoc/DisplayVariables.py b/datadoc/DisplayVariables.py
index c7ae1937..7d86bb59 100644
--- a/datadoc/DisplayVariables.py
+++ b/datadoc/DisplayVariables.py
@@ -1,32 +1,31 @@
-from collections import OrderedDict
 from dataclasses import dataclass, field
 from enum import Enum
-from typing import Any, List, Optional
+from typing import Dict, List, Optional
 
-from .Model import Datatype, VariableRole
+from datadoc.Model import Datatype, VariableRole
 
 
 class VariableIdentifiers(str, Enum):
     """As defined here: https://statistics-norway.atlassian.net/wiki/spaces/MPD/pages/3042869256/Variabelforekomst"""
 
-    SHORT_NAME = "shortName"
+    SHORT_NAME = "short_name"
     NAME = "name"
-    DATA_TYPE = "dataType"
-    VARIABLE_ROLE = "variableRole"
-    DEFINITION_URI = "definitionUri"
-    DIRECT_PERSON_IDENTIFYING = "directPersonIdentifying"
-    DATA_SOURCE = "dataSource"
-    POPULATION_DESCRIPTION = "populationDescription"
+    DATA_TYPE = "datatype"
+    VARIABLE_ROLE = "variable_role"
+    DEFINITION_URI = "definition_uri"
+    DIRECT_PERSON_IDENTIFYING = "direct_person_identifying"
+    DATA_SOURCE = "data_source"
+    POPULATION_DESCRIPTION = "population_description"
     COMMENT = "comment"
-    TEMPORALITY_TYPE = "temporalityType"
-    MEASUREMENT_UNIT = "measurementUnit"
+    TEMPORALITY_TYPE = "temporality_type"
+    MEASUREMENT_UNIT = "measurement_unit"
     FORMAT = "format"
-    CLASSIFICATION_URI = "classificationUri"
-    SENTINEL_VALUE_URI = "sentinelValueUri"
-    INVALID_VALUE_DESCRIPTION = "invalidValueDescription"
+    CLASSIFICATION_URI = "classification_uri"
+    SENTINEL_VALUE_URI = "sentinel_value_uri"
+    INVALID_VALUE_DESCRIPTION = "invalid_value_description"
     IDENTIFIER = "id"
-    CONTAINS_DATA_FROM = "containsDataFrom"
-    CONTAINS_DATA_UNTIL = "containsDataUntil"
+    CONTAINS_DATA_FROM = "contains_data_from"
+    CONTAINS_DATA_UNTIL = "contains_data_until"
 
 
 @dataclass
@@ -34,9 +33,9 @@ class DisplayVariable:
     identifier: str
     display_name: str
     description: str
+    options: Optional[Dict[str, List[Dict[str, str]]]] = None
     obligatory: bool = False
     presentation: Optional[str] = "input"
-    options: Optional[List[Any]] = field(default_factory=list)
     editable: bool = True
 
 
diff --git a/datadoc/Model.py b/datadoc/Model.py
index c4d4e33d..2f5fe5a4 100644
--- a/datadoc/Model.py
+++ b/datadoc/Model.py
@@ -1,69 +1,95 @@
 from enum import Enum, auto
 from datetime import date, datetime
-from typing import Optional
-from pydantic import BaseModel, constr
+from typing import Dict, List, Optional
+from pydantic import BaseModel, constr, conint
 
 ALPHANUMERIC_HYPHEN_UNDERSCORE = "[-A-Za-z0-9_.*/]"
 URL_FORMAT = "(https?:\/\/)?(www\.)?[a-zA-Z0-9]+([-a-zA-Z0-9.]{1,254}[A-Za-z0-9])?\.[a-zA-Z0-9()]{1,6}([\/][-a-zA-Z0-9_]+)*[\/]?"
 
 
-class Assessment(Enum):
+class Assessment(str, Enum):
     # TODO: May have some kind of relation to DataSetState (SSB decision not made yet)? E.g. if "PROCSESSED_DATA" then "PROTECTED"?
-    SENSITIVE = auto()
-    PROTECTED = auto()
-    OPEN = auto()
+    SENSITIVE = "SENSITIVE"
+    PROTECTED = "PROTECTED"
+    OPEN = "OPEN"
 
 
-class DataSetState(Enum):
-    SOURCE_DATA = auto()
-    INPUT_DATA = auto()
-    PROCESSED_DATA = auto()
-    STATISTIC = auto()
-    OUPUT_DATA = auto()
+class DataSetState(str, Enum):
+    SOURCE_DATA = "SOURCE_DATA"
+    INPUT_DATA = "INPUT_DATA"
+    PROCESSED_DATA = "PROCESSED_DATA"
+    STATISTIC = "STATISTIC"
+    OUTPUT_DATA = "OUTPUT_DATA"
 
 
-class AdministrativeStatus(Enum):
+class AdministrativeStatus(str, Enum):
     # TODO: The definition of this property is not complete (may change)?
-    DRAFT = auto()
-    INTERNAL = auto()
-    OPEN = auto()
-    DEPRECATED = auto()
+    DRAFT = "DRAFT"
+    INTERNAL = "INTERNAL"
+    OPEN = "OPEN"
+    DEPRECATED = "DEPRECATED"
 
 
-class UnitType(Enum):
+class UnitType(str, Enum):
     # TODO: May change in the nearest future? See list of SSB unit types https://www.ssb.no/metadata/definisjoner-av-statistiske-enheter
-    ARBEIDSULYKKE = auto()
-    BOLIG = auto()
-    BYGNING = auto()
-    EIENDOM = auto()
-    FAMILIE = auto()
-    FORETAK = auto()
-    FYLKE = auto()
-    HAVNEANLOEP = auto()
-    HUSHOLDNING = auto()
-    KJOERETOEY = auto()
-    KOMMUNE = auto()
-    KURS = auto()
-    LOVBRUDD = auto()
-    PERSON = auto()
-    STAT = auto()
-    STORFE = auto()
-    TRAFIKKULYKKE = auto()
-    TRANSAKSJON = auto()
-    VARE_TJENESTE = auto()
-    VERDIPAPIR = auto()
-    VIRKSOMHET = auto()
-
-
-class TemporalityType(Enum):
+    ARBEIDSULYKKE = "ARBEIDSULYKKE"
+    BOLIG = "BOLIG"
+    BYGNING = "BYGNING"
+    EIENDOM = "EIENDOM"
+    FAMILIE = "FAMILIE"
+    FORETAK = "FORETAK"
+    FYLKE = "FYLKE"
+    HAVNEANLOEP = "HAVNEANLOEP"
+    HUSHOLDNING = "HUSHOLDNING"
+    KJOERETOEY = "KJOERETOEY"
+    KOMMUNE = "KOMMUNE"
+    KURS = "KURS"
+    LOVBRUDD = "LOVBRUDD"
+    PERSON = "PERSON"
+    STAT = "STAT"
+    STORFE = "STORFE"
+    TRAFIKKULYKKE = "TRAFIKKULYKKE"
+    TRANSAKSJON = "TRANSAKSJON"
+    VARE_TJENESTE = "VARE_TJENESTE"
+    VERDIPAPIR = "VERDIPAPIR"
+    VIRKSOMHET = "VIRKSOMHET"
+
+
+class TemporalityType(str, Enum):
     # More information about temporality type: https://statistics-norway.atlassian.net/l/c/HV12q90R
-    FIXED = auto()
-    STATUS = auto()
-    ACCUMULATED = auto()
-    EVENT = auto()
+    FIXED = "FIXED"
+    STATUS = "STATUS"
+    ACCUMULATED = "ACCUMULATED"
+    EVENT = "EVENT"
 
 
-class DataDocDataSet(BaseModel):
+class Datatype(str, Enum):
+    STRING = "STRING"
+    INTEGER = "INTEGER"
+    FLOAT = "FLOAT"
+    DATETIME = "DATETIME"
+    BOOLEAN = "BOOLEAN"
+
+
+class VariableRole(str, Enum):
+    IDENTIFIER = "IDENTIFIER"
+    MEASURE = "MEASURE"
+    START_TIME = "START_TIME"
+    STOP_TIME = "STOP_TIME"
+    ATTRIBUTE = "ATTRIBUTE"
+
+
+class DataDocBaseModel(BaseModel):
+    """Defines configuration which applies to all Models in this application"""
+
+    class Config:
+        # Runs validation when a field value is assigned, not just in the constructor
+        validate_assignment = True
+        # Write only the values of enums during serialization
+        use_enum_values = True
+
+
+class DataDocDataSet(DataDocBaseModel):
     """DataDoc data set. See documentation https://statistics-norway.atlassian.net/l/c/NgjE7yj1"""
 
     short_name: Optional[
@@ -79,7 +105,7 @@ class DataDocDataSet(BaseModel):
     unit_type: Optional[UnitType]
     temporality_type: Optional[TemporalityType]
     description: Optional[str]
-    spatial_coverage_description: Optional[str]
+    spatial_coverage_description: Optional[List[Dict[str, str]]]
     id: Optional[constr(regex=URL_FORMAT)]
     owner: Optional[str]
     data_source_path: Optional[str]
@@ -89,23 +115,7 @@ class DataDocDataSet(BaseModel):
     last_updated_by: Optional[str]
 
 
-class Datatype(Enum):
-    STRING = auto()
-    INTEGER = auto()
-    FLOAT = auto()
-    DATETIME = auto()
-    BOOLEAN = auto()
-
-
-class VariableRole(Enum):
-    IDENTIFIER = auto()
-    MEASURE = auto()
-    START_TIME = auto()
-    STOP_TIME = auto()
-    ATTRIBUTE = auto()
-
-
-class DataDocVariable(BaseModel):
+class DataDocVariable(DataDocBaseModel):
     """DataDoc instance variable. See documentation https://statistics-norway.atlassian.net/l/c/goyNhUPP"""
 
     short_name: Optional[
@@ -129,3 +139,13 @@ class DataDocVariable(BaseModel):
     id: Optional[constr(regex=URL_FORMAT)]
     contains_data_from: Optional[date]
     contains_data_until: Optional[date]
+
+
+class MetadataDocument(DataDocBaseModel):
+    """Represents the data structure on file. Includes the dataset metadata from the user as
+    well as meta-metadata like the percentage of completed metadata fields and the document version"""
+
+    percentage_complete: conint(ge=0, le=100)
+    document_version: str
+    dataset: DataDocDataSet
+    variables: List[DataDocVariable]
diff --git a/datadoc/utils.py b/datadoc/utils.py
new file mode 100644
index 00000000..6aa7b976
--- /dev/null
+++ b/datadoc/utils.py
@@ -0,0 +1,10 @@
+def running_in_notebook() -> bool:
+    """Return True if running in Jupyter Notebook"""
+    try:
+        return get_ipython().__class__.__name__ == "ZMQInteractiveShell"
+    except NameError:
+        # The get_ipython method is globally available in ipython interpreters
+        # as used in Jupyter. However it is not available in other python
+        # interpreters and will throw a NameError. Therefore we're not running
+        # in Jupyter.
+        return False
diff --git a/doc/.DS_Store b/doc/.DS_Store
new file mode 100644
index 00000000..5008ddfc
Binary files /dev/null and b/doc/.DS_Store differ
diff --git a/doc/DataDoc_prototype_001.png b/doc/DataDoc_prototype_001.png
deleted file mode 100644
index c39f543a..00000000
Binary files a/doc/DataDoc_prototype_001.png and /dev/null differ
diff --git a/doc/datadoc.png b/doc/datadoc.png
new file mode 100644
index 00000000..4bcb4230
Binary files /dev/null and b/doc/datadoc.png differ
diff --git a/poetry.lock b/poetry.lock
index 35ce4f63..f536a40b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -10,23 +10,6 @@ python-versions = ">=3.6"
 docs = ["sphinx", "setuptools-scm", "sphinx-rtd-theme"]
 test = ["pytest", "pytest-cov"]
 
-[[package]]
-name = "anyio"
-version = "3.6.1"
-description = "High level compatibility layer for multiple asynchronous event loop implementations"
-category = "main"
-optional = false
-python-versions = ">=3.6.2"
-
-[package.dependencies]
-idna = ">=2.8"
-sniffio = ">=1.1"
-
-[package.extras]
-doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
-test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
-trio = ["trio (>=0.16)"]
-
 [[package]]
 name = "appnope"
 version = "0.1.3"
@@ -39,7 +22,7 @@ python-versions = "*"
 name = "argon2-cffi"
 version = "21.3.0"
 description = "The secure Argon2 password hashing algorithm."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -55,7 +38,7 @@ tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
 name = "argon2-cffi-bindings"
 version = "21.2.0"
 description = "Low-level CFFI bindings for Argon2"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -84,7 +67,7 @@ test = ["astroid", "pytest"]
 name = "atomicwrites"
 version = "1.4.0"
 description = "Atomic file writes."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
@@ -92,7 +75,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 name = "attrs"
 version = "21.4.0"
 description = "Classes Without Boilerplate"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 
@@ -114,7 +97,7 @@ python-versions = "*"
 name = "beautifulsoup4"
 version = "4.11.1"
 description = "Screen-scraping library"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6.0"
 
@@ -127,7 +110,7 @@ lxml = ["lxml"]
 
 [[package]]
 name = "black"
-version = "22.3.0"
+version = "22.6.0"
 description = "The uncompromising code formatter."
 category = "dev"
 optional = false
@@ -138,7 +121,7 @@ click = ">=8.0.0"
 mypy-extensions = ">=0.4.3"
 pathspec = ">=0.9.0"
 platformdirs = ">=2"
-tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
 typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
 
 [package.extras]
@@ -149,9 +132,9 @@ uvloop = ["uvloop (>=0.15.2)"]
 
 [[package]]
 name = "bleach"
-version = "5.0.0"
+version = "5.0.1"
 description = "An easy safelist-based HTML-sanitizing tool."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -160,8 +143,8 @@ six = ">=1.9.0"
 webencodings = "*"
 
 [package.extras]
-css = ["tinycss2 (>=1.1.0)"]
-dev = ["pip-tools (==6.5.1)", "pytest (==7.1.1)", "flake8 (==4.0.1)", "tox (==3.24.5)", "sphinx (==4.3.2)", "twine (==4.0.0)", "wheel (==0.37.1)", "hashin (==0.17.0)", "black (==22.3.0)", "mypy (==0.942)"]
+css = ["tinycss2 (>=1.1.0,<1.2)"]
+dev = ["build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "Sphinx (==4.3.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)", "black (==22.3.0)", "mypy (==0.961)"]
 
 [[package]]
 name = "brotli"
@@ -173,15 +156,15 @@ python-versions = "*"
 
 [[package]]
 name = "certifi"
-version = "2021.10.8"
+version = "2022.6.15"
 description = "Python package for providing Mozilla's CA Bundle."
 category = "main"
 optional = false
-python-versions = "*"
+python-versions = ">=3.6"
 
 [[package]]
 name = "cffi"
-version = "1.15.0"
+version = "1.15.1"
 description = "Foreign Function Interface for Python calling C code."
 category = "main"
 optional = false
@@ -192,11 +175,11 @@ pycparser = "*"
 
 [[package]]
 name = "charset-normalizer"
-version = "2.0.12"
+version = "2.1.0"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 category = "main"
 optional = false
-python-versions = ">=3.5.0"
+python-versions = ">=3.6.0"
 
 [package.extras]
 unicode_backport = ["unicodedata2"]
@@ -214,7 +197,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
 
 [[package]]
 name = "colorama"
-version = "0.4.4"
+version = "0.4.5"
 description = "Cross-platform colored terminal text."
 category = "main"
 optional = false
@@ -224,7 +207,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 name = "coverage"
 version = "6.4.1"
 description = "Code coverage measurement for Python"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -236,7 +219,7 @@ toml = ["tomli"]
 
 [[package]]
 name = "dash"
-version = "2.4.1"
+version = "2.5.1"
 description = "A Python framework for building reactive web-apps. Developed by Plotly."
 category = "main"
 optional = false
@@ -259,7 +242,7 @@ testing = ["beautifulsoup4 (>=4.8.2)", "lxml (>=4.6.2)", "percy (>=2.0.2)", "pyt
 
 [[package]]
 name = "dash-bootstrap-components"
-version = "1.1.0"
+version = "1.2.0"
 description = "Bootstrap themed components for use in Plotly Dash"
 category = "main"
 optional = false
@@ -315,7 +298,7 @@ python-versions = ">=3.5"
 name = "defusedxml"
 version = "0.7.1"
 description = "XML bomb protection for Python stdlib modules"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 
@@ -339,7 +322,7 @@ python-versions = "*"
 name = "fastjsonschema"
 version = "2.15.3"
 description = "Fastest Python implementation of JSON schema"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -400,7 +383,7 @@ python-versions = ">=3.5"
 
 [[package]]
 name = "importlib-metadata"
-version = "4.11.4"
+version = "4.12.0"
 description = "Read metadata from Python packages"
 category = "main"
 optional = false
@@ -412,13 +395,13 @@ zipp = ">=0.5"
 [package.extras]
 docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
 perf = ["ipython"]
-testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
 
 [[package]]
 name = "importlib-resources"
-version = "5.7.1"
+version = "5.8.0"
 description = "Read resources from Python packages"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -433,13 +416,13 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
 name = "iniconfig"
 version = "1.1.1"
 description = "iniconfig: brain-dead simple config-ini parsing"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
 [[package]]
 name = "ipykernel"
-version = "6.13.0"
+version = "6.15.0"
 description = "IPython Kernel for Jupyter"
 category = "main"
 optional = false
@@ -454,27 +437,16 @@ matplotlib-inline = ">=0.1"
 nest-asyncio = "*"
 packaging = "*"
 psutil = "*"
+pyzmq = ">=17"
 tornado = ">=6.1"
 traitlets = ">=5.1.0"
 
 [package.extras]
-test = ["pytest (>=6.0)", "pytest-cov", "flaky", "ipyparallel", "pre-commit", "pytest-timeout"]
-
-[[package]]
-name = "ipysheet"
-version = "0.5.0"
-description = "Spreadsheet in the Jupyter notebook."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-ipywidgets = ">=7.5.0,<8.0"
-jupyter-server = ">=1.6,<2"
+test = ["flaky", "ipyparallel", "pre-commit", "pytest-cov", "pytest-timeout", "pytest (>=6.0)"]
 
 [[package]]
 name = "ipython"
-version = "8.3.0"
+version = "8.4.0"
 description = "IPython: Productive Interactive Computing"
 category = "main"
 optional = false
@@ -511,15 +483,15 @@ test_extra = ["pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotli
 name = "ipython-genutils"
 version = "0.2.0"
 description = "Vestigial utilities from IPython"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
 [[package]]
 name = "ipywidgets"
-version = "7.7.0"
+version = "7.7.1"
 description = "IPython HTML widgets for Jupyter"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -528,7 +500,6 @@ ipykernel = ">=4.5.1"
 ipython = {version = ">=4.0.0", markers = "python_version >= \"3.3\""}
 ipython-genutils = ">=0.2.0,<0.3.0"
 jupyterlab-widgets = {version = ">=1.0.0", markers = "python_version >= \"3.6\""}
-nbformat = ">=4.2.0"
 traitlets = ">=4.3.1"
 widgetsnbextension = ">=3.6.0,<3.7.0"
 
@@ -574,9 +545,9 @@ i18n = ["Babel (>=2.7)"]
 
 [[package]]
 name = "jsonschema"
-version = "4.5.1"
+version = "4.6.1"
 description = "An implementation of JSON Schema validation for Python"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -587,7 +558,7 @@ pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
 
 [package.extras]
 format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
-format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
+format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
 
 [[package]]
 name = "jupyter"
@@ -607,7 +578,7 @@ qtconsole = "*"
 
 [[package]]
 name = "jupyter-client"
-version = "7.3.1"
+version = "7.3.4"
 description = "Jupyter protocol implementation and client libraries"
 category = "main"
 optional = false
@@ -618,21 +589,21 @@ entrypoints = "*"
 jupyter-core = ">=4.9.2"
 nest-asyncio = ">=1.5.4"
 python-dateutil = ">=2.8.2"
-pyzmq = ">=22.3"
+pyzmq = ">=23.0"
 tornado = ">=6.0"
 traitlets = "*"
 
 [package.extras]
-doc = ["ipykernel", "myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
+doc = ["ipykernel", "myst-parser", "sphinx-rtd-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt"]
 test = ["codecov", "coverage", "ipykernel (>=6.5)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"]
 
 [[package]]
 name = "jupyter-console"
-version = "6.4.3"
+version = "6.4.4"
 description = "Jupyter terminal console"
 category = "dev"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [package.dependencies]
 ipykernel = "*"
@@ -680,48 +651,19 @@ retrying = "*"
 [package.extras]
 dev = ["jupyterlab (>=2.0)", "notebook (>=6.0)", "jupyter-server-proxy"]
 
-[[package]]
-name = "jupyter-server"
-version = "1.17.0"
-description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-anyio = ">=3.1.0,<4"
-argon2-cffi = "*"
-jinja2 = "*"
-jupyter-client = ">=6.1.12"
-jupyter-core = ">=4.7.0"
-nbconvert = ">=6.4.4"
-nbformat = ">=5.2.0"
-packaging = "*"
-prometheus-client = "*"
-pywinpty = {version = "*", markers = "os_name == \"nt\""}
-pyzmq = ">=17"
-Send2Trash = "*"
-terminado = ">=0.8.3"
-tornado = ">=6.1.0"
-traitlets = ">=5.1"
-websocket-client = "*"
-
-[package.extras]
-test = ["coverage", "ipykernel", "pre-commit", "pytest-console-scripts", "pytest-cov", "pytest-mock", "pytest-timeout", "pytest-tornasync", "pytest (>=6.0)", "requests"]
-
 [[package]]
 name = "jupyterlab-pygments"
 version = "0.2.2"
 description = "Pygments theme using JupyterLab CSS variables"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
 [[package]]
 name = "jupyterlab-widgets"
-version = "1.1.0"
+version = "1.1.1"
 description = "A JupyterLab extension."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -756,7 +698,7 @@ python-versions = "*"
 name = "mistune"
 version = "0.8.4"
 description = "The fastest markdown parser in pure Python"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -788,9 +730,9 @@ python-versions = "*"
 
 [[package]]
 name = "nbclient"
-version = "0.6.3"
+version = "0.6.6"
 description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7.0"
 
@@ -798,7 +740,7 @@ python-versions = ">=3.7.0"
 jupyter-client = ">=6.1.5"
 nbformat = ">=5.0"
 nest-asyncio = "*"
-traitlets = ">=5.0.0"
+traitlets = ">=5.2.2"
 
 [package.extras]
 sphinx = ["autodoc-traits", "mock", "moto", "myst-parser", "Sphinx (>=1.7)", "sphinx-book-theme"]
@@ -808,7 +750,7 @@ test = ["black", "check-manifest", "flake8", "ipykernel", "ipython (<8.0.0)", "i
 name = "nbconvert"
 version = "6.5.0"
 description = "Converting Jupyter Notebooks"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -841,7 +783,7 @@ webpdf = ["pyppeteer (>=1,<1.1)"]
 name = "nbformat"
 version = "5.4.0"
 description = "The Jupyter Notebook format"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -854,6 +796,17 @@ traitlets = ">=5.1"
 [package.extras]
 test = ["check-manifest", "testpath", "pytest", "pre-commit"]
 
+[[package]]
+name = "nbstripout"
+version = "0.5.0"
+description = "Strips outputs from Jupyter and IPython notebooks"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+nbformat = "*"
+
 [[package]]
 name = "nest-asyncio"
 version = "1.5.5"
@@ -866,7 +819,7 @@ python-versions = ">=3.5"
 name = "notebook"
 version = "6.4.12"
 description = "A web-based notebook environment for interactive computing"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -894,7 +847,7 @@ test = ["pytest", "coverage", "requests", "testpath", "nbval", "selenium", "pyte
 
 [[package]]
 name = "numpy"
-version = "1.22.3"
+version = "1.23.0"
 description = "NumPy is the fundamental package for array computing with Python."
 category = "main"
 optional = false
@@ -913,7 +866,7 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
 
 [[package]]
 name = "pandas"
-version = "1.4.2"
+version = "1.4.3"
 description = "Powerful data structures for data analysis, time series, and statistics"
 category = "main"
 optional = false
@@ -936,7 +889,7 @@ test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]
 name = "pandocfilters"
 version = "1.5.0"
 description = "Utilities for writing pandoc filters in python"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
@@ -993,7 +946,7 @@ test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytes
 
 [[package]]
 name = "plotly"
-version = "5.8.0"
+version = "5.9.0"
 description = "An open-source, interactive data visualization library for Python"
 category = "main"
 optional = false
@@ -1006,7 +959,7 @@ tenacity = ">=6.2.0"
 name = "pluggy"
 version = "1.0.0"
 description = "plugin and hook calling mechanisms for python"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -1018,7 +971,7 @@ testing = ["pytest", "pytest-benchmark"]
 name = "prometheus-client"
 version = "0.14.1"
 description = "Python client for the Prometheus monitoring system."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -1027,7 +980,7 @@ twisted = ["twisted"]
 
 [[package]]
 name = "prompt-toolkit"
-version = "3.0.29"
+version = "3.0.30"
 description = "Library for building powerful interactive command lines in Python"
 category = "main"
 optional = false
@@ -1038,14 +991,14 @@ wcwidth = "*"
 
 [[package]]
 name = "psutil"
-version = "5.9.0"
+version = "5.9.1"
 description = "Cross-platform lib for process and system monitoring in Python."
 category = "main"
 optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 [package.extras]
-test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
+test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"]
 
 [[package]]
 name = "ptyprocess"
@@ -1147,7 +1100,7 @@ diagrams = ["railroad-diagrams", "jinja2"]
 name = "pyrsistent"
 version = "0.18.1"
 description = "Persistent/Functional/Immutable data structures"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -1155,7 +1108,7 @@ python-versions = ">=3.7"
 name = "pytest"
 version = "7.1.2"
 description = "pytest: simple powerful testing with Python"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -1176,7 +1129,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
 name = "pytest-cov"
 version = "3.0.0"
 description = "Pytest plugin for measuring coverage."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -1218,13 +1171,13 @@ python-versions = "*"
 name = "pywinpty"
 version = "2.0.5"
 description = "Pseudo terminal support for Windows from Python."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
 [[package]]
 name = "pyzmq"
-version = "22.3.0"
+version = "23.2.0"
 description = "Python bindings for 0MQ"
 category = "main"
 optional = false
@@ -1236,7 +1189,7 @@ py = {version = "*", markers = "implementation_name == \"pypy\""}
 
 [[package]]
 name = "qtconsole"
-version = "5.3.0"
+version = "5.3.1"
 description = "Jupyter Qt console"
 category = "dev"
 optional = false
@@ -1250,7 +1203,7 @@ jupyter-core = "*"
 pygments = "*"
 pyzmq = ">=17.1"
 qtpy = ">=2.0.1"
-traitlets = "*"
+traitlets = "<5.2.1 || >5.2.1,<5.2.2 || >5.2.2"
 
 [package.extras]
 doc = ["Sphinx (>=1.3)"]
@@ -1272,21 +1225,21 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"]
 
 [[package]]
 name = "requests"
-version = "2.27.1"
+version = "2.28.1"
 description = "Python HTTP for Humans."
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+python-versions = ">=3.7, <4"
 
 [package.dependencies]
 certifi = ">=2017.4.17"
-charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
-idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+charset-normalizer = ">=2,<3"
+idna = ">=2.5,<4"
 urllib3 = ">=1.21.1,<1.27"
 
 [package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
-use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"]
 
 [[package]]
 name = "retrying"
@@ -1303,7 +1256,7 @@ six = ">=1.7.0"
 name = "send2trash"
 version = "1.8.0"
 description = "Send file to trash natively under Mac OS X, Windows and Linux."
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -1320,25 +1273,17 @@ category = "main"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
 
-[[package]]
-name = "sniffio"
-version = "1.2.0"
-description = "Sniff out which async library your code is running under"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
 [[package]]
 name = "soupsieve"
 version = "2.3.2.post1"
 description = "A modern CSS selector implementation for Beautiful Soup."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
 [[package]]
 name = "stack-data"
-version = "0.2.0"
+version = "0.3.0"
 description = "Extract data from python stack frames and tracebacks for informative displays"
 category = "main"
 optional = false
@@ -1367,7 +1312,7 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"]
 name = "terminado"
 version = "0.15.0"
 description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -1383,7 +1328,7 @@ test = ["pre-commit", "pytest-timeout", "pytest (>=6.0)"]
 name = "tinycss2"
 version = "1.1.1"
 description = "A tiny CSS parser"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -1398,7 +1343,7 @@ test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
 name = "tomli"
 version = "2.0.1"
 description = "A lil' TOML parser"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -1412,7 +1357,7 @@ python-versions = ">= 3.5"
 
 [[package]]
 name = "traitlets"
-version = "5.2.1.post0"
+version = "5.3.0"
 description = ""
 category = "main"
 optional = false
@@ -1454,23 +1399,10 @@ python-versions = "*"
 name = "webencodings"
 version = "0.5.1"
 description = "Character encoding aliases for legacy web content"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
-[[package]]
-name = "websocket-client"
-version = "1.3.2"
-description = "WebSocket client for Python with low level API options"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"]
-optional = ["python-socks", "wsaccel"]
-test = ["websockets"]
-
 [[package]]
 name = "werkzeug"
 version = "2.1.2"
@@ -1484,9 +1416,9 @@ watchdog = ["watchdog"]
 
 [[package]]
 name = "widgetsnbextension"
-version = "3.6.0"
+version = "3.6.1"
 description = "IPython HTML widgets for Jupyter"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -1508,17 +1440,13 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.8"
-content-hash = "d43f2ecc960df42d9760350973147ab47f896a88aaec9746ba183ae30711f8e7"
+content-hash = "eb01a03d1dfbb150714e558b41a25d26ab2d348525c7868dd3e5918f1e592659"
 
 [metadata.files]
 ansi2html = [
     {file = "ansi2html-1.7.0-py3-none-any.whl", hash = "sha256:50517716d2e54f4167dadcec583fc7339e3684e4ff50a5f3d516fc8b54ce5875"},
     {file = "ansi2html-1.7.0.tar.gz", hash = "sha256:69316be8c68ac91c5582d397c2890e69c993cc7cda52062ac7e45fcb660d8edc"},
 ]
-anyio = [
-    {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"},
-    {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
-]
 appnope = [
     {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
     {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
@@ -1571,33 +1499,33 @@ beautifulsoup4 = [
     {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"},
 ]
 black = [
-    {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"},
-    {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"},
-    {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"},
-    {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"},
-    {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"},
-    {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"},
-    {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"},
-    {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"},
-    {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"},
-    {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"},
-    {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"},
-    {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"},
-    {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"},
-    {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"},
-    {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"},
-    {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"},
-    {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"},
-    {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"},
-    {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"},
-    {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"},
-    {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"},
-    {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"},
-    {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"},
+    {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"},
+    {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"},
+    {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"},
+    {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"},
+    {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"},
+    {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"},
+    {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"},
+    {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"},
+    {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"},
+    {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"},
+    {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"},
+    {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"},
+    {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"},
+    {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"},
+    {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"},
+    {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"},
+    {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"},
+    {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"},
+    {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"},
+    {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"},
+    {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"},
+    {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"},
+    {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"},
 ]
 bleach = [
-    {file = "bleach-5.0.0-py3-none-any.whl", hash = "sha256:08a1fe86d253b5c88c92cc3d810fd8048a16d15762e1e5b74d502256e5926aa1"},
-    {file = "bleach-5.0.0.tar.gz", hash = "sha256:c6d6cc054bdc9c83b48b8083e236e5f00f238428666d2ce2e083eaa5fd568565"},
+    {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"},
+    {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"},
 ]
 brotli = [
     {file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"},
@@ -1664,72 +1592,86 @@ brotli = [
     {file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
 ]
 certifi = [
-    {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
-    {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
+    {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"},
+    {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"},
 ]
 cffi = [
-    {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
-    {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
-    {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
-    {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
-    {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
-    {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
-    {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
-    {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
-    {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
-    {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
-    {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
-    {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
-    {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
-    {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
-    {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
-    {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
-    {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
-    {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
-    {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
-    {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
-    {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
-    {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
-    {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
-    {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
-    {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
+    {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
+    {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
+    {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
+    {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
+    {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
+    {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
+    {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
+    {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
+    {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
+    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
+    {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
+    {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
+    {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
+    {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
+    {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
+    {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
+    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
+    {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
+    {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
+    {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
+    {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
+    {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
+    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
+    {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
+    {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
+    {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
+    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
+    {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
+    {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
+    {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
+    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
+    {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
+    {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
+    {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
+    {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
+    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
+    {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
+    {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
+    {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
+    {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
+    {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
 ]
 charset-normalizer = [
-    {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
-    {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
+    {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"},
+    {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"},
 ]
 click = [
     {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
     {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
 ]
 colorama = [
-    {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
-    {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+    {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
+    {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
 ]
 coverage = [
     {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"},
@@ -1775,12 +1717,12 @@ coverage = [
     {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"},
 ]
 dash = [
-    {file = "dash-2.4.1-py3-none-any.whl", hash = "sha256:44ad5593d69f8aba37e4dd8fa07b1e5dfc012aebd8cccf6e4f68591bca02d177"},
-    {file = "dash-2.4.1.tar.gz", hash = "sha256:aef5fdbb2e948a378c1c8175d52a76c59cf998c62b25eaff616cd947310b04d3"},
+    {file = "dash-2.5.1-py3-none-any.whl", hash = "sha256:1b9110bcde7559a8405095901d4f7cfcfd18054b0c9758ec8562b3977824fbfe"},
+    {file = "dash-2.5.1.tar.gz", hash = "sha256:343c802006abcaf71aadd8c3f55737ea7c72116e62fff465e980f9f1f304f1ee"},
 ]
 dash-bootstrap-components = [
-    {file = "dash-bootstrap-components-1.1.0.tar.gz", hash = "sha256:340755d3ca4d2e5b2745fc67398785e673092faef2097e2895f8580b1ad1816e"},
-    {file = "dash_bootstrap_components-1.1.0-py3-none-any.whl", hash = "sha256:e0490bf865c61f14371e537bdab4be08db693cec01884eb5a9c55489ebd13d47"},
+    {file = "dash-bootstrap-components-1.2.0.tar.gz", hash = "sha256:4f95470952ecb9a0f2dbef34be2969353c5c16925817d48da50db51ade80f50f"},
+    {file = "dash_bootstrap_components-1.2.0-py3-none-any.whl", hash = "sha256:d7fd69cb2b1e86f9cc4bcee4036302e5d534d0bf102d331b29392a3c355d776a"},
 ]
 dash-core-components = [
     {file = "dash_core_components-2.0.0-py3-none-any.whl", hash = "sha256:52b8e8cce13b18d0802ee3acbc5e888cb1248a04968f962d63d070400af2e346"},
@@ -1851,36 +1793,32 @@ idna = [
     {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
 ]
 importlib-metadata = [
-    {file = "importlib_metadata-4.11.4-py3-none-any.whl", hash = "sha256:c58c8eb8a762858f49e18436ff552e83914778e50e9d2f1660535ffb364552ec"},
-    {file = "importlib_metadata-4.11.4.tar.gz", hash = "sha256:5d26852efe48c0a32b0509ffbc583fda1a2266545a78d104a6f4aff3db17d700"},
+    {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
+    {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
 ]
 importlib-resources = [
-    {file = "importlib_resources-5.7.1-py3-none-any.whl", hash = "sha256:e447dc01619b1e951286f3929be820029d48c75eb25d265c28b92a16548212b8"},
-    {file = "importlib_resources-5.7.1.tar.gz", hash = "sha256:b6062987dfc51f0fcb809187cffbd60f35df7acb4589091f154214af6d0d49d3"},
+    {file = "importlib_resources-5.8.0-py3-none-any.whl", hash = "sha256:7952325ffd516c05a8ad0858c74dff2c3343f136fe66a6002b2623dd1d43f223"},
+    {file = "importlib_resources-5.8.0.tar.gz", hash = "sha256:568c9f16cb204f9decc8d6d24a572eeea27dacbb4cee9e6b03a8025736769751"},
 ]
 iniconfig = [
     {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
     {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 ]
 ipykernel = [
-    {file = "ipykernel-6.13.0-py3-none-any.whl", hash = "sha256:2b0987af43c0d4b62cecb13c592755f599f96f29aafe36c01731aaa96df30d39"},
-    {file = "ipykernel-6.13.0.tar.gz", hash = "sha256:0e28273e290858393e86e152b104e5506a79c13d25b951ac6eca220051b4be60"},
-]
-ipysheet = [
-    {file = "ipysheet-0.5.0-py2.py3-none-any.whl", hash = "sha256:d9b3827f163177e7c293e7f7d3eaf1bbc6984e75cacee3c1a763971f110c076a"},
-    {file = "ipysheet-0.5.0.tar.gz", hash = "sha256:714804a9e6e7e413ef8e74fce026587239a853c3912060c2fcf5f8dce0b7db6c"},
+    {file = "ipykernel-6.15.0-py3-none-any.whl", hash = "sha256:b9ed519a29eb819eb82e87e0d3754088237b233e5c647b8bb0ff23c8c70ed16f"},
+    {file = "ipykernel-6.15.0.tar.gz", hash = "sha256:b59f9d9672c3a483494bb75915a2b315e78b833a38b039b1ee36dc28683f0d89"},
 ]
 ipython = [
-    {file = "ipython-8.3.0-py3-none-any.whl", hash = "sha256:341456643a764c28f670409bbd5d2518f9b82c013441084ff2c2fc999698f83b"},
-    {file = "ipython-8.3.0.tar.gz", hash = "sha256:807ae3cf43b84693c9272f70368440a9a7eaa2e7e6882dad943c32fbf7e51402"},
+    {file = "ipython-8.4.0-py3-none-any.whl", hash = "sha256:7ca74052a38fa25fe9bedf52da0be7d3fdd2fb027c3b778ea78dfe8c212937d1"},
+    {file = "ipython-8.4.0.tar.gz", hash = "sha256:f2db3a10254241d9b447232cec8b424847f338d9d36f9a577a6192c332a46abd"},
 ]
 ipython-genutils = [
     {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
     {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
 ]
 ipywidgets = [
-    {file = "ipywidgets-7.7.0-py2.py3-none-any.whl", hash = "sha256:e58ff58bc94d481e91ecb6e13a5cb96a87b6b8ade135e055603d0ca24593df38"},
-    {file = "ipywidgets-7.7.0.tar.gz", hash = "sha256:ab4a5596855a88b83761921c768707d65e5847068139bc1729ddfe834703542a"},
+    {file = "ipywidgets-7.7.1-py2.py3-none-any.whl", hash = "sha256:aa1076ab7102b2486ae2607c43c243200a07c17d6093676c419d4b6762489a50"},
+    {file = "ipywidgets-7.7.1.tar.gz", hash = "sha256:5f2fa1b7afae1af32c88088c9828ad978de93ddda393d7ed414e553fee93dcab"},
 ]
 itsdangerous = [
     {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
@@ -1895,8 +1833,8 @@ jinja2 = [
     {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
 ]
 jsonschema = [
-    {file = "jsonschema-4.5.1-py3-none-any.whl", hash = "sha256:71b5e39324422543546572954ce71c67728922c104902cb7ce252e522235b33f"},
-    {file = "jsonschema-4.5.1.tar.gz", hash = "sha256:7c6d882619340c3347a1bf7315e147e6d3dae439033ae6383d6acb908c101dfc"},
+    {file = "jsonschema-4.6.1-py3-none-any.whl", hash = "sha256:5eb781753403847fb320f05e9ab2191725b58c5e7f97f1bed63285ca423159bc"},
+    {file = "jsonschema-4.6.1.tar.gz", hash = "sha256:ec2802e6a37517f09d47d9ba107947589ae1d25ff557b925d83a321fc2aa5d3b"},
 ]
 jupyter = [
     {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"},
@@ -1904,12 +1842,12 @@ jupyter = [
     {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"},
 ]
 jupyter-client = [
-    {file = "jupyter_client-7.3.1-py3-none-any.whl", hash = "sha256:404abe552540aff3527e66e16beb114b6b4ff58479d51a301f4eb9701e4f52ef"},
-    {file = "jupyter_client-7.3.1.tar.gz", hash = "sha256:05d4ff6a0ade25138c6bb0fbeac7ddc26b5fe835e7dd816b64b4a45b931bdc0b"},
+    {file = "jupyter_client-7.3.4-py3-none-any.whl", hash = "sha256:17d74b0d0a7b24f1c8c527b24fcf4607c56bee542ffe8e3418e50b21e514b621"},
+    {file = "jupyter_client-7.3.4.tar.gz", hash = "sha256:aa9a6c32054b290374f95f73bb0cae91455c58dfb84f65c8591912b8f65e6d56"},
 ]
 jupyter-console = [
-    {file = "jupyter_console-6.4.3-py3-none-any.whl", hash = "sha256:e630bcb682c0088dda45688ad7c2424d4a825c8acf494cb036ced03ed0424841"},
-    {file = "jupyter_console-6.4.3.tar.gz", hash = "sha256:55f32626b0be647a85e3217ddcdb22db69efc79e8b403b9771eb9ecc696019b5"},
+    {file = "jupyter_console-6.4.4-py3-none-any.whl", hash = "sha256:756df7f4f60c986e7bc0172e4493d3830a7e6e75c08750bbe59c0a5403ad6dee"},
+    {file = "jupyter_console-6.4.4.tar.gz", hash = "sha256:172f5335e31d600df61613a97b7f0352f2c8250bbd1092ef2d658f77249f89fb"},
 ]
 jupyter-core = [
     {file = "jupyter_core-4.10.0-py3-none-any.whl", hash = "sha256:e7f5212177af7ab34179690140f188aa9bf3d322d8155ed972cbded19f55b6f3"},
@@ -1919,17 +1857,13 @@ jupyter-dash = [
     {file = "jupyter-dash-0.4.2.tar.gz", hash = "sha256:d546c7c25a2867c14c95a48af0ad572803b26915a5ce6052158c9dede4dbf48c"},
     {file = "jupyter_dash-0.4.2-py3-none-any.whl", hash = "sha256:b07d90ccf38d4dfb04efd630a2b2627f367b79fa4296ee3912d0c4e21e73e9b2"},
 ]
-jupyter-server = [
-    {file = "jupyter_server-1.17.0-py3-none-any.whl", hash = "sha256:5aa5e0945e3dbf29390cfe9c418a9af245d812ce282932ae97d0671e10c147a0"},
-    {file = "jupyter_server-1.17.0.tar.gz", hash = "sha256:7b3aa524790ab0da64f06dfe0b2af149d0a3f59aad71fdedcf1d8bae6508018c"},
-]
 jupyterlab-pygments = [
     {file = "jupyterlab_pygments-0.2.2-py2.py3-none-any.whl", hash = "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f"},
     {file = "jupyterlab_pygments-0.2.2.tar.gz", hash = "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d"},
 ]
 jupyterlab-widgets = [
-    {file = "jupyterlab_widgets-1.1.0-py3-none-any.whl", hash = "sha256:c2a9bd3789f120f64d73268c066ed3b000c56bc1dda217be5cdc43e7b4ebad3f"},
-    {file = "jupyterlab_widgets-1.1.0.tar.gz", hash = "sha256:d5f41bc1713795385f718d44dcba47e1e1473c6289f28a95aa6b2c0782ee372a"},
+    {file = "jupyterlab_widgets-1.1.1-py3-none-any.whl", hash = "sha256:90ab47d99da03a3697074acb23b2975ead1d6171aa41cb2812041a7f2a08177a"},
+    {file = "jupyterlab_widgets-1.1.1.tar.gz", hash = "sha256:67d0ef1e407e0c42c8ab60b9d901cd7a4c68923650763f75bf17fb06c1943b79"},
 ]
 markupsafe = [
     {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
@@ -2015,8 +1949,8 @@ mypy-extensions = [
     {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
 ]
 nbclient = [
-    {file = "nbclient-0.6.3-py3-none-any.whl", hash = "sha256:2747ac9b385720d8a6c34f2f71e72cbe64aec6cadaadcc064a4df0b0e99c5874"},
-    {file = "nbclient-0.6.3.tar.gz", hash = "sha256:b80726fc1fb89a0e8f8be1e77e28d0026b1e8ed90bc143c8a0c7622e4f8cdd9e"},
+    {file = "nbclient-0.6.6-py3-none-any.whl", hash = "sha256:09bae4ea2df79fa6bc50aeb8278d8b79d2036792824337fa6eee834afae17312"},
+    {file = "nbclient-0.6.6.tar.gz", hash = "sha256:0df76a7961d99a681b4796c74a1f2553b9f998851acc01896dce064ad19a9027"},
 ]
 nbconvert = [
     {file = "nbconvert-6.5.0-py3-none-any.whl", hash = "sha256:c56dd0b8978a1811a5654f74c727ff16ca87dd5a43abd435a1c49b840fcd8360"},
@@ -2026,6 +1960,10 @@ nbformat = [
     {file = "nbformat-5.4.0-py3-none-any.whl", hash = "sha256:0d6072aaec95dddc39735c144ee8bbc6589c383fb462e4058abc855348152dad"},
     {file = "nbformat-5.4.0.tar.gz", hash = "sha256:44ba5ca6acb80c5d5a500f1e5b83ede8cbe364d5a495c4c8cf60aaf1ba656501"},
 ]
+nbstripout = [
+    {file = "nbstripout-0.5.0-py2.py3-none-any.whl", hash = "sha256:1e8a471780d1429aaa5d954c4d26c312df8666cc84de0ff160d62d8b576ae65b"},
+    {file = "nbstripout-0.5.0.tar.gz", hash = "sha256:86ab50136998d62c9fa92478d2eb9ddc4137e51a28568f78fa8f24a6fbb6a7d8"},
+]
 nest-asyncio = [
     {file = "nest_asyncio-1.5.5-py3-none-any.whl", hash = "sha256:b98e3ec1b246135e4642eceffa5a6c23a3ab12c82ff816a92c612d68205813b2"},
     {file = "nest_asyncio-1.5.5.tar.gz", hash = "sha256:e442291cd942698be619823a17a86a5759eabe1f8613084790de189fe9e16d65"},
@@ -2035,53 +1973,55 @@ notebook = [
     {file = "notebook-6.4.12.tar.gz", hash = "sha256:6268c9ec9048cff7a45405c990c29ac9ca40b0bc3ec29263d218c5e01f2b4e86"},
 ]
 numpy = [
-    {file = "numpy-1.22.3-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75"},
-    {file = "numpy-1.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab"},
-    {file = "numpy-1.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e"},
-    {file = "numpy-1.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4"},
-    {file = "numpy-1.22.3-cp310-cp310-win32.whl", hash = "sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430"},
-    {file = "numpy-1.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4"},
-    {file = "numpy-1.22.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce"},
-    {file = "numpy-1.22.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe"},
-    {file = "numpy-1.22.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5"},
-    {file = "numpy-1.22.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1"},
-    {file = "numpy-1.22.3-cp38-cp38-win32.whl", hash = "sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62"},
-    {file = "numpy-1.22.3-cp38-cp38-win_amd64.whl", hash = "sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676"},
-    {file = "numpy-1.22.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123"},
-    {file = "numpy-1.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802"},
-    {file = "numpy-1.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d"},
-    {file = "numpy-1.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168"},
-    {file = "numpy-1.22.3-cp39-cp39-win32.whl", hash = "sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa"},
-    {file = "numpy-1.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a"},
-    {file = "numpy-1.22.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f"},
-    {file = "numpy-1.22.3.zip", hash = "sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18"},
+    {file = "numpy-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58bfd40eb478f54ff7a5710dd61c8097e169bc36cc68333d00a9bcd8def53b38"},
+    {file = "numpy-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:196cd074c3f97c4121601790955f915187736f9cf458d3ee1f1b46aff2b1ade0"},
+    {file = "numpy-1.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1d88ef79e0a7fa631bb2c3dda1ea46b32b1fe614e10fedd611d3d5398447f2f"},
+    {file = "numpy-1.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d54b3b828d618a19779a84c3ad952e96e2c2311b16384e973e671aa5be1f6187"},
+    {file = "numpy-1.23.0-cp310-cp310-win32.whl", hash = "sha256:2b2da66582f3a69c8ce25ed7921dcd8010d05e59ac8d89d126a299be60421171"},
+    {file = "numpy-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:97a76604d9b0e79f59baeca16593c711fddb44936e40310f78bfef79ee9a835f"},
+    {file = "numpy-1.23.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d8cc87bed09de55477dba9da370c1679bd534df9baa171dd01accbb09687dac3"},
+    {file = "numpy-1.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f0f18804df7370571fb65db9b98bf1378172bd4e962482b857e612d1fec0f53e"},
+    {file = "numpy-1.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac86f407873b952679f5f9e6c0612687e51547af0e14ddea1eedfcb22466babd"},
+    {file = "numpy-1.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae8adff4172692ce56233db04b7ce5792186f179c415c37d539c25de7298d25d"},
+    {file = "numpy-1.23.0-cp38-cp38-win32.whl", hash = "sha256:fe8b9683eb26d2c4d5db32cd29b38fdcf8381324ab48313b5b69088e0e355379"},
+    {file = "numpy-1.23.0-cp38-cp38-win_amd64.whl", hash = "sha256:5043bcd71fcc458dfb8a0fc5509bbc979da0131b9d08e3d5f50fb0bbb36f169a"},
+    {file = "numpy-1.23.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c29b44905af288b3919803aceb6ec7fec77406d8b08aaa2e8b9e63d0fe2f160"},
+    {file = "numpy-1.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98e8e0d8d69ff4d3fa63e6c61e8cfe2d03c29b16b58dbef1f9baa175bbed7860"},
+    {file = "numpy-1.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a506cacf2be3a74ead5467aee97b81fca00c9c4c8b3ba16dbab488cd99ba10"},
+    {file = "numpy-1.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:092f5e6025813e64ad6d1b52b519165d08c730d099c114a9247c9bb635a2a450"},
+    {file = "numpy-1.23.0-cp39-cp39-win32.whl", hash = "sha256:d6ca8dabe696c2785d0c8c9b0d8a9b6e5fdbe4f922bde70d57fa1a2848134f95"},
+    {file = "numpy-1.23.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc431493df245f3c627c0c05c2bd134535e7929dbe2e602b80e42bf52ff760bc"},
+    {file = "numpy-1.23.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f9c3fc2adf67762c9fe1849c859942d23f8d3e0bee7b5ed3d4a9c3eeb50a2f07"},
+    {file = "numpy-1.23.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0d2094e8f4d760500394d77b383a1b06d3663e8892cdf5df3c592f55f3bff66"},
+    {file = "numpy-1.23.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:94b170b4fa0168cd6be4becf37cb5b127bd12a795123984385b8cd4aca9857e5"},
+    {file = "numpy-1.23.0.tar.gz", hash = "sha256:bd3fa4fe2e38533d5336e1272fc4e765cabbbde144309ccee8675509d5cd7b05"},
 ]
 packaging = [
     {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
     {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
 ]
 pandas = [
-    {file = "pandas-1.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be67c782c4f1b1f24c2f16a157e12c2693fd510f8df18e3287c77f33d124ed07"},
-    {file = "pandas-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5a206afa84ed20e07603f50d22b5f0db3fb556486d8c2462d8bc364831a4b417"},
-    {file = "pandas-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0010771bd9223f7afe5f051eb47c4a49534345dfa144f2f5470b27189a4dd3b5"},
-    {file = "pandas-1.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3228198333dd13c90b6434ddf61aa6d57deaca98cf7b654f4ad68a2db84f8cfe"},
-    {file = "pandas-1.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b79af3a69e5175c6fa7b4e046b21a646c8b74e92c6581a9d825687d92071b51"},
-    {file = "pandas-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5586cc95692564b441f4747c47c8a9746792e87b40a4680a2feb7794defb1ce3"},
-    {file = "pandas-1.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:061609334a8182ab500a90fe66d46f6f387de62d3a9cb9aa7e62e3146c712167"},
-    {file = "pandas-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b8134651258bce418cb79c71adeff0a44090c98d955f6953168ba16cc285d9f7"},
-    {file = "pandas-1.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df82739e00bb6daf4bba4479a40f38c718b598a84654cbd8bb498fd6b0aa8c16"},
-    {file = "pandas-1.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:385c52e85aaa8ea6a4c600a9b2821181a51f8be0aee3af6f2dcb41dafc4fc1d0"},
-    {file = "pandas-1.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295872bf1a09758aba199992c3ecde455f01caf32266d50abc1a073e828a7b9d"},
-    {file = "pandas-1.4.2-cp38-cp38-win32.whl", hash = "sha256:95c1e422ced0199cf4a34385ff124b69412c4bc912011ce895582bee620dfcaa"},
-    {file = "pandas-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5c54ea4ef3823108cd4ec7fb27ccba4c3a775e0f83e39c5e17f5094cb17748bc"},
-    {file = "pandas-1.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c072c7f06b9242c855ed8021ff970c0e8f8b10b35e2640c657d2a541c5950f59"},
-    {file = "pandas-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f549097993744ff8c41b5e8f2f0d3cbfaabe89b4ae32c8c08ead6cc535b80139"},
-    {file = "pandas-1.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff08a14ef21d94cdf18eef7c569d66f2e24e0bc89350bcd7d243dd804e3b5eb2"},
-    {file = "pandas-1.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c5bf555b6b0075294b73965adaafb39cf71c312e38c5935c93d78f41c19828a"},
-    {file = "pandas-1.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51649ef604a945f781105a6d2ecf88db7da0f4868ac5d45c51cb66081c4d9c73"},
-    {file = "pandas-1.4.2-cp39-cp39-win32.whl", hash = "sha256:d0d4f13e4be7ce89d7057a786023c461dd9370040bdb5efa0a7fe76b556867a0"},
-    {file = "pandas-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:09d8be7dd9e1c4c98224c4dfe8abd60d145d934e9fc1f5f411266308ae683e6a"},
-    {file = "pandas-1.4.2.tar.gz", hash = "sha256:92bc1fc585f1463ca827b45535957815b7deb218c549b7c18402c322c7549a12"},
+    {file = "pandas-1.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d51674ed8e2551ef7773820ef5dab9322be0828629f2cbf8d1fc31a0c4fed640"},
+    {file = "pandas-1.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:16ad23db55efcc93fa878f7837267973b61ea85d244fc5ff0ccbcfa5638706c5"},
+    {file = "pandas-1.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:958a0588149190c22cdebbc0797e01972950c927a11a900fe6c2296f207b1d6f"},
+    {file = "pandas-1.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e48fbb64165cda451c06a0f9e4c7a16b534fcabd32546d531b3c240ce2844112"},
+    {file = "pandas-1.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f803320c9da732cc79210d7e8cc5c8019aad512589c910c66529eb1b1818230"},
+    {file = "pandas-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:2893e923472a5e090c2d5e8db83e8f907364ec048572084c7d10ef93546be6d1"},
+    {file = "pandas-1.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:24ea75f47bbd5574675dae21d51779a4948715416413b30614c1e8b480909f81"},
+    {file = "pandas-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ebc990bd34f4ac3c73a2724c2dcc9ee7bf1ce6cf08e87bb25c6ad33507e318"},
+    {file = "pandas-1.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d6c0106415ff1a10c326c49bc5dd9ea8b9897a6ca0c8688eb9c30ddec49535ef"},
+    {file = "pandas-1.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78b00429161ccb0da252229bcda8010b445c4bf924e721265bec5a6e96a92e92"},
+    {file = "pandas-1.4.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfbf16b1ea4f4d0ee11084d9c026340514d1d30270eaa82a9f1297b6c8ecbf0"},
+    {file = "pandas-1.4.3-cp38-cp38-win32.whl", hash = "sha256:48350592665ea3cbcd07efc8c12ff12d89be09cd47231c7925e3b8afada9d50d"},
+    {file = "pandas-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:605d572126eb4ab2eadf5c59d5d69f0608df2bf7bcad5c5880a47a20a0699e3e"},
+    {file = "pandas-1.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a3924692160e3d847e18702bb048dc38e0e13411d2b503fecb1adf0fcf950ba4"},
+    {file = "pandas-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07238a58d7cbc8a004855ade7b75bbd22c0db4b0ffccc721556bab8a095515f6"},
+    {file = "pandas-1.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:755679c49460bd0d2f837ab99f0a26948e68fa0718b7e42afbabd074d945bf84"},
+    {file = "pandas-1.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41fc406e374590a3d492325b889a2686b31e7a7780bec83db2512988550dadbf"},
+    {file = "pandas-1.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d9382f72a4f0e93909feece6fef5500e838ce1c355a581b3d8f259839f2ea76"},
+    {file = "pandas-1.4.3-cp39-cp39-win32.whl", hash = "sha256:0daf876dba6c622154b2e6741f29e87161f844e64f84801554f879d27ba63c0d"},
+    {file = "pandas-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:721a3dd2f06ef942f83a819c0f3f6a648b2830b191a72bbe9451bcd49c3bd42e"},
+    {file = "pandas-1.4.3.tar.gz", hash = "sha256:2ff7788468e75917574f080cd4681b27e1a7bf36461fe968b49a87b5a54d007c"},
 ]
 pandocfilters = [
     {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"},
@@ -2108,8 +2048,8 @@ platformdirs = [
     {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
 ]
 plotly = [
-    {file = "plotly-5.8.0-py2.py3-none-any.whl", hash = "sha256:0e6e2382aafe2b2978d2c1b10ea93e73ad1ec80fa9a195ff6eea62af7905dfdc"},
-    {file = "plotly-5.8.0.tar.gz", hash = "sha256:58cef3292f5994d82154d51fbc7338c48009fc47ea32ffe052ad29aaa15e0df9"},
+    {file = "plotly-5.9.0-py2.py3-none-any.whl", hash = "sha256:9acf168915101caac82ed6da3390840c622d9d4e4a78cbe1260f52501efc3117"},
+    {file = "plotly-5.9.0.tar.gz", hash = "sha256:b0536e72bbc0b3cf169ac1fd00759d77aae7bb12ae378cdc75c5dc362f5de576"},
 ]
 pluggy = [
     {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
@@ -2120,42 +2060,42 @@ prometheus-client = [
     {file = "prometheus_client-0.14.1.tar.gz", hash = "sha256:5459c427624961076277fdc6dc50540e2bacb98eebde99886e59ec55ed92093a"},
 ]
 prompt-toolkit = [
-    {file = "prompt_toolkit-3.0.29-py3-none-any.whl", hash = "sha256:62291dad495e665fca0bda814e342c69952086afb0f4094d0893d357e5c78752"},
-    {file = "prompt_toolkit-3.0.29.tar.gz", hash = "sha256:bd640f60e8cecd74f0dc249713d433ace2ddc62b65ee07f96d358e0b152b6ea7"},
+    {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"},
+    {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"},
 ]
 psutil = [
-    {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"},
-    {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"},
-    {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"},
-    {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"},
-    {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"},
-    {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"},
-    {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"},
-    {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"},
-    {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"},
-    {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"},
-    {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"},
-    {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"},
-    {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"},
-    {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"},
-    {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"},
-    {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"},
-    {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"},
-    {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"},
-    {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"},
-    {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"},
-    {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"},
-    {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"},
-    {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"},
-    {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"},
-    {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"},
-    {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"},
-    {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"},
-    {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"},
-    {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"},
-    {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"},
-    {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"},
-    {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"},
+    {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"},
+    {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"},
+    {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"},
+    {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"},
+    {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"},
+    {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"},
+    {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"},
+    {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"},
+    {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"},
+    {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"},
+    {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"},
+    {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"},
+    {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"},
+    {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"},
+    {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"},
+    {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"},
+    {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"},
+    {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"},
+    {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"},
+    {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"},
+    {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"},
+    {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"},
+    {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"},
+    {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"},
+    {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"},
+    {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"},
+    {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"},
+    {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"},
+    {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"},
+    {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"},
+    {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"},
+    {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"},
 ]
 ptyprocess = [
     {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
@@ -2321,65 +2261,75 @@ pywinpty = [
     {file = "pywinpty-2.0.5.tar.gz", hash = "sha256:e125d3f1804d8804952b13e33604ad2ca8b9b2cac92b27b521c005d1604794f8"},
 ]
 pyzmq = [
-    {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:6b217b8f9dfb6628f74b94bdaf9f7408708cb02167d644edca33f38746ca12dd"},
-    {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2841997a0d85b998cbafecb4183caf51fd19c4357075dfd33eb7efea57e4c149"},
-    {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f89468059ebc519a7acde1ee50b779019535db8dcf9b8c162ef669257fef7a93"},
-    {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea12133df25e3a6918718fbb9a510c6ee5d3fdd5a346320421aac3882f4feeea"},
-    {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c532fd68b93998aab92356be280deec5de8f8fe59cd28763d2cc8a58747b7f"},
-    {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f907c7359ce8bf7f7e63c82f75ad0223384105f5126f313400b7e8004d9b33c3"},
-    {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:902319cfe23366595d3fa769b5b751e6ee6750a0a64c5d9f757d624b2ac3519e"},
-    {file = "pyzmq-22.3.0-cp310-cp310-win32.whl", hash = "sha256:67db33bea0a29d03e6eeec55a8190e033318cee3cbc732ba8fd939617cbf762d"},
-    {file = "pyzmq-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:7661fc1d5cb73481cf710a1418a4e1e301ed7d5d924f91c67ba84b2a1b89defd"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79244b9e97948eaf38695f4b8e6fc63b14b78cc37f403c6642ba555517ac1268"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab888624ed68930442a3f3b0b921ad7439c51ba122dbc8c386e6487a658e4a4e"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18cd854b423fce44951c3a4d3e686bac8f1243d954f579e120a1714096637cc0"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:de8df0684398bd74ad160afdc2a118ca28384ac6f5e234eb0508858d8d2d9364"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:62bcade20813796c426409a3e7423862d50ff0639f5a2a95be4b85b09a618666"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ea5a79e808baef98c48c884effce05c31a0698c1057de8fc1c688891043c1ce1"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-win32.whl", hash = "sha256:3c1895c95be92600233e476fe283f042e71cf8f0b938aabf21b7aafa62a8dac9"},
-    {file = "pyzmq-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:851977788b9caa8ed011f5f643d3ee8653af02c5fc723fa350db5125abf2be7b"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4ebed0977f92320f6686c96e9e8dd29eed199eb8d066936bac991afc37cbb70"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42abddebe2c6a35180ca549fadc7228d23c1e1f76167c5ebc8a936b5804ea2df"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1e41b32d6f7f9c26bc731a8b529ff592f31fc8b6ef2be9fa74abd05c8a342d7"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:be4e0f229cf3a71f9ecd633566bd6f80d9fa6afaaff5489492be63fe459ef98c"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:badb868fff14cfd0e200eaa845887b1011146a7d26d579aaa7f966c203736b92"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-win32.whl", hash = "sha256:7c58f598d9fcc52772b89a92d72bf8829c12d09746a6d2c724c5b30076c1f11d"},
-    {file = "pyzmq-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2b97502c16a5ec611cd52410bdfaab264997c627a46b0f98d3f666227fd1ea2d"},
-    {file = "pyzmq-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d728b08448e5ac3e4d886b165385a262883c34b84a7fe1166277fe675e1c197a"},
-    {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:480b9931bfb08bf8b094edd4836271d4d6b44150da051547d8c7113bf947a8b0"},
-    {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7dc09198e4073e6015d9a8ea093fc348d4e59de49382476940c3dd9ae156fba8"},
-    {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ca6cd58f62a2751728016d40082008d3b3412a7f28ddfb4a2f0d3c130f69e74"},
-    {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:468bd59a588e276961a918a3060948ae68f6ff5a7fa10bb2f9160c18fe341067"},
-    {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c88fa7410e9fc471e0858638f403739ee869924dd8e4ae26748496466e27ac59"},
-    {file = "pyzmq-22.3.0-cp38-cp38-win32.whl", hash = "sha256:c0f84360dcca3481e8674393bdf931f9f10470988f87311b19d23cda869bb6b7"},
-    {file = "pyzmq-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f762442bab706fd874064ca218b33a1d8e40d4938e96c24dafd9b12e28017f45"},
-    {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:954e73c9cd4d6ae319f1c936ad159072b6d356a92dcbbabfd6e6204b9a79d356"},
-    {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f43b4a2e6218371dd4f41e547bd919ceeb6ebf4abf31a7a0669cd11cd91ea973"},
-    {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:acebba1a23fb9d72b42471c3771b6f2f18dcd46df77482612054bd45c07dfa36"},
-    {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cf98fd7a6c8aaa08dbc699ffae33fd71175696d78028281bc7b832b26f00ca57"},
-    {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d072f7dfbdb184f0786d63bda26e8a0882041b1e393fbe98940395f7fab4c5e2"},
-    {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:53f4fd13976789ffafedd4d46f954c7bb01146121812b72b4ddca286034df966"},
-    {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1b5d457acbadcf8b27561deeaa386b0217f47626b29672fa7bd31deb6e91e1b"},
-    {file = "pyzmq-22.3.0-cp39-cp39-win32.whl", hash = "sha256:e6a02cf7271ee94674a44f4e62aa061d2d049001c844657740e156596298b70b"},
-    {file = "pyzmq-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d3dcb5548ead4f1123851a5ced467791f6986d68c656bc63bfff1bf9e36671e2"},
-    {file = "pyzmq-22.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3a4c9886d61d386b2b493377d980f502186cd71d501fffdba52bd2a0880cef4f"},
-    {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:80e043a89c6cadefd3a0712f8a1322038e819ebe9dbac7eca3bce1721bcb63bf"},
-    {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1621e7a2af72cced1f6ec8ca8ca91d0f76ac236ab2e8828ac8fe909512d566cb"},
-    {file = "pyzmq-22.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d6157793719de168b199194f6b6173f0ccd3bf3499e6870fac17086072e39115"},
-    {file = "pyzmq-22.3.0.tar.gz", hash = "sha256:8eddc033e716f8c91c6a2112f0a8ebc5e00532b4a6ae1eb0ccc48e027f9c671c"},
+    {file = "pyzmq-23.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:22ac0243a41798e3eb5d5714b28c2f28e3d10792dffbc8a5fca092f975fdeceb"},
+    {file = "pyzmq-23.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f685003d836ad0e5d4f08d1e024ee3ac7816eb2f873b2266306eef858f058133"},
+    {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d4651de7316ec8560afe430fb042c0782ed8ac54c0be43a515944d7c78fddac8"},
+    {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bcc6953e47bcfc9028ddf9ab2a321a3c51d7cc969db65edec092019bb837959f"},
+    {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e08671dc202a1880fa522f921f35ca5925ba30da8bc96228d74a8f0643ead9c"},
+    {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de727ea906033b30527b4a99498f19aca3f4d1073230a958679a5b726e2784e0"},
+    {file = "pyzmq-23.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5aa9da520e4bb8cee8189f2f541701405e7690745094ded7a37b425d60527ea"},
+    {file = "pyzmq-23.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f3ff6abde52e702397949054cb5b06c1c75b5d6542f6a2ce029e46f71ffbbbf2"},
+    {file = "pyzmq-23.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e2e2db5c6ef376e97c912733dfc24406f5949474d03e800d5f07b6aca4d870af"},
+    {file = "pyzmq-23.2.0-cp310-cp310-win32.whl", hash = "sha256:e669913cb2179507628419ec4f0e453e48ce6f924de5884d396f18c31836089c"},
+    {file = "pyzmq-23.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:a3dc339f7bc185d5fd0fd976242a5baf35de404d467e056484def8a4dd95868b"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:30c365e60c39c53f8eea042b37ea28304ffa6558fb7241cf278745095a5757da"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c2d8b69a2bf239ae3d987537bf3fbc2b044a405394cf4c258fc684971dd48b2"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:602835e5672ca9ca1d78e6c148fb28c4f91b748ebc41fbd2f479d8763d58bc9b"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:831da96ba3f36cc892f0afbb4fb89b28b61b387261676e55d55a682addbd29f7"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c8dec8a2f3f0bb462e6439df436cd8c7ec37968e90b4209ac621e7fbc0ed3b00"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:814e5aaf0c3be9991a59066eafb2d6e117aed6b413e3e7e9be45d4e55f5e2748"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-win32.whl", hash = "sha256:8496a2a5efd055c61ac2c6a18116c768a25c644b6747dcfde43e91620ab3453c"},
+    {file = "pyzmq-23.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:60746a7e8558655420a69441c0a1d47ed225ed3ac355920b96a96d0554ef7e6b"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5cb642e94337b0c76c9c8cb9bfb0f8a78654575847d080d3e1504f312d691fc3"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:444f7d615d5f686d0ef508b9edfa8a286e6d89f449a1ba37b60ef69d869220a3"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c9638e0057e3f1a8b7c5ce33c7575349d9183a033a19b5676ad55096ae36820b"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:004a431dfa0459123e6f4660d7e3c4ac19217d134ca38bacfffb2e78716fe944"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5592fb4316f895922b1cacb91b04a0fa09d6f6f19bbab4442b4d0a0825177b93"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c0a5f987d73fd9b46c3d180891f829afda714ab6bab30a1218724d4a0a63afd8"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-win32.whl", hash = "sha256:d11628212fd731b8986f1561d9bb3f8c38d9c15b330c3d8a88963519fbcd553b"},
+    {file = "pyzmq-23.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:558f5f636e3e65f261b64925e8b190e8689e334911595394572cc7523879006d"},
+    {file = "pyzmq-23.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:61b97f624da42813f74977425a3a6144d604ea21cf065616d36ea3a866d92c1c"},
+    {file = "pyzmq-23.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:693c96ae4d975eb8efa1639670e9b1fac0c3f98b7845b65c0f369141fb4bb21f"},
+    {file = "pyzmq-23.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b054525c9f7e240562185bf21671ca16d56bde92e9bd0f822c07dec7626b704"},
+    {file = "pyzmq-23.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:859059caf564f0c9398c9005278055ed3d37af4d73de6b1597821193b04ca09b"},
+    {file = "pyzmq-23.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8355744fdbdeac5cfadfa4f38b82029b5f2b8cab7472a33453a217a7f3a9dce2"},
+    {file = "pyzmq-23.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:420b9abd1a7330687a095373b8280a20cdee04342fbc8ccb3b56d9ec8efd4e62"},
+    {file = "pyzmq-23.2.0-cp38-cp38-win32.whl", hash = "sha256:59928dfebe93cf1e203e3cb0fd5d5dd384da56b99c8305f2e1b0a933751710f6"},
+    {file = "pyzmq-23.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:c882f1d4f96fbd807e92c334251d8ebd159a1ef89059ccd386ddea83fdb91bd8"},
+    {file = "pyzmq-23.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:ced12075cdf3c7332ecc1960f77f7439d5ebb8ea20bbd3c34c8299e694f1b0a1"},
+    {file = "pyzmq-23.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3a4d87342c2737fbb9eee5c33c792db27b36b04957b4e6b7edd73a5b239a2a13"},
+    {file = "pyzmq-23.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:99cedf38eaddf263cf7e2a50e405f12c02cedf6d9df00a0d9c5d7b9417b57f76"},
+    {file = "pyzmq-23.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1610260cc672975723fcf7705c69a95f3b88802a594c9867781bedd9b13422c"},
+    {file = "pyzmq-23.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c223a13555444707a0a7ebc6f9ee63053147c8c082bd1a31fd1207a03e8b0500"},
+    {file = "pyzmq-23.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5fdb00d65ec44b10cc6b9b6318ef1363b81647a4aa3270ca39565eadb2d1201"},
+    {file = "pyzmq-23.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:984b232802eddf9f0be264a4d57a10b3a1fd7319df14ee6fc7b41c6d155a3e6c"},
+    {file = "pyzmq-23.2.0-cp39-cp39-win32.whl", hash = "sha256:f146648941cadaaaf01254a75651a23c08159d009d36c5af42a7cc200a5e53ec"},
+    {file = "pyzmq-23.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:83005d8928f8a5cebcfb33af3bfb84b1ad65d882b899141a331cc5d07d89f093"},
+    {file = "pyzmq-23.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fee86542dc4ee8229e023003e3939b4d58cc2453922cf127778b69505fc9064b"},
+    {file = "pyzmq-23.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5d57542429df6acff02ff022067aa75b677603cee70e3abb9742787545eec966"},
+    {file = "pyzmq-23.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:057b154471e096e2dda147f7b057041acc303bb7ca4aa24c3b88c6cecdd78717"},
+    {file = "pyzmq-23.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5d92e7cbeab7f70b08cc0f27255b0bb2500afc30f31075bca0b1cb87735d186c"},
+    {file = "pyzmq-23.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eb4a573a8499685d62545e806d8fd143c84ac8b3439f925cd92c8763f0ed9bd7"},
+    {file = "pyzmq-23.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:da338e2728410d74ddeb1479ec67cfba73311607037455a40f92b6f5c62bf11d"},
+    {file = "pyzmq-23.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1b2a21f595f8cc549abd6c8de1fcd34c83441e35fb24b8a59bf161889c62a486"},
+    {file = "pyzmq-23.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8c0f4d6f8c985bab83792be26ff3233940ba42e22237610ac50cbcfc10a5c235"},
+    {file = "pyzmq-23.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bbabd1df23bf63ae829e81200034c0e433499275a6ed29ca1a912ea7629426d9"},
+    {file = "pyzmq-23.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21552624ce69e69f7924f413b802b1fb554f4c0497f837810e429faa1cd4f163"},
+    {file = "pyzmq-23.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c616893a577e9d6773a3836732fd7e2a729157a108b8fccd31c87512fa01671a"},
+    {file = "pyzmq-23.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce4f71e17fa849de41a06109030d3f6815fcc33338bf98dd0dde6d456d33c929"},
+    {file = "pyzmq-23.2.0.tar.gz", hash = "sha256:a51f12a8719aad9dcfb55d456022f16b90abc8dde7d3ca93ce3120b40e3fa169"},
 ]
 qtconsole = [
-    {file = "qtconsole-5.3.0-py3-none-any.whl", hash = "sha256:75f2ded876444454edcb5a53262149e33b53db3a4a53116b7c3df52830905b0f"},
-    {file = "qtconsole-5.3.0.tar.gz", hash = "sha256:8e3520fdc75e46abc4cc6cffeca16fa2652754109b8ae839fa28e27d1eba5625"},
+    {file = "qtconsole-5.3.1-py3-none-any.whl", hash = "sha256:d364592d7ede3257f1e17fcdbfd339c26e2cc638ca4fa4ee56a724e26ea13c81"},
+    {file = "qtconsole-5.3.1.tar.gz", hash = "sha256:b73723fac43938b684dcb237a88510dc7721c43a726cea8ade179a2927c0a2f3"},
 ]
 qtpy = [
     {file = "QtPy-2.1.0-py3-none-any.whl", hash = "sha256:aee0586081f943029312becece9f63977b0a9e3788f77a6ac8cc74802bb173d6"},
     {file = "QtPy-2.1.0.tar.gz", hash = "sha256:ca8cd4217175186344299ee4c0f7e7adcf362c70852ba35b255a534077025c06"},
 ]
 requests = [
-    {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
-    {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
+    {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
+    {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
 ]
 retrying = [
     {file = "retrying-1.3.3.tar.gz", hash = "sha256:08c039560a6da2fe4f2c426d0766e284d3b736e355f8dd24b37367b0bb41973b"},
@@ -2392,17 +2342,13 @@ six = [
     {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
     {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
 ]
-sniffio = [
-    {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
-    {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
-]
 soupsieve = [
     {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
     {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
 ]
 stack-data = [
-    {file = "stack_data-0.2.0-py3-none-any.whl", hash = "sha256:999762f9c3132308789affa03e9271bbbe947bf78311851f4d485d8402ed858e"},
-    {file = "stack_data-0.2.0.tar.gz", hash = "sha256:45692d41bd633a9503a5195552df22b583caf16f0b27c4e58c98d88c8b648e12"},
+    {file = "stack_data-0.3.0-py3-none-any.whl", hash = "sha256:aa1d52d14d09c7a9a12bb740e6bdfffe0f5e8f4f9218d85e7c73a8c37f7ae38d"},
+    {file = "stack_data-0.3.0.tar.gz", hash = "sha256:77bec1402dcd0987e9022326473fdbcc767304892a533ed8c29888dacb7dddbc"},
 ]
 tenacity = [
     {file = "tenacity-8.0.1-py3-none-any.whl", hash = "sha256:f78f4ea81b0fabc06728c11dc2a8c01277bfc5181b321a4770471902e3eb844a"},
@@ -2464,8 +2410,8 @@ tornado = [
     {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"},
 ]
 traitlets = [
-    {file = "traitlets-5.2.1.post0-py3-none-any.whl", hash = "sha256:f44b708d33d98b0addb40c29d148a761f44af740603a8fd0e2f8b5b27cf0f087"},
-    {file = "traitlets-5.2.1.post0.tar.gz", hash = "sha256:70815ecb20ec619d1af28910ade523383be13754283aef90528eb3d47b77c5db"},
+    {file = "traitlets-5.3.0-py3-none-any.whl", hash = "sha256:65fa18961659635933100db8ca120ef6220555286949774b9cfc106f941d1c7a"},
+    {file = "traitlets-5.3.0.tar.gz", hash = "sha256:0bb9f1f9f017aa8ec187d8b1b2a7a6626a2a1d877116baba52a129bfa124f8e2"},
 ]
 typing-extensions = [
     {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
@@ -2483,17 +2429,13 @@ webencodings = [
     {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
     {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
 ]
-websocket-client = [
-    {file = "websocket-client-1.3.2.tar.gz", hash = "sha256:50b21db0058f7a953d67cc0445be4b948d7fc196ecbeb8083d68d94628e4abf6"},
-    {file = "websocket_client-1.3.2-py3-none-any.whl", hash = "sha256:722b171be00f2b90e1d4fb2f2b53146a536ca38db1da8ff49c972a4e1365d0ef"},
-]
 werkzeug = [
     {file = "Werkzeug-2.1.2-py3-none-any.whl", hash = "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"},
     {file = "Werkzeug-2.1.2.tar.gz", hash = "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6"},
 ]
 widgetsnbextension = [
-    {file = "widgetsnbextension-3.6.0-py2.py3-none-any.whl", hash = "sha256:4fd321cad39fdcf8a8e248a657202d42917ada8e8ed5dd3f60f073e0d54ceabd"},
-    {file = "widgetsnbextension-3.6.0.tar.gz", hash = "sha256:e84a7a9fcb9baf3d57106e184a7389a8f8eb935bf741a5eb9d60aa18cc029a80"},
+    {file = "widgetsnbextension-3.6.1-py2.py3-none-any.whl", hash = "sha256:954e0faefdd414e4e013f17dbc7fd86f24cf1d243a3ac85d5f0fc2c2d2b50c66"},
+    {file = "widgetsnbextension-3.6.1.tar.gz", hash = "sha256:9c84ae64c2893c7cbe2eaafc7505221a795c27d68938454034ac487319a75b10"},
 ]
 zipp = [
     {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
diff --git a/pyproject.toml b/pyproject.toml
index 82defcde..1c8a3dfe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,8 +6,6 @@ authors = ["Bjørn Roar Joneid <bnj@ssb.no>", "Miles Winther <mmw@ssb.no>", "Jan
 
 [tool.poetry.dependencies]
 python = "^3.8"
-ipywidgets = "^7.7.0"
-ipysheet = "^0.5.0"
 requests = "^2.27.1"
 pyarrow = "^8.0.0"
 jupyter-dash = "^0.4.2"
@@ -15,7 +13,6 @@ dash = "^2.4.1"
 pydantic = "^1.9.1"
 dash-bootstrap-components = "^1.1.0"
 pandas = "^1.4.2"
-pytest-cov = "^3.0.0"
 
 [tool.poetry.dev-dependencies]
 jupyter = "^1.0.0"
@@ -25,6 +22,8 @@ flake8 = "^4.0.1"
 mypy = "^0.950"
 black = "^22.3.0"
 pytest = "^7.1.2"
+pytest-cov = "^3.0.0"
+nbstripout = "^0.5.0"
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]


Test patch:
diff --git a/datadoc/tests/resources/existing_metadata_file/person_data_v1__DOC.json b/datadoc/tests/resources/existing_metadata_file/person_data_v1__DOC.json
index 8e6217c6..38d8263d 100644
--- a/datadoc/tests/resources/existing_metadata_file/person_data_v1__DOC.json
+++ b/datadoc/tests/resources/existing_metadata_file/person_data_v1__DOC.json
@@ -1,162 +1,188 @@
 {
-    "dataSourcePath": "klargjorte_data/person_data_v1.parquet",
-    "shortName": "person_data_v1",
-    "name": [
-        {
-            "languageCode": "no",
-            "value": "successfully_read_existing_file"
-        }
-    ],
-    "dataSetState": "PROCESSED_DATA",
-    "description": [
-        {
-            "languageCode": "no",
-            "value": ""
-        }
-    ],
-    "temporalityType": null,
-    "spatialCoverageDescription": [
+    "short_name": "person_data_v1",
+    "assessment": null,
+    "dataset_state": "PROCESSED_DATA",
+    "name": "successfully_read_existing_file",
+    "data_source": null,
+    "population_description": null,
+    "administrative_status": "DRAFT",
+    "version": "1",
+    "unit_type": null,
+    "temporality_type": null,
+    "description": null,
+    "spatial_coverage_description": [
         {
             "languageCode": "no",
             "value": "Norge"
         }
     ],
-    "populationDescription": [],
     "id": null,
-    "createdDate": "2022-06-23 15:32:41.120207",
-    "createdBy": "default_user@ssb.no",
-    "dataOwner": null,
-    "lastUpdatedDate": null,
-    "lastUpdatedBy": null,
-    "version": "1",
-    "administrativeStatus": "DRAFT",
+    "owner": null,
+    "data_source_path": "klargjorte_data/person_data_v1.parquet",
+    "created_date": "2022-06-30 16:07:42.988259",
+    "created_by": "default_user@ssb.no",
+    "last_updated_date": null,
+    "last_updated_by": null,
     "variables": [
         {
-            "shortName": "pers_id",
+            "short_name": "pers_id",
             "name": null,
-            "dataType": "STRING",
-            "variableRole": null,
+            "datatype": "STRING",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         },
         {
-            "definitionUri": null,
-            "shortName": "tidspunkt",
+            "short_name": "tidspunkt",
             "name": null,
-            "dataType": "DATETIME",
-            "variableRole": null,
+            "datatype": "DATETIME",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         },
         {
-            "definitionUri": null,
-            "shortName": "sivilstand",
+            "short_name": "sivilstand",
             "name": null,
-            "dataType": "STRING",
-            "variableRole": null,
+            "datatype": "STRING",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         },
         {
-            "definitionUri": null,
-            "shortName": "alm_inntekt",
+            "short_name": "alm_inntekt",
             "name": null,
-            "dataType": "INTEGER",
-            "variableRole": null,
+            "datatype": "INTEGER",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         },
         {
-            "definitionUri": null,
-            "shortName": "sykepenger",
+            "short_name": "sykepenger",
             "name": null,
-            "dataType": "INTEGER",
-            "variableRole": null,
+            "datatype": "INTEGER",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         },
         {
-            "definitionUri": null,
-            "shortName": "ber_bruttoformue",
+            "short_name": "ber_bruttoformue",
             "name": null,
-            "dataType": "INTEGER",
-            "variableRole": null,
+            "datatype": "INTEGER",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         },
         {
-            "definitionUri": null,
-            "shortName": "fullf_utdanning",
+            "short_name": "fullf_utdanning",
             "name": null,
-            "dataType": "STRING",
-            "variableRole": null,
+            "datatype": "STRING",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         },
         {
-            "definitionUri": null,
-            "shortName": "hoveddiagnose",
+            "short_name": "hoveddiagnose",
             "name": null,
-            "dataType": "STRING",
-            "variableRole": null,
+            "datatype": "STRING",
+            "variable_role": null,
+            "definition_uri": null,
+            "direct_person_identifying": null,
+            "data_source": null,
+            "population_description": null,
             "comment": null,
-            "populationDescription": null,
-            "temporalityType": null,
-            "mesurementType": null,
-            "measurementUnit": null,
+            "temporality_type": null,
+            "measurement_unit": null,
             "format": null,
-            "sentinelAndMissingValueUri": [],
-            "unitType": null,
-            "foreignKeyType": null
+            "classification_uri": null,
+            "sentinel_value_uri": null,
+            "invalid_value_description": null,
+            "id": null,
+            "contains_data_from": null,
+            "contains_data_until": null
         }
     ]
 }
\ No newline at end of file
diff --git a/datadoc/tests/test_datadoc_metadata.py b/datadoc/tests/test_datadoc_metadata.py
index 1494b66b..fefbde28 100644
--- a/datadoc/tests/test_datadoc_metadata.py
+++ b/datadoc/tests/test_datadoc_metadata.py
@@ -3,6 +3,8 @@
 from pathlib import PurePath
 import shutil
 import pytest
+
+from datadoc.Model import DataSetState
 from ..DataDocMetadata import DataDocMetadata
 from .utils import (
     TEST_EXISTING_METADATA_FILE_NAME,
@@ -16,9 +18,9 @@
 def make_paths():
     split_path = list(PurePath(TEST_PARQUET_FILEPATH).parts)
     initial_data = [
-        ("kildedata", "SOURCE_DATA"),
-        ("inndata", "INPUT_DATA"),
-        ("klargjorte_data", "PROCESSED_DATA"),
+        ("kildedata", DataSetState.SOURCE_DATA),
+        ("inndata", DataSetState.INPUT_DATA),
+        ("klargjorte_data", DataSetState.PROCESSED_DATA),
         ("", None),
     ]
     test_data = []
@@ -55,7 +57,5 @@ def existing_metadata_file():
 
 
 def test_existing_metadata_file(existing_metadata_file):
-    # Existing metadata file in place
-    # self.meta is filled with correct data
     metadata = DataDocMetadata(TEST_PARQUET_FILEPATH)
-    assert metadata.meta["name"][0]["value"] == "successfully_read_existing_file"
+    assert metadata.dataset_metadata.name == "successfully_read_existing_file"
diff --git a/datadoc/tests/test_dataset_reader.py b/datadoc/tests/test_dataset_reader.py
new file mode 100644
index 00000000..d8d88ee6
--- /dev/null
+++ b/datadoc/tests/test_dataset_reader.py
@@ -0,0 +1,75 @@
+import json
+import random
+
+from datadoc.Model import DataDocVariable, Datatype
+from ..DatasetReader import (
+    KNOWN_BOOLEAN_TYPES,
+    KNOWN_DATETIME_TYPES,
+    KNOWN_FLOAT_TYPES,
+    KNOWN_INTEGER_TYPES,
+    KNOWN_STRING_TYPES,
+    DatasetReader,
+)
+from .utils import TEST_PARQUET_FILEPATH, TEST_SAS7BDAT_FILEPATH
+from pytest import raises
+
+
+def test_use_abstract_class_directly():
+    with raises(TypeError):
+        DatasetReader().get_fields()
+
+
+def test_get_fields_parquet():
+    expected_fields = [
+        DataDocVariable(short_name="pers_id", datatype=Datatype.STRING),
+        DataDocVariable(short_name="tidspunkt", datatype=Datatype.DATETIME),
+        DataDocVariable(short_name="sivilstand", datatype=Datatype.STRING),
+        DataDocVariable(short_name="alm_inntekt", datatype=Datatype.INTEGER),
+        DataDocVariable(short_name="sykepenger", datatype=Datatype.INTEGER),
+        DataDocVariable(short_name="ber_bruttoformue", datatype=Datatype.INTEGER),
+        DataDocVariable(short_name="fullf_utdanning", datatype=Datatype.STRING),
+        DataDocVariable(short_name="hoveddiagnose", datatype=Datatype.STRING),
+    ]
+
+    reader = DatasetReader.for_file(TEST_PARQUET_FILEPATH)
+    fields = reader.get_fields()
+
+    assert fields == expected_fields
+
+
+def test_get_fields_sas7bdat():
+    expected_fields = [
+        DataDocVariable(short_name="tekst", name="Tekst", datatype=Datatype.STRING),
+        DataDocVariable(short_name="tall", name="Tall", datatype=Datatype.FLOAT),
+        DataDocVariable(short_name="dato", name="Dato", datatype=Datatype.DATETIME),
+    ]
+
+    reader = DatasetReader.for_file(TEST_SAS7BDAT_FILEPATH)
+    fields = reader.get_fields()
+
+    assert fields == expected_fields
+
+
+def test_get_fields_unknown_file_type():
+    with raises(NotImplementedError):
+        DatasetReader.for_file("my_dataset.csv").get_fields()
+
+
+def test_transform_datatype_unknown_type():
+    expected = None
+    input_data = "definitely not a known data type"
+    actual = DatasetReader.transform_data_type(input_data)
+    assert actual == expected
+
+
+def test_transform_datatype():
+    for expected, input_options in [
+        (Datatype.INTEGER, KNOWN_INTEGER_TYPES),
+        (Datatype.FLOAT, KNOWN_FLOAT_TYPES),
+        (Datatype.STRING, KNOWN_STRING_TYPES),
+        (Datatype.DATETIME, KNOWN_DATETIME_TYPES),
+        (Datatype.BOOLEAN, KNOWN_BOOLEAN_TYPES),
+    ]:
+        input_data = random.choice(input_options)
+        actual = DatasetReader.transform_data_type(input_data)
+        assert actual == expected
diff --git a/datadoc/tests/test_dataset_schema.py b/datadoc/tests/test_dataset_schema.py
deleted file mode 100644
index ecc96eae..00000000
--- a/datadoc/tests/test_dataset_schema.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import random
-
-from datadoc.Model import Datatype
-from ..DatasetSchema import (
-    KNOWN_BOOLEAN_TYPES,
-    KNOWN_DATETIME_TYPES,
-    KNOWN_FLOAT_TYPES,
-    KNOWN_INTEGER_TYPES,
-    KNOWN_STRING_TYPES,
-    DatasetSchema,
-)
-from .utils import TEST_PARQUET_FILEPATH, TEST_SAS7BDAT_FILEPATH
-from pytest import raises
-
-
-def test_get_fields_parquet():
-    expected_fields = [
-        {"shortName": "pers_id", "dataType": Datatype.STRING},
-        {"shortName": "tidspunkt", "dataType": Datatype.DATETIME},
-        {"shortName": "sivilstand", "dataType": Datatype.STRING},
-        {"shortName": "alm_inntekt", "dataType": Datatype.INTEGER},
-        {"shortName": "sykepenger", "dataType": Datatype.INTEGER},
-        {"shortName": "ber_bruttoformue", "dataType": Datatype.INTEGER},
-        {"shortName": "fullf_utdanning", "dataType": Datatype.STRING},
-        {"shortName": "hoveddiagnose", "dataType": Datatype.STRING},
-    ]
-
-    schema = DatasetSchema(TEST_PARQUET_FILEPATH)
-    fields = schema.get_fields()
-
-    assert fields == expected_fields
-
-
-def test_get_fields_sas7bdat():
-    expected_fields = [
-        {"shortName": "tekst", "name": "Tekst", "dataType": Datatype.STRING},
-        {"shortName": "tall", "name": "Tall", "dataType": Datatype.FLOAT},
-        {"shortName": "dato", "name": "Dato", "dataType": Datatype.DATETIME},
-    ]
-
-    schema = DatasetSchema(TEST_SAS7BDAT_FILEPATH)
-    fields = schema.get_fields()
-
-    assert fields == expected_fields
-
-
-def test_get_fields_csv():
-    with raises(NotImplementedError):
-        DatasetSchema("my_dataset.csv").get_fields()
-
-
-def test_get_fields_json():
-    with raises(NotImplementedError):
-        DatasetSchema("my_dataset.json").get_fields()
-
-
-def test_get_fields_xml():
-    with raises(NotImplementedError):
-        DatasetSchema("my_dataset.xml").get_fields()
-
-
-def test_transform_datatype_unknown_type():
-    expected = None
-    input_data = "definitely not a known data type"
-    actual = DatasetSchema.transform_data_type(input_data)
-    assert actual == expected
-
-
-def test_transform_datatype():
-    for expected, input_options in [
-        (Datatype.INTEGER, KNOWN_INTEGER_TYPES),
-        (Datatype.FLOAT, KNOWN_FLOAT_TYPES),
-        (Datatype.STRING, KNOWN_STRING_TYPES),
-        (Datatype.DATETIME, KNOWN_DATETIME_TYPES),
-        (Datatype.BOOLEAN, KNOWN_BOOLEAN_TYPES),
-    ]:
-        input_data = random.choice(input_options)
-        actual = DatasetSchema.transform_data_type(input_data)
-        assert actual == expected