From 3eec6069d2f0f2badaba94ef60a48c6dd4199afe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Max=20V=C3=A4ist=C3=B6?= <maxvaist@ad.helsinki.fi>
Date: Mon, 16 Oct 2023 12:57:36 +0300
Subject: [PATCH] Made the project multi-page

---
 dash_plot_generation/app.py                   | 110 ++++
 .../background_image.jpg                      | Bin
 .../{dash_assets => assets}/broken car.jpg    | Bin
 .../{dash_assets => assets}/dark city.jpg     | Bin
 .../dark_computer_room.jpg                    | Bin
 .../{dash_assets => assets}/radioactive.jpg   | Bin
 .../{dash_assets => assets}/styles.css        |  38 +-
 dash_plot_generation/dash_plot_generator.py   | 619 ------------------
 dash_plot_generation/data_store.py            |  38 ++
 .../pages/__pycache__/about.cpython-311.pyc   | Bin 0 -> 1117 bytes
 .../dash_plot_generator.cpython-311.pyc       | Bin 0 -> 14146 bytes
 .../pages/__pycache__/home.cpython-311.pyc    | Bin 0 -> 643 bytes
 dash_plot_generation/pages/about.py           |  21 +
 .../pages/dash_plot_generator.py              | 314 +++++++++
 dash_plot_generation/pages/home.py            |  11 +
 .../pages/technical_report.py                 |   0
 dash_plot_generation/styles_and_handles.py    |  72 ++
 dash_plot_generation/utils.py                 |  82 +++
 18 files changed, 675 insertions(+), 630 deletions(-)
 create mode 100644 dash_plot_generation/app.py
 rename dash_plot_generation/{dash_assets => assets}/background_image.jpg (100%)
 rename dash_plot_generation/{dash_assets => assets}/broken car.jpg (100%)
 rename dash_plot_generation/{dash_assets => assets}/dark city.jpg (100%)
 rename dash_plot_generation/{dash_assets => assets}/dark_computer_room.jpg (100%)
 rename dash_plot_generation/{dash_assets => assets}/radioactive.jpg (100%)
 rename dash_plot_generation/{dash_assets => assets}/styles.css (76%)
 delete mode 100644 dash_plot_generation/dash_plot_generator.py
 create mode 100644 dash_plot_generation/data_store.py
 create mode 100644 dash_plot_generation/pages/__pycache__/about.cpython-311.pyc
 create mode 100644 dash_plot_generation/pages/__pycache__/dash_plot_generator.cpython-311.pyc
 create mode 100644 dash_plot_generation/pages/__pycache__/home.cpython-311.pyc
 create mode 100644 dash_plot_generation/pages/about.py
 create mode 100644 dash_plot_generation/pages/dash_plot_generator.py
 create mode 100644 dash_plot_generation/pages/home.py
 create mode 100644 dash_plot_generation/pages/technical_report.py
 create mode 100644 dash_plot_generation/styles_and_handles.py

diff --git a/dash_plot_generation/app.py b/dash_plot_generation/app.py
new file mode 100644
index 0000000..904843d
--- /dev/null
+++ b/dash_plot_generation/app.py
@@ -0,0 +1,110 @@
+import dash
+import pandas
+from dash import html, dash, Output, Input
+from dash.exceptions import PreventUpdate
+
+from dash_plot_generation.data_store import initialize_data, OWNER_RANGE_PARTS_SORTED, FULL_DATA
+from dash_plot_generation.utils import get_average_user_rating_label, get_game_count_label, get_top_revenue_game_labels, \
+    get_total_revenue_label, get_top_genre_labels, get_ccu_label, get_average_game_rev_label
+from dash_plot_generation.styles_and_handles import RATING_MIN_REVIEWS, RATING_SLIDER, RATING_TABLE, \
+    RATING_DISTRIBUTION_PLOT, DEV_AVERAGE_RATING_LABEL, DENSITY_LAYOUT_STYLE, WHITE_STEAM, TAB_COLOR, TAB_EDGE, \
+    TAB_HEADER_COLOR, DEVELOPER_DROPDOWN, DEV_TOP_GENRES_LABEL, DEV_CCU_LABEL, DEV_GAME_COUNT_LABEL, \
+    DEV_REV_PER_GAME_LABEL, DEV_REVENUE_LABEL, DEV_TOP_GAMES
+from visual_presentation.Distribution_of_review_rating import get_rating_density_plot
+
+APP = dash.Dash(
+    name=__name__,
+    use_pages=True,
+    external_stylesheets=['/assets/styles.css',
+                          'https://codepen.io/chriddyp/pen/bWLwgP.css']
+)
+
+# APP.css.append_css({
+#    'external_url': 'styles.css'
+# })
+
+APP.layout = html.Div([
+    html.Nav(className="navbar", children=[
+        html.H6("SteamSavvy - Steam game data insights",
+                style={"margin-left": "60px", "display": "inline-block"},
+                className="nav-item-1"),
+        html.A('About', className="nav-item nav-link btn", href='/about',
+               style={"margin-left": "150px"},),
+        html.A('Dashboard', className="nav-item nav-link btn", href='/dashboard',
+               style={"margin-left": "150px"}),
+        html.A('Technical report', className="nav-item nav-link active btn", href='/Documentation/Report',
+               style={"margin-left": "150px"})
+    ]),
+
+    dash.page_container
+], className="body")
+
+
+@APP.callback([Output(DEV_REVENUE_LABEL, "children"),
+               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 FULL_DATA, OWNER_RANGE_PARTS_SORTED
+    if not (dev_name and isinstance(FULL_DATA, pandas.DataFrame)):
+        raise PreventUpdate
+
+    # Remove empty rows
+    mask = FULL_DATA.developer.apply(lambda x: dev_name in x if isinstance(x, str) else False)
+    dev_data = FULL_DATA[mask]
+
+    # 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)
+
+    # 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
+
+
+@APP.callback(Output(RATING_DISTRIBUTION_PLOT, "figure"),
+              Output("Game pop bottom_region", 'children'),
+              Input(RATING_SLIDER, "value"),
+              Input(RATING_MIN_REVIEWS, "value"))
+def update_density_filter_plot(rating_range, min_reviews):
+    allowed_indexes = [str_val for (val, str_val) in OWNER_RANGE_PARTS_SORTED[rating_range[0]:rating_range[1] + 1]]
+    allowed_ratings = [" .. ".join([val, allowed_indexes[i + 1]]) for (i, val) in enumerate(allowed_indexes)
+                       if i < len(allowed_indexes) - 1]
+
+    output = get_rating_density_plot(FULL_DATA, allowed_ratings, min_reviews, layout=DENSITY_LAYOUT_STYLE)
+    table = html.Div(dash.dash_table.DataTable(output['top_games']['non_free'].to_dict('records'),
+                                               id=RATING_TABLE,
+
+                                               style_data={'backgroundColor': TAB_COLOR,
+                                                           'color': WHITE_STEAM,
+                                                           'border': '1px solid ' + TAB_EDGE},
+                                               style_header={'backgroundColor': TAB_HEADER_COLOR,
+                                                             'color': WHITE_STEAM,
+                                                             'border': '1px solid ' + TAB_EDGE}))
+    return output['fig'], [table]
+
+
+if __name__ == "__main__":
+    initialize_data()
+    APP.run_server(debug=True, host="0.0.0.0")
diff --git a/dash_plot_generation/dash_assets/background_image.jpg b/dash_plot_generation/assets/background_image.jpg
similarity index 100%
rename from dash_plot_generation/dash_assets/background_image.jpg
rename to dash_plot_generation/assets/background_image.jpg
diff --git a/dash_plot_generation/dash_assets/broken car.jpg b/dash_plot_generation/assets/broken car.jpg
similarity index 100%
rename from dash_plot_generation/dash_assets/broken car.jpg
rename to dash_plot_generation/assets/broken car.jpg
diff --git a/dash_plot_generation/dash_assets/dark city.jpg b/dash_plot_generation/assets/dark city.jpg
similarity index 100%
rename from dash_plot_generation/dash_assets/dark city.jpg
rename to dash_plot_generation/assets/dark city.jpg
diff --git a/dash_plot_generation/dash_assets/dark_computer_room.jpg b/dash_plot_generation/assets/dark_computer_room.jpg
similarity index 100%
rename from dash_plot_generation/dash_assets/dark_computer_room.jpg
rename to dash_plot_generation/assets/dark_computer_room.jpg
diff --git a/dash_plot_generation/dash_assets/radioactive.jpg b/dash_plot_generation/assets/radioactive.jpg
similarity index 100%
rename from dash_plot_generation/dash_assets/radioactive.jpg
rename to dash_plot_generation/assets/radioactive.jpg
diff --git a/dash_plot_generation/dash_assets/styles.css b/dash_plot_generation/assets/styles.css
similarity index 76%
rename from dash_plot_generation/dash_assets/styles.css
rename to dash_plot_generation/assets/styles.css
index 17e9629..6ace393 100644
--- a/dash_plot_generation/dash_assets/styles.css
+++ b/dash_plot_generation/assets/styles.css
@@ -1,8 +1,30 @@
 
-html, body {
-  height: 100%;
-  margin: 0;
-  background-color: rgb(27,40,56);
+body {
+    margin: 0;
+    padding: 0;
+    object-fit: fill;
+    height: 100%;
+    width: 100%;
+    background-color: rgb(27,40,56);
+    color: rgb(235,235,235);
+
+}
+
+.navbar {
+    background-color: rgb(23,29,37);
+    color: rgb(197,195,192);
+    width: 100%;
+    margin:0;
+}
+
+.nav-item-1 {
+   color: rgb(197,195,192);
+    font-size: 30px;
+}
+
+.nav-link {
+    color: rgb(197,195,192);
+    font-size: 20px;
 }
 #app-container {
   height: 100%;
@@ -43,9 +65,6 @@ html, body {
     border-bottom: 2px solid rgb(37,55,77);
 }
 
-.dark_plot{
-    background-color: black;
-}
 
 /* Track rgb(197,195,192) */
 div.scrollable::-webkit-scrollbar {
@@ -55,10 +74,7 @@ div.scrollable::-webkit-scrollbar {
 }
 
 div.scrollable::-webkit-scrollbar-track {
-    margin-top: 20px;
-    margin-bottom: 20px;
-    margin-left: 20px;
-    margin-right: 20px;
+    margin: 20px;
 }
 
 div.scrollable::-webkit-scrollbar-thumb {
diff --git a/dash_plot_generation/dash_plot_generator.py b/dash_plot_generation/dash_plot_generator.py
deleted file mode 100644
index 419fba3..0000000
--- a/dash_plot_generation/dash_plot_generator.py
+++ /dev/null
@@ -1,619 +0,0 @@
-
-import os
-
-import numpy
-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, \
-    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
-
-from visual_presentation.Distribution_of_review_rating import get_rating_density_plot
-
-RATING_MIN_REVIEWS = "min_reviews_id"
-
-RATING_SLIDER = "rating_slider"
-
-RATING_TABLE = "rating_data_table"
-
-RATING_DISTRIBUTION_PLOT = "game_popularity_density_plot"
-
-MAIN_PANEL_TAB_DICT = {'height': '550px', 'width': '100%', 'margin': '0', 'overflow': 'auto'}
-
-SPACE_NORMAL_ENTRY = 35
-
-DEV_AVERAGE_RATING_LABEL = "dev_average_rating"
-
-RIGHT_SIDE_TEXT_DICT = {'display': 'inline-block',
-                        'float': 'right', 'margin-right': '0%', 'margin-bottom': '0px'}
-
-DEFAULT_PLOT_STYLE_DICT = dict(
-    template="plotly_dark",
-    plot_bgcolor="rgb(31,46,65)",
-    paper_bgcolor="rgb(31,46,65)",
-)
-
-DENSITY_LAYOUT_STYLE = DEFAULT_PLOT_STYLE_DICT | dict(
-    title='Distribution of Game Review Rating',
-    xaxis_title="Game user rating",
-    yaxis_title="Proportion"
-
-)
-
-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)"
-
-layout = dict(template="plotly_dark", plot_bgcolor="rgb(31,46,65)", paper_bgcolor="rgb(31,46,65)")
-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,
-                      'color': WHITE_STEAM, 'height': '600px'}
-SMALL_PANEL_DICT = {'float': 'left', 'background-color': 'transparent', 'box-sizing': 'border-box',
-                    'padding': '10px'}
-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"
-
-LIST_DICT = {'display': 'inline-block', 'margin-bottom': '0px', 'padding-right': '0px'}
-
-NORMAL_DIVISION_DICT = SMALL_PANEL_DICT | {'width': '60%', 'height': '100%', 'margin-right': '5%', 'padding-left': '5%',
-                                           'margin-bottom': '5%', 'background-color': TAB_COLOR}
-
-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))
-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))
-
-FULL_DATA = None
-OWNER_RANGE_PARTS_SORTED = None
-
-DASH_ASSETS_PATH = "dash_assets"
-
-APP = dash.Dash(
-    name=__name__,
-    assets_folder=DASH_ASSETS_PATH,
-    external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css'],
-)
-
-
-def initialize_data():
-    global FULL_DATA, OWNER_RANGE_PARTS_SORTED
-    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)
-
-    add_game_revenues_and_owner_means(dataframe)
-    owner_ranges = {value_range for value_range in dataframe["owners"].unique()}
-
-    ranges_test = [(convert_owners_to_limits(value_range), value_range.split(" .. ")) for value_range in owner_ranges]
-    unique_owner_values = {(limits[i], limits_str[i]) for (limits, limits_str)
-                           in ranges_test for i in range(2)}
-    sorted_owner_list = sorted(unique_owner_values, key=lambda range: range[0])
-
-    OWNER_RANGE_PARTS_SORTED = sorted_owner_list
-    FULL_DATA = dataframe
-
-
-@APP.callback(Output(RATING_DISTRIBUTION_PLOT, "figure"),
-              Output("Game pop bottom_region", 'children'),
-              Input(RATING_SLIDER, "value"),
-              Input(RATING_MIN_REVIEWS, "value"))
-def update_density_filter_plot(rating_range, min_reviews):
-    global FULL_DATA, OWNER_RANGE_PARTS_SORTED
-
-    allowed_indexes = [str_val for (val, str_val) in OWNER_RANGE_PARTS_SORTED[rating_range[0]:rating_range[1] + 1]]
-    allowed_ratings = [" .. ".join([val, allowed_indexes[i + 1]]) for (i, val) in enumerate(allowed_indexes)
-                       if i < len(allowed_indexes) - 1]
-
-    output = get_rating_density_plot(FULL_DATA, allowed_ratings, min_reviews, layout=DENSITY_LAYOUT_STYLE)
-    table = html.Div(dash.dash_table.DataTable(output['top_games']['non_free'].to_dict('records'),
-                                               id=RATING_TABLE,
-
-                                               style_data={'backgroundColor': TAB_COLOR,
-                                                           'color': WHITE_STEAM,
-                                                           'border': '1px solid ' + TAB_EDGE},
-                                               style_header={'backgroundColor': TAB_HEADER_COLOR,
-                                                             'color': WHITE_STEAM,
-                                                             'border': '1px solid ' + TAB_EDGE}))
-    return output['fig'], [table]
-
-
-@APP.callback([Output(DEV_REVENUE_LABEL, "children"),
-               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 FULL_DATA, OWNER_RANGE_PARTS_SORTED
-    if not (dev_name and isinstance(FULL_DATA, pandas.DataFrame)):
-        raise PreventUpdate
-
-    # Remove empty rows
-    mask = FULL_DATA.developer.apply(lambda x: dev_name in x if isinstance(x, str) else False)
-    dev_data = FULL_DATA[mask]
-
-    # 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)
-
-    # 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, ".")
-    return label
-
-
-def get_game_count_label(dev_data):
-    return label_with_text("Number of games", str(dev_data.shape[0]), SPACE_NORMAL_ENTRY, ".")
-
-
-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"]))
-    else 0, axis=1)
-
-
-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,
-                                                                   ".", "$"), 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
-
-
-def get_top_genre_labels(data):
-    genre_totals = [genre for genre_list in data["genres"] if isinstance(genre_list, str)
-                    for genre in genre_list.split(", ")]
-    genre_counts = Counter(genre_totals).most_common(3)
-    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%'})
-    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, ".")
-
-
-def get_genre_popularity_counts(df, group_after_largest=8):
-    genre_df = df[["genres", "owner_means", "game_revenue"]]
-    genre_owners = {}
-    genre_revenue = {}
-
-    for index, row in genre_df.iterrows():
-        if not isinstance(row.genres, str):
-            continue
-        genre_list = row.genres.split(", ")
-        for genre in genre_list:
-            if genre in genre_owners.keys():
-                genre_owners[genre] += row["owner_means"]
-                genre_revenue[genre] += row["game_revenue"]
-            else:
-                genre_owners[genre] = row["owner_means"]
-                genre_revenue[genre] = row["game_revenue"]
-    top_owners = dict(Counter(genre_owners).most_common(group_after_largest))
-    top_revenue = dict(Counter(genre_revenue).most_common(group_after_largest))
-    top_owners["Other"] = sum([val for (key, val) in genre_owners.items()
-                               if key not in top_owners.keys()])
-    top_revenue["Other"] = sum([val for (key, val) in genre_revenue.items()
-                                if key not in top_revenue.keys()])
-
-    return top_owners, top_revenue
-
-
-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, ".", "$")
-    dev_game_revenue_per_game = " ".join(["•", dev_game_revenue_per_game_row])
-    return dev_game_revenue_per_game
-
-
-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, FULL_DATA, DEMO_PLOT_COLORS, DEMO_PLOT_LABELS, OWNER_RANGE_PARTS_SORTED
-
-    # 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"]
-
-    # Genre performance table_values
-    # genre_owners, genre_revenue = get_genre_popularity_counts(FULL_DATA, 6)
-    genre_owners = {key: val for (key, val) in
-                    zip(["Action", "Adventure", "RPG", "Puzzle", "Strategy", "Other"],
-                        [0.7, 0.5, 0.1, 0.4, 0.3, 0.7])}
-    genre_revenue = {key: val for (key, val) in
-                     zip(["Action", "Adventure", "RPG", "Puzzle", "Strategy", "Other"],
-                         [0.5, 0.4, 0.3, 0.4, 0.6, 0.7])}
-
-    # Game popularity filter values
-    max_reviews = numpy.nanmax(FULL_DATA.apply(lambda x: x["positive"] + x["negative"], axis=1))
-    owner_range_dict = {index: val_str for (index, (val, val_str)) in enumerate(OWNER_RANGE_PARTS_SORTED)}
-    min_owner = min(owner_range_dict.keys())
-    max_owner = max(owner_range_dict.keys())
-
-    APP.css.append_css({
-        'external_url': 'styles.css'
-    })
-    APP.layout = html.Div(
-        children=[
-            html.Nav(className="nav nav-pills", children=[
-                html.H6("SteamSavvy - Steam game data insights",
-                        style={"margin-left": "60px", "width": "20%", "display": "inline-block"}),
-                html.A('About', className="nav-item nav-link btn", href='/Documentation/About',
-                       style={"margin-left": "300px"}),
-                html.A('Technical report', className="nav-item nav-link active btn", href='/Documentation/Report',
-                       style={"margin-left": "150px"})
-            ],
-                     style={"background-color": "rgb(23,29,37)", "color": "rgb(197,195,192)", 'width': '100%'}),
-            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=[
-                                        html.Div(
-                                            children=[
-                                                html.Div(style=NORMAL_DIVISION_DICT,
-                                                         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=DEFAULT_PLOT_STYLE_DICT |
-                                                                        dict(title="Relative genre perfomance")
-                                                             ),
-
-                                                         ),
-                                                             html.P(f"""Genre performance measures the assessed 
-                                                             exploitability of the specific game genre. The assessment 
-                                                             is done by estimating the genre popularity, and games 
-                                                             developed in the next two years and showing the relative 
-                                                             differences between the genres.""")]),
-                                                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=list(genre_owners.keys()),
-                                                                                  values=list(genre_owners.values()),
-                                                                                  sort=False)],
-                                                                                  layout=DEFAULT_PLOT_STYLE_DICT |
-                                                                                         dict(title="Genre popularity",
-                                                                                              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=list(genre_revenue.keys()),
-                                                                                  values=list(genre_revenue.values()),
-                                                                                  sort=False)],
-                                                                                  layout=DEFAULT_PLOT_STYLE_DICT |
-                                                                                         dict(
-                                                                                             title="Genre revenue share",
-                                                                                             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=DEFAULT_PLOT_STYLE_DICT |
-                                                                              dict(title="Genre prediction plot",
-                                                                                   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=NORMAL_DIVISION_DICT | {'width': '90%'})
-
-                                    ],
-                                        style=MAIN_PANEL_TAB_DICT,
-                                        className="scrollable")
-                                ]),
-                        dcc.Tab(label="Game popularity", value="tab2",
-                                style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT,
-                                children=[
-                                    html.Div(id="Game pop general layout",
-                                             style=MAIN_PANEL_TAB_DICT,
-                                             className="scrollable",
-                                             children=[
-                                                 html.Div(id="Game pop top div",
-                                                          children=[
-                                                              html.Div(id="game popularity filters",
-                                                                       style=NORMAL_DIVISION_DICT | {'width': '100%',
-                                                                                                     'height': '100%',
-                                                                                                     'margin_left': '0px',
-                                                                                                     'margin_right': '0px',
-                                                                                                     'background-color': TAB_COLOR,
-                                                                                                     'display': 'inline-block'},
-
-                                                                       children=[
-                                                                           html.P("Filters"),
-                                                                           html.Small("Number of game owners"),
-                                                                           dcc.RangeSlider(id=RATING_SLIDER,
-                                                                                           min=min_owner, max=max_owner,
-                                                                                           marks=owner_range_dict,
-                                                                                           step=None,
-                                                                                           value=[min_owner,
-                                                                                                  max_owner]),
-                                                                           html.Small("Minimum amount of reviews"),
-                                                                           dcc.Input(id=RATING_MIN_REVIEWS,
-                                                                                     type="number", min=0,
-                                                                                     max=max_reviews, step=1, value=0)
-                                                                       ]
-                                                                       ),
-                                                              html.Div(
-                                                                  children=[dcc.Graph(id=RATING_DISTRIBUTION_PLOT)],
-                                                                  style=NORMAL_DIVISION_DICT | {'width': '100%',
-                                                                                                'display': 'inline-block'}
-                                                              )
-                                                          ]
-                                                          ),
-                                                 html.Div(id="Game pop bottom_region",
-                                                          style=NORMAL_DIVISION_DICT | {'width': '90%',
-                                                                                        'overflow': 'auto',
-                                                                                        'height': '70%'},
-                                                          className="scrollable",
-                                                          children=[
-                                                              html.Div(dash.dash_table.DataTable(id=RATING_TABLE
-                                                                                                 ))
-                                                          ])
-                                             ]
-                                             ),
-                                ]
-                                ),
-                        dcc.Tab(label="Market performance", value="tab4",
-                                style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
-                        dcc.Tab(label="Market prediction tool", value="tab5",
-                                style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
-                    ],
-                             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'
-                                               }),
-                html.Div(children=[
-                    dcc.Tabs(id="company_information", value="tab3", children=[
-                        dcc.Tab(label="Developer infromation", value="tab3", children=[
-                            html.Div(children=[
-                                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',
-                                             ),
-                                html.Div(children=[
-                                    html.Div(
-                                        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",
-                                                                   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'}
-                                    ),
-                                    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",
-                                                               style=LIST_DICT),
-                                                    ],
-                                                        style={'margin-bottom': '10px'}
-                                                    ),
-                                                    html.Div(children=[
-                                                        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,
-                                                               children="",
-                                                               style=LIST_DICT)
-                                                    ])
-                                                ])
-                                            ])
-
-                                    ], style=SMALL_TAB_PANEL_DICT | {'width': '45%', 'height': '100%',
-                                                                     'margin-right': '0px', 'margin-left': '20px'}
-                                    )
-                                ], style={'height': '100%'})
-                            ], style={'margin-left': '20px', 'margin-right': '0px'}
-                            )
-                        ], style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
-                        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],
-                                         ),
-                        ], style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT)
-
-                    ],
-                             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'
-                                                })
-            ],
-                     style={'width': '100%', "padding-top": "30px", 'padding-left': "50px"}),
-        ],
-        style={"font-family": "Tahoma"}
-    )
-
-    APP.run(host=host, **kwargs)
-    print("The server has closed!")
-
-
-if __name__ == "__main__":
-    initialize_data()
-    initialize_dash(debug=True)
-
-print(csv_path)
diff --git a/dash_plot_generation/data_store.py b/dash_plot_generation/data_store.py
new file mode 100644
index 0000000..50a084a
--- /dev/null
+++ b/dash_plot_generation/data_store.py
@@ -0,0 +1,38 @@
+import os
+import pandas
+
+from dash_plot_generation.utils import convert_owners_to_limits, get_owner_means
+
+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")
+
+
+def initialize_data():
+    global FULL_DATA, OWNER_RANGE_PARTS_SORTED
+    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)
+
+    add_game_revenues_and_owner_means(dataframe)
+    owner_ranges = {value_range for value_range in dataframe["owners"].unique()}
+
+    ranges_test = [(convert_owners_to_limits(value_range), value_range.split(" .. ")) for value_range in owner_ranges]
+    unique_owner_values = {(limits[i], limits_str[i]) for (limits, limits_str)
+                           in ranges_test for i in range(2)}
+    sorted_owner_list = sorted(unique_owner_values, key=lambda range: range[0])
+
+    OWNER_RANGE_PARTS_SORTED = sorted_owner_list
+    FULL_DATA = dataframe
+    return dataframe, sorted_owner_list
+
+
+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"]))
+    else 0, axis=1)
+
+
+FULL_DATA, OWNER_RANGE_PARTS_SORTED = initialize_data()
diff --git a/dash_plot_generation/pages/__pycache__/about.cpython-311.pyc b/dash_plot_generation/pages/__pycache__/about.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..adec7b2db59a047318316c250003e7dea481c0c0
GIT binary patch
literal 1117
zcmaJ<y=xRf6rZ`<`_9FWB%o+9AOr#?L9h^!1o1$HBnoFN!pbqXGufNVelat5WK*TE
zu&}VRu(6P+Bv|GjxYi<p6vtKpZR{>2HmQ8G`M@AJyZd(DypP}Sy*Hn$)iQ$htvJa)
zWDxpcl(SVh-5mae&3nWU!yZ~P|BQ!xyo52zAdcD)cd~evWw)#i&^OP|QXky5&U_#O
zw{vG2%WYurhVNvh*8G{q^8d9KE?V6zD{hr8qAwNxVOVArHndgU07jeVtT5D8M-#)p
z4D`YTPOO&py<<FWwh`5>werJe*wG}SE?@gn*PQxw+U>@qMHSI4PTY`sBwQx0=!(E4
zPUuH8h)KldawvToIGl(;3Ad#cp=l};rA#8?Q@O%5VN}zpdc++8sg^^hqli!@ArFWS
z$+8F-;j57s3Qe0H1an7=FaR&GpC@yv&rdT>b>2fcf02cBlteOYLnQbxPMR^9173je
zn1;7WolBPs@|*@#LRJch^K_NG?0phS_qK^6In|s2JDEa04YUkdC*@%{)}RDZcLX=}
zI$=jDQ#N_RJtcw_K^6n3Mk=A>siXo0>d<dALl#4uOO8jCS^)M*sYX?+8B)pChUY27
zvvWN^oi=YGo8+|69#1NaD@TgRbV8bYHBn3~<1e$IBvr{>pw4B(OfD%l8UY|Q8cDV$
zx=H5A9r*4hd5^|0-LL2nV$km;5RdNLiwdaPK5f&k{Vd=$3Eb@$ac?{7ZO6Lz$xf#?
zX~s5r+Em*hH(*4%;qrh>YG~O8TG=V_sVF`!KJcMu9-on80A%jI`VL}{#TfTdWj%fR
zXl&==yN|A}TYKp0yL%sR?V#~pG`@$%VY6Sl@@DkywZ2tZuf2Z!W^UKIzGq!ORR;)H
zZG3=m?iD^t<^GAEG0d3rS#vx)C>2IBgJCp$V{oZZzIlK^92v2mAB-YAk|Ou5p?-O+
Zf0^`4R|cgV&Kw{RM@9@T3OND={Q-@uKJWkl

literal 0
HcmV?d00001

diff --git a/dash_plot_generation/pages/__pycache__/dash_plot_generator.cpython-311.pyc b/dash_plot_generation/pages/__pycache__/dash_plot_generator.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c5d8cf4cd3f1ceae68109aef27d31b3ad4697280
GIT binary patch
literal 14146
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

literal 0
HcmV?d00001

diff --git a/dash_plot_generation/pages/__pycache__/home.cpython-311.pyc b/dash_plot_generation/pages/__pycache__/home.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bcd74e231a6abc0348aa9a610418920a59f5b153
GIT binary patch
literal 643
zcmY*Vzl#(x7)@qof4jonDR^ixAS^6+CxV3>$2Bf!;hn{5;W9A9O?GGIGLs?64vZjd
zVR40pot;?7iFoz-2kbwv2U{#tEm(=o$Z2zx$(|$Zm*mU$l9%s2^10cp0~OzX-XXc#
zziUuL`CoDVK^5--1{hM%)4M@I2zw9$0}v1x#RFnu^Taw<`0M|(B?ksp!BH<@P%02E
z9Ye)d>oS(Ng)YG5)!U(gD<{>}{=Eu@UC^%m&<zgSFta=v(hGQ3mobuFR_{uJ!Y)dZ
z^wnO+7k!oxZcC5YgAt{6%p{4VkEqQCw$M}?BZ=$*<93MnA(1wU5KV<IwzEcmBqR&n
zmkg&$pw_2IhyxUob~Upw5*{~|jjYMZ&{w^<8>1n~s;(O;j_YP-$4@fj@jbQfXJv{~
zHj-b!x%U3&g-Q>1oacgY;e;qaiSslf9quQ@d6rI2<H>0%C!d_9$1Uw~E9NX9ed%ar
zH>OOwLlO~=q|YKpOAE(iA=!@8i|TI3@Q9K<zM*vVhl|%L<tBu14jSX-n1k!n)wuxc
zV`~Q1-#z$vdkR`-pfv+6RV-?2N7vrom|Kl;=gp&|Cui2?%-URB!s!)-_h;6AUMX!D
Zc^zE6nKw%HTfcxxzjYdy_26sD!Cz6suj&8*

literal 0
HcmV?d00001

diff --git a/dash_plot_generation/pages/about.py b/dash_plot_generation/pages/about.py
new file mode 100644
index 0000000..ed454f0
--- /dev/null
+++ b/dash_plot_generation/pages/about.py
@@ -0,0 +1,21 @@
+import dash
+from dash import html
+
+dash.register_page(__name__, path='/')
+
+layout = html.Div([
+    html.H1('About page'),
+    html.H4('SteamSavvy has the goal of giving company performance insights about the steam market data.'
+            'Special focus is given to find exploitable sections in the steam game market.'),
+    html.H4('SteamSavvy is a project made by Linsen Gao, Sergei Panarin and Max Väistö created for the Introduction'
+            ' to Data Science course in Helsinki University.'),
+
+], className="custom_body")
+
+
+dash.register_page(
+    __name__,
+    title="Dashboard",
+    description="Main dashboard",
+    path="/about",
+)
\ No newline at end of file
diff --git a/dash_plot_generation/pages/dash_plot_generator.py b/dash_plot_generation/pages/dash_plot_generator.py
new file mode 100644
index 0000000..a88940f
--- /dev/null
+++ b/dash_plot_generation/pages/dash_plot_generator.py
@@ -0,0 +1,314 @@
+import numpy
+import dash
+from dash import html, dcc
+from plotly import graph_objects as go
+
+from dash_plot_generation.styles_and_handles import RATING_MIN_REVIEWS, RATING_SLIDER, RATING_TABLE, \
+    RATING_DISTRIBUTION_PLOT, MAIN_PANEL_TAB_DICT, DEV_AVERAGE_RATING_LABEL, \
+    DEFAULT_PLOT_STYLE_DICT, WHITE_STEAM, TAB_COLOR, TAB_EDGE, DEFAULT_TABS_DICT, DEVELOPER_DROPDOWN, TAB_NORMAL_DICT, \
+    TAB_HIGHLIGHT_DICT, PANEL_DEFAULT_DICT, SMALL_PANEL_DICT, SMALL_TAB_PANEL_DICT, SMALL_PANEL_HEADER_DICT, \
+    DEV_TOP_GENRES_LABEL, LIST_DICT, NORMAL_DIVISION_DICT, DEV_CCU_LABEL, DEV_GAME_COUNT_LABEL, DEV_REV_PER_GAME_LABEL, \
+    DEV_REVENUE_LABEL, DEV_TOP_GAMES, DARK_BLUE_STEAM
+
+from dash_plot_generation.data_store import FULL_DATA, OWNER_RANGE_PARTS_SORTED
+
+global APP
+
+# 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"]
+
+# Genre performance table_values
+# genre_owners, genre_revenue = get_genre_popularity_counts(FULL_DATA, 6)
+genre_owners = {key: val for (key, val) in
+                zip(["Action", "Adventure", "RPG", "Puzzle", "Strategy", "Other"],
+                    [0.7, 0.5, 0.1, 0.4, 0.3, 0.7])}
+genre_revenue = {key: val for (key, val) in
+                 zip(["Action", "Adventure", "RPG", "Puzzle", "Strategy", "Other"],
+                     [0.5, 0.4, 0.3, 0.4, 0.6, 0.7])}
+
+# Game popularity filter values
+max_reviews = numpy.nanmax(FULL_DATA.apply(lambda x: x["positive"] + x["negative"], axis=1))
+owner_range_dict = {index: val_str for (index, (val, val_str)) in enumerate(OWNER_RANGE_PARTS_SORTED)}
+min_owner = min(owner_range_dict.keys())
+max_owner = max(owner_range_dict.keys())
+
+
+layout = html.Div(
+    children=[
+        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=[
+                                    html.Div(
+                                        children=[
+                                            html.Div(style=NORMAL_DIVISION_DICT,
+                                                     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=DEFAULT_PLOT_STYLE_DICT |
+                                                                    dict(title="Relative genre perfomance")
+                                                         ),
+
+                                                     ),
+                                                         html.P(f"""Genre performance measures the assessed 
+                                                         exploitability of the specific game genre. The assessment 
+                                                         is done by estimating the genre popularity, and games 
+                                                         developed in the next two years and showing the relative 
+                                                         differences between the genres.""")]),
+                                            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=list(genre_owners.keys()),
+                                                                              values=list(genre_owners.values()),
+                                                                              sort=False)],
+                                                                              layout=DEFAULT_PLOT_STYLE_DICT |
+                                                                                     dict(title="Genre popularity",
+                                                                                          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=list(genre_revenue.keys()),
+                                                                              values=list(genre_revenue.values()),
+                                                                              sort=False)],
+                                                                              layout=DEFAULT_PLOT_STYLE_DICT |
+                                                                                     dict(
+                                                                                         title="Genre revenue share",
+                                                                                         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=DEFAULT_PLOT_STYLE_DICT |
+                                                                          dict(title="Genre prediction plot",
+                                                                               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=NORMAL_DIVISION_DICT | {'width': '90%'})
+
+                                ],
+                                    style=MAIN_PANEL_TAB_DICT,
+                                    className="scrollable")
+                            ]),
+                    dcc.Tab(label="Game popularity", value="tab2",
+                            style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT,
+                            children=[
+                                html.Div(id="Game pop general layout",
+                                         style=MAIN_PANEL_TAB_DICT,
+                                         className="scrollable",
+                                         children=[
+                                             html.Div(id="Game pop top div",
+                                                      children=[
+                                                          html.Div(id="game popularity filters",
+                                                                   style=NORMAL_DIVISION_DICT | {'width': '100%',
+                                                                                                 'height': '100%',
+                                                                                                 'margin_left': '0px',
+                                                                                                 'margin_right': '0px',
+                                                                                                 'background-color': TAB_COLOR,
+                                                                                                 'display': 'inline-block'},
+
+                                                                   children=[
+                                                                       html.P("Filters"),
+                                                                       html.Small("Number of game owners"),
+                                                                       dcc.RangeSlider(id=RATING_SLIDER,
+                                                                                       min=min_owner, max=max_owner,
+                                                                                       marks=owner_range_dict,
+                                                                                       step=None,
+                                                                                       value=[min_owner,
+                                                                                              max_owner]),
+                                                                       html.Small("Minimum amount of reviews"),
+                                                                       dcc.Input(id=RATING_MIN_REVIEWS,
+                                                                                 type="number", min=0,
+                                                                                 max=max_reviews, step=1, value=0)
+                                                                   ]
+                                                                   ),
+                                                          html.Div(
+                                                              children=[dcc.Graph(id=RATING_DISTRIBUTION_PLOT)],
+                                                              style=NORMAL_DIVISION_DICT | {'width': '100%',
+                                                                                            'display': 'inline-block'}
+                                                          )
+                                                      ]
+                                                      ),
+                                             html.Div(id="Game pop bottom_region",
+                                                      style=NORMAL_DIVISION_DICT | {'width': '90%',
+                                                                                    'overflow': 'auto',
+                                                                                    'height': '70%'},
+                                                      className="scrollable",
+                                                      children=[
+                                                          html.Div(dash.dash_table.DataTable(id=RATING_TABLE
+                                                                                             ))
+                                                      ])
+                                         ]
+                                         ),
+                            ]
+                            ),
+                    dcc.Tab(label="Market performance", value="tab4",
+                            style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
+                    dcc.Tab(label="Market prediction tool", value="tab5",
+                            style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
+                ],
+                         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'
+                                           }),
+            html.Div(children=[
+                dcc.Tabs(id="company_information", value="tab3", children=[
+                    dcc.Tab(label="Developer infromation", value="tab3", children=[
+                        html.Div(children=[
+                            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',
+                                         ),
+                            html.Div(children=[
+                                html.Div(
+                                    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",
+                                                               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'}
+                                ),
+                                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",
+                                                           style=LIST_DICT),
+                                                ],
+                                                    style={'margin-bottom': '10px'}
+                                                ),
+                                                html.Div(children=[
+                                                    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,
+                                                           children="",
+                                                           style=LIST_DICT)
+                                                ])
+                                            ])
+                                        ])
+
+                                ], style=SMALL_TAB_PANEL_DICT | {'width': '45%', 'height': '100%',
+                                                                 'margin-right': '0px', 'margin-left': '20px'}
+                                )
+                            ], style={'height': '100%'})
+                        ], style={'margin-left': '20px', 'margin-right': '0px'}
+                        )
+                    ], style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT),
+                    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],
+                                     ),
+                    ], style=TAB_NORMAL_DICT, selected_style=TAB_HIGHLIGHT_DICT)
+
+                ],
+                         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'
+                                            })
+        ],
+                 style={'width': '100%', "padding-top": "30px", 'padding-left': "50px"}),
+    ],
+    style={"font-family": "Tahoma"},
+    className="body"
+)
+
+dash.register_page(
+    __name__,
+    path_template="/stocks/<ticker>",
+    title="Dashboard",
+    description="Main dashboard",
+    path="/dashboard",
+)
\ No newline at end of file
diff --git a/dash_plot_generation/pages/home.py b/dash_plot_generation/pages/home.py
new file mode 100644
index 0000000..bf74e8b
--- /dev/null
+++ b/dash_plot_generation/pages/home.py
@@ -0,0 +1,11 @@
+import dash
+from dash import html
+
+dash.register_page(__name__, path='/')
+
+layout = html.Div([
+    html.H1('SteamSavvy'),
+
+    html.H4('Discover the full potential of steam data for market analysis.'),
+
+], className="custom_body")
\ No newline at end of file
diff --git a/dash_plot_generation/pages/technical_report.py b/dash_plot_generation/pages/technical_report.py
new file mode 100644
index 0000000..e69de29
diff --git a/dash_plot_generation/styles_and_handles.py b/dash_plot_generation/styles_and_handles.py
new file mode 100644
index 0000000..354a4ae
--- /dev/null
+++ b/dash_plot_generation/styles_and_handles.py
@@ -0,0 +1,72 @@
+
+# html handles
+RATING_MIN_REVIEWS = "min_reviews_id"
+RATING_SLIDER = "rating_slider"
+RATING_TABLE = "rating_data_table"
+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"
+DEV_TOP_GENRES_LABEL = "dev_top_genres"
+DEVELOPER_DROPDOWN = "developer_dropdown"
+RATING_DISTRIBUTION_PLOT = "game_popularity_density_plot"
+DEV_AVERAGE_RATING_LABEL = "dev_average_rating"
+
+
+MAIN_PANEL_TAB_DICT = {'height': '550px', 'width': '100%', 'margin': '0', 'overflow': 'auto'}
+SPACE_NORMAL_ENTRY = 35
+RIGHT_SIDE_TEXT_DICT = {'display': 'inline-block',
+                        'float': 'right', 'margin-right': '0%', 'margin-bottom': '0px'}
+
+DEFAULT_PLOT_STYLE_DICT = dict(
+    template="plotly_dark",
+    plot_bgcolor="rgb(31,46,65)",
+    paper_bgcolor="rgb(31,46,65)",
+)
+
+DENSITY_LAYOUT_STYLE = DEFAULT_PLOT_STYLE_DICT | dict(
+    title='Distribution of Game Review Rating',
+    xaxis_title="Game user rating",
+    yaxis_title="Proportion"
+
+)
+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)"
+TAB_HEADER_COLOR = "rgb(45,96,150)"
+
+DEFAULT_TABS_DICT = {'width': 'auto', 'display': 'flex',
+                     'background-color': TAB_COLOR, 'border-color': TAB_EDGE}
+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,
+                      'color': WHITE_STEAM, 'height': '600px'}
+SMALL_PANEL_DICT = {'float': 'left', 'background-color': 'transparent', 'box-sizing': 'border-box',
+                    'padding': '10px'}
+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%'}
+LIST_DICT = {'display': 'inline-block', 'margin-bottom': '0px', 'padding-right': '0px'}
+NORMAL_DIVISION_DICT = SMALL_PANEL_DICT | {'width': '60%', 'height': '100%', 'margin-right': '5%', 'padding-left': '5%',
+                                           'margin-bottom': '5%', 'background-color': TAB_COLOR}
diff --git a/dash_plot_generation/utils.py b/dash_plot_generation/utils.py
index 6d380af..4fd831a 100644
--- a/dash_plot_generation/utils.py
+++ b/dash_plot_generation/utils.py
@@ -1,7 +1,13 @@
 import math
 import re
+from collections import Counter
 from typing import Sequence, Optional, Any
+
+import numpy
 import pandas
+from dash import html
+
+from dash_plot_generation.styles_and_handles import SPACE_NORMAL_ENTRY
 
 DEFAULT_ILLEGAL_CONTINUATIONS = {"INC.", "LLC", "CO.", "LTD.", "S.R.O."}
 
@@ -120,3 +126,79 @@ def round_to_three_largest_digits(number, accuracy=2):
     round_val = -(len(str(round(number))) - accuracy)
     return_val = round(round(number), min(round_val, 0))
     return return_val
+
+
+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, ".")
+    return label
+
+
+def get_game_count_label(dev_data):
+    return label_with_text("Number of games", str(dev_data.shape[0]), SPACE_NORMAL_ENTRY, ".")
+
+
+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,
+                                                                   ".", "$"), 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
+
+
+def get_top_genre_labels(data):
+    genre_totals = [genre for genre_list in data["genres"] if isinstance(genre_list, str)
+                    for genre in genre_list.split(", ")]
+    genre_counts = Counter(genre_totals).most_common(3)
+    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%'})
+    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, ".")
+
+
+def get_genre_popularity_counts(df, group_after_largest=8):
+    genre_df = df[["genres", "owner_means", "game_revenue"]]
+    genre_owners = {}
+    genre_revenue = {}
+
+    for index, row in genre_df.iterrows():
+        if not isinstance(row.genres, str):
+            continue
+        genre_list = row.genres.split(", ")
+        for genre in genre_list:
+            if genre in genre_owners.keys():
+                genre_owners[genre] += row["owner_means"]
+                genre_revenue[genre] += row["game_revenue"]
+            else:
+                genre_owners[genre] = row["owner_means"]
+                genre_revenue[genre] = row["game_revenue"]
+    top_owners = dict(Counter(genre_owners).most_common(group_after_largest))
+    top_revenue = dict(Counter(genre_revenue).most_common(group_after_largest))
+    top_owners["Other"] = sum([val for (key, val) in genre_owners.items()
+                               if key not in top_owners.keys()])
+    top_revenue["Other"] = sum([val for (key, val) in genre_revenue.items()
+                                if key not in top_revenue.keys()])
+
+    return top_owners, top_revenue
+
+
+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, ".", "$")
+    dev_game_revenue_per_game = " ".join(["•", dev_game_revenue_per_game_row])
+    return dev_game_revenue_per_game
-- 
GitLab