Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various improvements of the plot #44

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 104 additions & 12 deletions web/dashboard/main_dashboard.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import json
from datetime import datetime
from functools import lru_cache

import colorcet as cc
import pandas as pd
import panel as pn
import param
from components.select_picker import SelectPicker
from ui import divider
from ui import MAIN_COLOR, divider

pn.extension("echarts")
pn.extension(
"echarts",
"codeeditor",
)

pd.options.display.max_columns = None

Expand Down Expand Up @@ -90,9 +95,22 @@ class MainDashboard(param.Parameterized):

# UI elements
echarts_pane = pn.pane.ECharts(
{}, height=640, width=960, renderer="svg", options={"replaceMerge": ["series"]}
{}, height=640, width=1200, renderer="svg", options={"replaceMerge": ["series"]}
)

# DEBUG
# This is a code editor to update the ECharts config and render the plot from the browser
# without having to restart the server.

# Switch this to True to enable it.
debug = False

echarts_config_editor = pn.widgets.CodeEditor(
value="", sizing_mode="stretch_width", language="javascript", height=800
)
echarts_update_button = pn.widgets.Button(name="Update ECharts")
echarts_config = param.Dict(default={})

def __init__(self, datasets, **params):
super().__init__(**params)

Expand Down Expand Up @@ -135,6 +153,9 @@ def __init__(self, datasets, **params):

self.build_pubdate_filter()

# DEBUG
self.echarts_update_button.on_click(self.did_click_update_echart_plot)

@pn.depends("extraction_tool", watch=True)
def did_change_extraction_tool(self):
print("DID_CHANGE_EXTRACTION_TOOL")
Expand Down Expand Up @@ -437,6 +458,7 @@ def updated_echart_plot(self):
legend_data = [
{"name": self.metrics, "icon": "path://M 0 0 H 20 V 20 H 0 Z"},
]
colormap = [MAIN_COLOR]
else:
title = f"{self.metrics} by {self.splitting_var} ({int(self.filter_pubdate[0])}-{int(self.filter_pubdate[1])})"

Expand Down Expand Up @@ -464,6 +486,7 @@ def updated_echart_plot(self):
splitting_var_column = "journal"
splitting_var_query = lambda cell, selected_item: cell == selected_item

last_year_values = {}
for selected_item in sorted(splitting_var_filter):
# sub_df = df.query(f"{splitting_var_column} == '{selected_item}'")

Expand All @@ -480,22 +503,59 @@ def updated_echart_plot(self):
.reset_index()
)

value_last_year = sub_df[sub_df.year == sub_df.year.max()][
raw_metric
].values[0]
last_year_values[selected_item] = value_last_year

series.append(
{
"id": selected_item,
"name": selected_item,
"type": "line",
"data": sub_df[raw_metric].tolist(),
# Shows a label at the end of the line.
# Labels end up overlapping in some cases.
# To fix this, we would need to change the offset of the label
# with values calculated to avoid overlapping.
# https://echarts.apache.org/en/option.html#series-line.endLabel.offset
# "endLabel":{
# "formatter":selected_item,
# "show":True,
# }
}
)
legend_data.append(
{"name": selected_item, "icon": "path://M 0 0 H 20 V 20 H 0 Z"}
)

# Sort the legend series by decreasing order of the last year value
legend_data.sort(
key=lambda x: last_year_values.get(x["name"], 0), reverse=True
)

colormap = cc.glasbey_light

# Hack for the tooltip.
# The tooltip shows values with 15 decimals, which is not very useful.
# We could use the formatter option of the tooltip, but it requires a bit of
# time to get the same good-looking result.
# So for the sake of delivering fast, I just round the values to 2 decimals.
for serie in series:
serie["data"] = [round(v, 2) for v in serie["data"]]

# Default colormap is :
# ["#5470c6", "#91cc75", "#fac858", "#ee6666", "#73c0de", "#3ba272", "#fc8452", "#9a60b4", "#ea7ccc"]
# https://echarts.apache.org/en/option.html#color

echarts_config = {
"color": colormap,
"title": {
"text": title,
},
"grid": {
"width": "800",
},
"tooltip": {
"show": True,
"trigger": "axis",
Expand All @@ -505,27 +565,52 @@ def updated_echart_plot(self):
},
"legend": {
"data": legend_data,
"type": "scroll",
"orient": "vertical",
"right": 10,
"top": 20,
"bottom": 20,
"show": True,
"right": "0",
"textStyle": {"width": "250", "overflow": "break"},
},
"xAxis": {
"data": xAxis,
"name": "year",
"nameLocation": "center",
"nameGap": 30,
"nameGap": 40,
"nameTextStyle": {
"fontWeight": "bold",
"fontFamily": "Roboto",
"fontSize": "20",
},
},
"yAxis": {
"name": "percent",
"name": self.metrics,
"nameLocation": "center",
"nameGap": 30,
"nameGap": 80,
"nameTextStyle": {
"fontWeight": "bold",
"fontFamily": "Roboto",
"fontSize": "20",
},
"axisLabel": {"formatter": "{value}%"},
},
"series": series,
}

self.echarts_pane.object = echarts_config
self.echarts_config = echarts_config
self.echarts_config_editor.value = json.dumps(
self.echarts_config, indent=4, sort_keys=True
)

@pn.depends("echarts_config", watch=True)
def echarts_config_updated(self):
self.echarts_pane.object = self.echarts_config
self.echarts_pane.loading = False

def did_click_update_echart_plot(self, event):
print("DID_CLICK_UPDATE_ECHART_PLOT")
self.echarts_pane.loading = True
self.echarts_config = json.loads(self.echarts_config_editor.value)
self.echarts_pane.object = self.echarts_config
self.echarts_pane.loading = False

# Below are all the functions returning the different parts of the dashboard :
Expand Down Expand Up @@ -689,12 +774,19 @@ def get_intro_block(self):
def get_dashboard(self):
print("GET_DASHBOARD")

# Layout the dashboard
dashboard = pn.Column(
items = [
self.get_top_bar(),
divider(),
self.echarts_pane,
# self.get_intro_block(),
]

if self.debug:
items += [divider(), self.echarts_update_button, self.echarts_config_editor]

# Layout the dashboard
dashboard = pn.Column(
*items,
css_classes=["dashboard-column"],
)

Expand Down
Loading