Skip to content
Snippets Groups Projects
dash_plot_generator.py 27.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Max Väistö's avatar
    Max Väistö committed
    import os
    
    
    maxvaisto's avatar
    maxvaisto committed
    import numpy
    
    Max Väistö's avatar
    Max Väistö committed
    import pandas
    
    from dash import dash, dcc, html, Output, Input
    from collections import Counter
    from dash_plot_generation.utils import split_companies, extract_unique_companies, convert_owners_to_limits, \
    
    Max Väistö's avatar
    Max Väistö committed
        get_owner_means, round_to_three_largest_digits, replace_owner_number_with_symbol_real_numeric, \
        convert_to_numeric_str, label_with_rev, label_with_text
    
    from dash.exceptions import PreventUpdate
    
    import plotly.graph_objects as go
    import plotly.express as px
    
    SPACE_NORMAL_ENTRY = 35
    
    
    DEV_AVERAGE_RATING_LABEL = "dev_average_rating"
    
    RIGHT_SIDE_TEXT_DICT = {'display': 'inline-block',
    
    Max Väistö's avatar
    Max Väistö committed
                            'float': 'right', 'margin-right': '0%', 'margin-bottom': '0px'}
    
    DARK_STEAM = "rgb(23,29,37)"
    WHITE_STEAM = "rgb(235,235,235)"
    TITLE_WHITE_STEAM = "rgb(197,195,192)"
    DARK_BLUE_STEAM = "rgb(27,40,56)"
    TAB_COLOR = "rgb(31,46,65)"
    TAB_EDGE = "rgb(37,55,77)"
    DROPDOWN_COLOR = "rgb(50,70,101"
    
    SMALL_PANEL_COLOR = "rgb(22,32,45)"
    
    
    DEFAULT_TABS_DICT = {'width': 'auto', 'display': 'flex',
    
                         'background-color': TAB_COLOR, 'border-color': TAB_EDGE}
    
    TAB_HEADER_COLOR = "rgb(45,96,150)"
    DEVELOPER_DROPDOWN = "developer_dropdown"
    
    TAB_NORMAL_DICT = {'background-color': TAB_COLOR, 'color': TITLE_WHITE_STEAM,
                       'border': '0px solid',
    
                       'font-size': '15px',
    
                       'border_bottom': '2px solid ' + TAB_EDGE}
    
    TAB_HIGHLIGHT_DICT = {'backgroundColor': TAB_HEADER_COLOR, 'color': 'white', "border-color": "transparent",
                          'font-size': '15px'}
    PANEL_DEFAULT_DICT = {'display': 'inline-block',
    
                          'background-color': TAB_COLOR, 'border': '2px solid', 'border-color': TAB_EDGE,
    
    Max Väistö's avatar
    Max Väistö committed
                          'color': WHITE_STEAM, 'height': '600px'}
    SMALL_PANEL_DICT = {'float': 'left', 'background-color': 'transparent', 'box-sizing': 'border-box',
    
    SMALL_TAB_PANEL_DICT = SMALL_PANEL_DICT | {'width': '48%', 'height': '100%',
                                               'margin-bottom': '50px',
                                               'padding-top': '4%', 'padding-bottom': '5%', 'padding-left': '5%',
                                               'padding-right': '5%',
                                               'margin-top': '20px'
                                               }
    SMALL_PANEL_HEADER_DICT = {'text-align': 'center', 'padding-top': '5%', 'padding-bottom': '2%'}
    
    DEV_TOP_GENRES_LABEL = "dev_top_genres"
    
    
    Max Väistö's avatar
    Max Väistö committed
    LIST_DICT = {'display': 'inline-block', 'margin-bottom': '0px', 'padding-right': '0px'}
    
    DEV_CCU_LABEL = "dev_ccu"
    
    DEV_GAME_COUNT_LABEL = "dev_game_count"
    
    DEV_REV_PER_GAME_LABEL = "dev_rev_per_game"
    
    DEV_REVENUE_LABEL = "dev_revenue"
    
    
    DEV_TOP_GAMES = "pub_top_games"
    
    PUB_TOP_GENRES_LABEL = "pub_top_genres"
    
    PUB_CCU_LABEL = "pub_ccu"
    
    PUB_GAME_COUNT_LABEL = "pub_game_count"
    
    PUB_REV_PER_GAME_LABEL = "pub_rev_per_game"
    
    PUB_REVENUE_LABEL = "pub_revenue"
    
    PUB_TOP_GAMES = "pub_top_games"
    
    DEMO_PLOT_LABELS = ["Action", "Adventure", "RPG", "Puzzle", "Strategy", "Other"]
    DEMO_PLOT_COLORS = list(zip(DEMO_PLOT_LABELS, px.colors.qualitative.G10))
    
    Max Väistö's avatar
    Max Väistö committed
    csv_path = os.path.normpath(os.getcwd() + os.sep + os.pardir + os.sep + "api_exploration")
    split_csv_path = os.path.join(csv_path, "file_segments")
    
    # steam_dark_template = dict(layout=go.Layout(title_font))
    
    Max Väistö's avatar
    Max Väistö committed
    
    df = None
    DASH_ASSETS_PATH = "dash_assets"
    
    
    APP = dash.Dash(
    
    maxvaisto's avatar
    maxvaisto committed
        name=__name__,
        assets_folder=DASH_ASSETS_PATH,
    
        external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css'],
    
    Max Väistö's avatar
    Max Väistö committed
    def initialize_data():
        global df
        dataframe = None
        for file in os.listdir(split_csv_path):
            file_path = os.path.join(split_csv_path, file)
            dataframe = pandas.concat([dataframe, pandas.read_csv(file_path)]) if dataframe is not None \
    
                else pandas.read_csv(file_path)
    
    Max Väistö's avatar
    Max Väistö committed
        df = dataframe
    
    
    
    @APP.callback([Output(DEV_REVENUE_LABEL, "children"),
    
    maxvaisto's avatar
    maxvaisto committed
                   Output(DEV_TOP_GENRES_LABEL, "children"),
                   Output(DEV_CCU_LABEL, "children"),
                   Output(DEV_GAME_COUNT_LABEL, "children"),
                   Output(DEV_REV_PER_GAME_LABEL, "children"),
    
                   Output(DEV_TOP_GAMES, "children"),
                   Output(DEV_AVERAGE_RATING_LABEL, "children")],
    
                  inputs=[Input(DEVELOPER_DROPDOWN, "value")])
    def update_dev_info(dev_name):
        global df
    
    Max Väistö's avatar
    Max Väistö committed
        if not (dev_name and isinstance(df,pandas.DataFrame)):
    
    Max Väistö's avatar
    Max Väistö committed
    
        # Remove empty rows
    
    maxvaisto's avatar
    maxvaisto committed
        mask = df.developer.apply(lambda x: dev_name in x if isinstance(x, str) else False)
    
        dev_data = df[mask]
    
    Max Väistö's avatar
    Max Väistö committed
    
        # Engineer revenue data into the dataframe
        add_game_revenues_and_owner_means(dev_data)
    
        # Top games
        dev_top_games_label = get_top_revenue_game_labels(dev_data)
    
        # Dev total revenue
    
        dev_revenue = get_total_revenue_label(dev_data)
    
    Max Väistö's avatar
    Max Väistö committed
    
        # Dev revenue per game
        dev_game_revenue_per_game = get_average_game_rev_label(dev_data)
    
        # Top genres
        dev_top_genre_labels = get_top_genre_labels(dev_data)
    
        # CCU
        dev_ccu = get_ccu_label(dev_data)
    
        # Game count
        dev_game_count = get_game_count_label(dev_data)
    
        user_rating_value = get_average_user_rating_label(dev_data)
        return dev_revenue, dev_top_genre_labels, dev_ccu, dev_game_count, dev_game_revenue_per_game, dev_top_games_label, \
            user_rating_value
    
    
    def get_average_user_rating_label(dev_data):
        value_str = str(round(100 * dev_data["Review_rating"].mean())) + "%"
    
        label = label_with_text("Average game rating", value_str, SPACE_NORMAL_ENTRY, ".")
    
    Max Väistö's avatar
    Max Väistö committed
        return label
    
    
    def get_game_count_label(dev_data):
    
        return label_with_text("Number of games", str(dev_data.shape[0]), SPACE_NORMAL_ENTRY, ".")
    
    Max Väistö's avatar
    Max Väistö committed
    
    
    def add_game_revenues_and_owner_means(data):
        data["owner_means"] = data["owners"].apply(lambda x: get_owner_means(convert_owners_to_limits(x)))
        data["game_revenue"] = data.apply(lambda x: x["owner_means"] * x["price"] if
        not (pandas.isna(x["owner_means"]) or pandas.isna(x["price"]))
    
    maxvaisto's avatar
    maxvaisto committed
        else 0, axis=1)
    
    Max Väistö's avatar
    Max Väistö committed
    
    
    def get_top_revenue_game_labels(data):
        top_games = data.sort_values(by=["game_revenue"], ascending=False).head(3)
    
        top_games_processed = top_games.apply(lambda x: label_with_rev(x["name"], x["game_revenue"], SPACE_NORMAL_ENTRY,
    
    Max Väistö's avatar
    Max Väistö committed
                                                                       ".", "$"), axis=1)
        dev_top_games_with_dot = [" ".join(["", game]) for game in top_games_processed]
        dev_top_games_label = html.Div("\n".join(dev_top_games_with_dot),
                                       style={'white-space': 'pre-line', 'padding-left': '5%'})
        return dev_top_games_label
    
    
    
    def get_total_revenue_label(data):
        top_games_processed = label_with_rev("• Total", numpy.nansum(data["game_revenue"]), SPACE_NORMAL_ENTRY, ".", "$")
        return top_games_processed
    
    Max Väistö's avatar
    Max Väistö committed
    
    
    def get_top_genre_labels(data):
        genre_totals = [genre for genre_list in data["genres"] if isinstance(genre_list, str)
    
    maxvaisto's avatar
    maxvaisto committed
                        for genre in genre_list.split(", ")]
        genre_counts = Counter(genre_totals).most_common(3)
    
    Max Väistö's avatar
    Max Väistö committed
        top_genres_rows = [label_with_text(genre[0], str(genre[1]), 50, ".") for genre in genre_counts]
        top_genres_with_dot = [" ".join(["", row]) for row in top_genres_rows]
        top_genre_labels = html.Div("\n".join(top_genres_with_dot),
    
                                        style={'white-space': 'pre-line', 'padding-left': '5%'})
    
    Max Väistö's avatar
    Max Väistö committed
        return top_genre_labels
    
    
    def get_ccu_label(data):
        ccu = sum(data["ccu"])
        dev_ccu = convert_to_numeric_str(ccu)
    
    
        return label_with_text("Concurrent users", dev_ccu, SPACE_NORMAL_ENTRY, ".")
    
    Max Väistö's avatar
    Max Väistö committed
    
    
    def get_average_game_rev_label(data):
        game_revenue_per_game_raw = numpy.nansum(data["game_revenue"]) / len(data["game_revenue"])
    
        dev_game_revenue_per_game_row = label_with_rev("Average", game_revenue_per_game_raw, SPACE_NORMAL_ENTRY, ".", "$")
    
    Max Väistö's avatar
    Max Väistö committed
        dev_game_revenue_per_game = " ".join(["", dev_game_revenue_per_game_row])
        return dev_game_revenue_per_game
    
    Max Väistö's avatar
    Max Väistö committed
    def initialize_dash(host: str = "0.0.0.0", **kwargs):
        """
        Runs the Dash server.
        Args:
            host: IP address of the server
            kwargs: Variables which are passed down to APP.run function as named arguments.
        Note:
            The server IP is not actually 0.0.0.0; if the dash app is not accessible via this address, use the same port
            number but replace the IP address with your local network IP instead.
        """
    
    
        global APP, df, DEMO_PLOT_COLORS, DEMO_PLOT_LABELS
    
    Max Väistö's avatar
    Max Väistö committed
    
    
        unique_publishers = extract_unique_companies(df["publisher"].apply(lambda x: split_companies(x)))
        unique_developers = extract_unique_companies(df["developer"].iloc[0:10].apply(lambda x: split_companies(x)))
        # unique_publishers = ["Valve"]
        # unique_developers = ["Valve"]
    
        APP.css.append_css({
            'external_url': 'styles.css'
        })
    
    maxvaisto's avatar
    maxvaisto committed
        APP.layout = html.Div(
            children=[
                html.Nav(className="nav nav-pills", children=[
    
    maxvaisto's avatar
    maxvaisto committed
                    html.H6("SteamSavvy - Steam game data insights",
    
                            style={"margin-left": "60px", "width": "20%", "display": "inline-block"}),
    
    maxvaisto's avatar
    maxvaisto committed
                    html.A('About', className="nav-item nav-link btn", href='/apps/App1',
                           style={"margin-left": "300px"}),
                    html.A('Technical report', className="nav-item nav-link active btn", href='/apps/App2',
                           style={"margin-left": "150px"})
                ],
    
                         style={"background-color": "rgb(23,29,37)", "color": "rgb(197,195,192)", 'width': '100%'}),
    
    maxvaisto's avatar
    maxvaisto committed
                html.Div(className="row", children=[
                    html.Div(children=[
    
                        dcc.Tabs(id="main_plots", value="tab1", children=[
    
                            dcc.Tab(label="Genre performance", value="tab1",
    
                                    style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT,
                                    children=[
                                        html.Div(children=[
    
    Max Väistö's avatar
    Max Väistö committed
                                            html.Div(
                                                children=[
                                                    html.Div(style=SMALL_PANEL_DICT | {'width': '60%', 'height': '100%',
                                                                                       'margin-right': '5%',
                                                                                       'padding-left': '5%',
                                                                                       'margin-bottom': '5%',
                                                                                       'background-color': TAB_COLOR},
                                                             children=[dcc.Graph(
                                                                 figure=go.Figure(data=[
                                                                     {'x': ["Action", "Adventure", "RPG", "Puzzle",
                                                                            "Strategy"],
                                                                      'y': [0.7, 0.4, 0.8, 1.2, 1.3],
                                                                      'type': 'bar'},
                                                                 ],
                                                                     layout=dict(template="plotly_dark",
                                                                                 title="Relative genre perfomance",
                                                                                 plot_bgcolor=TAB_COLOR,
                                                                                 paper_bgcolor=TAB_COLOR)),
    
                                                             ),
                                                                 html.P(f"""Genre performance measures the assessed exploitability of the 
    
                                                specific game genre. The assesment is done by estimating the genre popularity,
                                                and games developed in the next two years and showing the relative differences
                                                between the genres.""")]),
    
    Max Väistö's avatar
    Max Väistö committed
                                                    html.Div(style=SMALL_PANEL_DICT | {'width': '35%', 'height': '100%',
                                                                                       'background-color': TAB_COLOR},
                                                             children=[
                                                                 html.Div(children=[
                                                                     html.Div(style={'width': '100%', 'height': '50%'},
                                                                              children=[dcc.Graph(
                                                                                  figure=go.Figure(data=[go.Pie(
                                                                                      labels=["Action", "Adventure", "RPG",
                                                                                              "Puzzle",
                                                                                              "Strategy",
                                                                                              "Other"],
                                                                                      values=[0.8, 0.3, 0.4, 0.4, 0.3,
                                                                                              0.55],
                                                                                      sort=False)],
                                                                                      layout=dict(template="plotly_dark",
                                                                                                  title="Genre popularity",
                                                                                                  plot_bgcolor=TAB_COLOR,
                                                                                                  paper_bgcolor=TAB_COLOR,
                                                                                                  margin=dict(l=20, r=20,
                                                                                                              t=50, b=20))),
                                                                                  style={'width': '100%',
                                                                                         'height': '100%'})]),
                                                                     html.Div(style={'width': '100%', 'height': '50%'},
                                                                              children=[dcc.Graph(
                                                                                  figure=go.Figure(data=[go.Pie(
                                                                                      labels=["Action", "Adventure", "RPG",
                                                                                              "Puzzle",
                                                                                              "Strategy",
                                                                                              "Other"],
                                                                                      values=[0.7, 0.5, 0.1, 0.4, 0.3, 0.7],
                                                                                      sort=False)],
                                                                                      layout=dict(template="plotly_dark",
                                                                                                  title="Genre revenue share",
                                                                                                  plot_bgcolor=TAB_COLOR,
                                                                                                  paper_bgcolor=TAB_COLOR,
                                                                                                  margin=dict(l=20, r=20,
                                                                                                              t=50, b=20))),
                                                                                  style={'width': '100%',
                                                                                         'height': '100%'})]
                                                                              )
    
                                                                 ], style={'height': '540px'}
                                                                 ),
                                                             ]
                                                             )
                                                ]
                                            ),
    
                                            html.Div(children=[
                                                html.H5("Genre prediction", style={'margin-bottom':'50px'}),
                                                html.Div(children=[
                                                    html.Div(children=[
                                                        html.P("Selected genre:", style={'margin-bottom':'10px'}),
                                                        dcc.Dropdown(id="genre_dropdown", value="action",
                                                                     options=[{"label": html.Span([genre],
                                                                                                  style={
                                                                                                      'color': WHITE_STEAM}),
                                                                               "value": genre} for genre in
                                                                              ["action"]],
                                                                     style={'color': WHITE_STEAM, 'display': 'inline-block',
                                                                            'width': '50%'},
                                                                     className='dash-dropdown',
                                                                     ),
    
                                                    ],
                                                        style={'width': '100%', 'margin-bottom':'50px'})
                                                ]),
                                                dcc.Graph(figure=go.Figure(layout=dict(template="plotly_dark",
                                                                                       title="Genre prediction plot",
                                                                                       plot_bgcolor=TAB_COLOR,
                                                                                       paper_bgcolor=TAB_COLOR,
                                                                                       margin=dict(l=20, r=20,
                                                                                                   t=50, b=20)))),
                                                html.P("""This is an individual regression estimate for the genre that represents
                                            the estimated amount of games to be produced in the next two years""")
    
                                            ], style=SMALL_PANEL_DICT | {'height': '100%', 'margin-right': '5%',
                                                                         'padding-left': '5%', 'margin-bottom': '5%',
                                                                         'margin-top': '5%', 'background-color': TAB_COLOR})
    
                                        ],
                                            style={'height': '550px', 'width': '100%', 'margin': '0', 'overflow': 'auto'},
                                            className="scrollable")
    
                            dcc.Tab(label="Game popularity", value="tab2",
    
                                    style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
    
                            dcc.Tab(label="Market performance", value="tab4",
    
                                    style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
    
    Max Väistö's avatar
    Max Väistö committed
                            dcc.Tab(label="Market prediction tool", value="tab5",
                                    style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
    
    maxvaisto's avatar
    maxvaisto committed
                        ],
    
                                 style=DEFAULT_TABS_DICT),
    
                    ], style=PANEL_DEFAULT_DICT | {'width': '900px',
                                                   'margin-right': '100px', 'padding-left': '50px',
                                                   'padding-right': '50px', 'padding-bottom': '50px',
                                                   'padding-top': '50px', 'margin-bottom': '50px'
    
    maxvaisto's avatar
    maxvaisto committed
                    html.Div(children=[
    
                        dcc.Tabs(id="company_information", value="tab3", children=[
    
    maxvaisto's avatar
    maxvaisto committed
                            dcc.Tab(label="Developer infromation", value="tab3", children=[
    
                                html.Div(children=[
    
    maxvaisto's avatar
    maxvaisto committed
                                    dcc.Dropdown(id=DEVELOPER_DROPDOWN, value="Valve",
    
                                                 options=[{"label": html.Span([developer], style={'color': WHITE_STEAM}),
                                                           "value": developer} for developer in unique_developers],
                                                 style={'margin-top': '20px', 'color': WHITE_STEAM},
    
                                                 className='dash-dropdown',
    
    maxvaisto's avatar
    maxvaisto committed
                                                 ),
    
                                    html.Div(children=[
    
                                            children=[
                                                html.Div(
                                                    children=[html.P("Revenue", style=SMALL_PANEL_HEADER_DICT)],
                                                    style={'margin-bottom': '10%',
                                                           'border-bottom': '2px solid ' + TAB_EDGE}),
                                                html.Div(children=[
                                                    html.Div(children=[
                                                        html.P("Game sale revenue estimates"),
                                                        html.Div(children=[
                                                            html.Div(children=[
                                                                html.P(id=DEV_REVENUE_LABEL, children="$524 M",
    
                                                                       style=LIST_DICT | {'padding-left': '5%'})
    
                                                            ]),
                                                            html.Div(children=[
                                                                html.P(id=DEV_REV_PER_GAME_LABEL, children="$925 M",
    
    Max Väistö's avatar
    Max Väistö committed
                                                                       style=LIST_DICT | {'padding-left': '5%'})
                                                            ]),
    
                                                            style={'margin-bottom': '20px'}),
    
                                                        html.Div(children=[
                                                            html.P("Top games by revenue:"),
                                                            html.Small(id=DEV_TOP_GAMES, children="Half life 2"),
                                                        ])
                                                    ]),
                                                ], style={'padding-left': '5%', 'padding-right': '5%',
                                                          'padding-bottom': '5%'})
                                            ],
    
                                            style=SMALL_TAB_PANEL_DICT | {'margin-right': '20px', 'margin-left': '0px'}
    
    maxvaisto's avatar
    maxvaisto committed
                                        ),
    
                                        html.Div(children=[
    
                                            html.Div(
                                                children=[
                                                    html.Div(
                                                        children=[html.P("General information",
                                                                         style=SMALL_PANEL_HEADER_DICT)],
                                                        style={'margin-bottom': '30px',
                                                               'border-bottom': '2px solid ' + TAB_EDGE}),
                                                    html.Div(children=[
                                                        html.Div(children=[
                                                            html.P(id=DEV_GAME_COUNT_LABEL, children="5",
    
    Max Väistö's avatar
    Max Väistö committed
                                                                   style=LIST_DICT),
    
                                                        ],
                                                            style={'margin-bottom': '10px'}
                                                        ),
                                                        html.Div(children=[
    
    Max Väistö's avatar
    Max Väistö committed
                                                            html.P(id=DEV_CCU_LABEL, children="",
                                                                   style=LIST_DICT),
    
                                                        ],
                                                            style={'margin-bottom': '10px'}
                                                        ),
                                                        html.Div(children=[
    
                                                            html.P("Popular game genres:"),
    
                                                            html.Small(id=DEV_TOP_GENRES_LABEL,
                                                                       children="FPS, Action, Puzzle"),
                                                        ],
                                                            style={'margin-bottom': '10px'}
                                                        ),
                                                        html.Div(children=[
                                                            html.P(id=DEV_AVERAGE_RATING_LABEL,
    
    Max Väistö's avatar
    Max Väistö committed
                                                                   children="",
                                                                   style=LIST_DICT)
    
                                        ], style=SMALL_TAB_PANEL_DICT | {'width': '45%', 'height': '100%',
                                                                         'margin-right': '0px', 'margin-left': '20px'}
    
    maxvaisto's avatar
    maxvaisto committed
                                        )
    
                                    ], style={'height': '100%'})
    
                                ], style={'margin-left': '20px', 'margin-right': '0px'}
    
    maxvaisto's avatar
    maxvaisto committed
                                )
    
                            ], style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
    
    maxvaisto's avatar
    maxvaisto committed
                            dcc.Tab(label="Publisher information", value="tab4", children=[
    
                                dcc.Dropdown(id="publisher_dropdown", value="Valve",
                                             options=[{"label": publisher, "value": publisher} for publisher in
    
                                                      unique_publishers],
    
    maxvaisto's avatar
    maxvaisto committed
                                             ),
    
                            ], style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT)
    
    maxvaisto's avatar
    maxvaisto committed
                        ],
    
                                 style=DEFAULT_TABS_DICT),
    
                        style=PANEL_DEFAULT_DICT | {'width':'700px',
                                                    'padding-left': '50px',
                                                    'padding-right': '50px', 'padding-bottom': '50px',
                                                    'padding-top': '50px', 'margin-bottom': '50px'
    
    Max Väistö's avatar
    Max Väistö committed
                ],
    
                         style={'width': '100%', "padding-top": "30px", 'padding-left': "50px"}),
    
            ],
            style={"font-family": "Tahoma"}
    
    Max Väistö's avatar
    Max Väistö committed
        APP.run(host=host, **kwargs)
        print("The server has closed!")
    
    
    if __name__ == "__main__":
        initialize_data()
    
        initialize_dash(debug=True)
    
    Max Väistö's avatar
    Max Väistö committed
    
    print(csv_path)