From bc569c2106c5843a3ffebd3e0a7799ad0e67b40c Mon Sep 17 00:00:00 2001 From: LeoLuosifen <18077921025@163.com> Date: Wed, 5 Nov 2025 16:35:25 +1100 Subject: [PATCH 1/3] fix: fixed some issues provided - updated the Menu structure - centred the Sun and cloud tab - fixed the Outdoor Comfort / UTCI chart's bar chart - made the Apply filter button more visible - made the dropdown for the filtering is open by default --- config.py | 2 +- pages/lib/layout.py | 43 ++++++++++++++++++++++++++---------- pages/lib/template_graphs.py | 24 ++++++++++++++------ pages/select.py | 3 --- pages/sun.py | 4 ++-- tests/test_filter.py | 15 ++----------- tests/test_summary.py | 7 +++--- 7 files changed, 57 insertions(+), 41 deletions(-) diff --git a/config.py b/config.py index bc79454..b53ce10 100644 --- a/config.py +++ b/config.py @@ -54,7 +54,7 @@ class Assets: class PageInfo: """Stores page names and orders for registration.""" - SELECT_NAME = "Select Weather File" + SELECT_NAME = "Select weather file" SELECT_ORDER = 0 SUMMARY_NAME = "Climate Summary" SUMMARY_ORDER = 1 diff --git a/pages/lib/layout.py b/pages/lib/layout.py index 523b9e6..ac06535 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -14,7 +14,6 @@ class NavBarIcons: _ICON_MAP = { - "Select Weather File": "tabler:upload", "Climate Summary": "tabler:chart-bar", "Temperature and Humidity": "tabler:temperature", "Sun and Clouds": "tabler:sun", @@ -26,7 +25,6 @@ class NavBarIcons: "Changelog": "tabler:history", } - SELECT_WEATHER_FILE = _ICON_MAP["Select Weather File"] CLIMATE_SUMMARY = _ICON_MAP["Climate Summary"] TEMPERATURE_AND_HUMIDITY = _ICON_MAP["Temperature and Humidity"] SUN_AND_CLOUDS = _ICON_MAP["Sun and Clouds"] @@ -54,7 +52,7 @@ def create_tools_filter_components(): "Apply month and hour filter", id=ElementIds.TOOLS_APPLY_MONTH_HOUR_FILTER, color="blue", - variant="light", + variant="filled", size="xs", ), # Month controls @@ -137,7 +135,21 @@ def create_navbar(): } } - # Secondary Menu + # Select weather file - top-level menu item + select_weather_file_page = next( + (page for page in dash.page_registry.values() + if page[Variables.NAME.col_name] == "Select weather file"), + None + ) + select_weather_file_link = dmc.NavLink( + label=select_weather_file_page[Variables.NAME.col_name], + href=select_weather_file_page[Variables.PATH.col_name], + id=f"nav-{select_weather_file_page[Variables.PATH.col_name].replace('/', '')}", + active=False, + styles=nav_link_styles, + ) if select_weather_file_page else None + + # Secondary Menu - exclude "Select weather file" as it will be a top-level menu sub_links = [ dmc.NavLink( label=page[Variables.NAME.col_name], @@ -150,11 +162,11 @@ def create_navbar(): styles=nav_link_styles, ) for page in dash.page_registry.values() - if page[Variables.NAME.col_name] not in ["404", "Changelog"] + if page[Variables.NAME.col_name] not in ["404", "Changelog", "Select weather file"] ] parent_group = dmc.NavLink( - label="Pages Menu", + label="Visualize weather file", children=sub_links, id=ElementIds.NAV_GROUP_MAIN, variant="light", @@ -168,8 +180,9 @@ def create_navbar(): controls_stack = dmc.Stack( gap="xs", - py="xs", + p="xs", children=[ + dmc.Divider(label="Unit function", size="xs", color="blue"), dmc.Tooltip( label=dmc.Stack( gap="xs", @@ -189,7 +202,7 @@ def create_navbar(): {"label": "Global", "value": "global"}, {"label": "Local", "value": "local"}, ], - w=220, + w=210, size="sm", styles=segmented_control_styles, ), @@ -211,7 +224,7 @@ def create_navbar(): {"label": "SI", "value": UnitSystem.SI}, {"label": "IP", "value": UnitSystem.IP}, ], - w=220, + w=210, size="sm", styles=segmented_control_styles, ), @@ -223,11 +236,12 @@ def create_navbar(): # Tools controls_group = dmc.NavLink( - label="Tools Menu", - children=[controls_stack, filter_components], + label="Filters and units", + children=[filter_components, controls_stack], id=ElementIds.NAV_GROUP_CONTROLS, variant="light", childrenOffset=0, + opened=True, ) # Documentation @@ -240,7 +254,12 @@ def create_navbar(): ) return dmc.ScrollArea( - children=[parent_group, controls_group, doc_link], + children=[ + select_weather_file_link, + parent_group, + controls_group, + doc_link, + ], ) diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index b6ab5b5..d76665e 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -405,7 +405,9 @@ def heatmap_with_filter( ), ) - if global_local == "global": + # For category variables (e.g., UTCI categories), always use global range + # to ensure consistent color mapping regardless of data range + if "_categories" in var or global_local == "global": # Set Global values for Max and minimum range_z = var_range else: @@ -712,7 +714,9 @@ def wind_rose(df, title, month, hour, labels, si_ip, skip_time_filter=False): spd_colors = wind_speed_variable.get_color() spd_unit = wind_speed_variable.get_unit(si_ip) - spd_bins = WIND_ROSE_BINS + spd_bins = list( + WIND_ROSE_BINS + ) # Create a copy to avoid modifying the global constant if si_ip == UnitSystem.IP: spd_bins = convert_bins(spd_bins) @@ -810,12 +814,18 @@ def wind_rose(df, title, month, hour, labels, si_ip, skip_time_filter=False): def convert_bins(sbins): - i = 0 + """Convert wind speed bins from m/s to fpm (feet per minute). + + Returns a new list without modifying the input list. + """ + result = [] for x in sbins: - x = x * 196.85039370078738 - sbins[i] = round(x, 1) - i = i + 1 - return sbins + if np.isfinite(x): + converted = round(x * 196.85039370078738, 1) + result.append(converted) + else: + result.append(x) # Preserve np.inf + return result def thermal_stress_stacked_barchart( diff --git a/pages/select.py b/pages/select.py index 2b61da8..d295c06 100644 --- a/pages/select.py +++ b/pages/select.py @@ -248,7 +248,6 @@ def switch_si_ip(_, si_ip_input, url_store, lines): @callback( [ - Output(ElementIds.NAV, "disabled"), Output(ElementIds.NAV_SUMMARY, "disabled"), Output(ElementIds.NAV_T_RH, "disabled"), Output(ElementIds.NAV_SUN, "disabled"), @@ -277,7 +276,6 @@ def enable_tabs_when_data_is_loaded(meta, data): True, True, True, - True, default, ) else: @@ -290,7 +288,6 @@ def enable_tabs_when_data_is_loaded(meta, data): False, False, False, - False, "Current Location: " + meta[Variables.CITY.col_name] + ", " diff --git a/pages/sun.py b/pages/sun.py index 650fe97..c717677 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -286,7 +286,7 @@ def sun_path_chart(_, view, var, global_local, global_filter_data, df, meta, si_ units = "" if var == "None" else generate_units(si_ip) if view == "polar": return dcc.Graph( - style={"width": "100%", "height": "520px"}, + style={"maxWidth": "50em", "height": "32.5em"}, config=generate_chart_name( TabNames.SPHERICAL_SUNPATH, meta, custom_inputs, units ), @@ -294,7 +294,7 @@ def sun_path_chart(_, view, var, global_local, global_filter_data, df, meta, si_ ) else: return dcc.Graph( - style={"width": "100%", "height": "520px"}, + style={"maxWidth": "50em", "height": "32.5em"}, config=generate_chart_name( TabNames.CARTESIAN_SUNPATH, meta, custom_inputs, units ), diff --git a/tests/test_filter.py b/tests/test_filter.py index 7347e93..95a3367 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -26,19 +26,8 @@ def ensure_local_mode_and_invert_off(page: Page): def open_tools_menu_and_filter_section(page: Page): - """Open Tools Menu → Filter function, using the simplest reliable click.""" - header = page.get_by_text("Tools Menu", exact=False).first - header.scroll_into_view_if_needed() - header.click() - - try: - page.get_by_text("Filter function", exact=False).first.click() - except Exception: - pass - - expect( - page.get_by_text("Apply month and hour filter", exact=False).first - ).to_be_visible() + apply_btn = page.get_by_text("Apply month and hour filter", exact=False) + expect(apply_btn.first).to_be_visible() ensure_local_mode_and_invert_off(page) diff --git a/tests/test_summary.py b/tests/test_summary.py index 72aad62..402a4af 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -86,11 +86,12 @@ def test_unit_switch(page: Page): # Click the "IP" option ip_button = page.get_by_text("IP", exact=True) + expect(ip_button).to_be_visible() ip_button.scroll_into_view_if_needed() ip_button.wait_for(state="visible") ip_button.click(force=True) info_section = page.locator("#location-info") - expect(info_section).to_contain_text("°F") - expect(info_section).to_contain_text("ft") - expect(info_section).to_contain_text("kBtu/ft2") + expect(info_section.get_by_text("°F")).to_be_visible + expect(info_section.get_by_text("ft")).to_be_visible + expect(info_section.get_by_text("kBtu/ft2")).to_be_visible \ No newline at end of file From 0355b30e41015209bb63d30bf3606da4679b396a Mon Sep 17 00:00:00 2001 From: LeoLuosifen <18077921025@163.com> Date: Wed, 5 Nov 2025 16:59:48 +1100 Subject: [PATCH 2/3] fix: formatted the code. --- pages/lib/layout.py | 30 +++++++++++++++++++----------- tests/test_summary.py | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pages/lib/layout.py b/pages/lib/layout.py index ac06535..0400572 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -137,17 +137,24 @@ def create_navbar(): # Select weather file - top-level menu item select_weather_file_page = next( - (page for page in dash.page_registry.values() - if page[Variables.NAME.col_name] == "Select weather file"), - None + ( + page + for page in dash.page_registry.values() + if page[Variables.NAME.col_name] == "Select weather file" + ), + None, + ) + select_weather_file_link = ( + dmc.NavLink( + label=select_weather_file_page[Variables.NAME.col_name], + href=select_weather_file_page[Variables.PATH.col_name], + id=f"nav-{select_weather_file_page[Variables.PATH.col_name].replace('/', '')}", + active=False, + styles=nav_link_styles, + ) + if select_weather_file_page + else None ) - select_weather_file_link = dmc.NavLink( - label=select_weather_file_page[Variables.NAME.col_name], - href=select_weather_file_page[Variables.PATH.col_name], - id=f"nav-{select_weather_file_page[Variables.PATH.col_name].replace('/', '')}", - active=False, - styles=nav_link_styles, - ) if select_weather_file_page else None # Secondary Menu - exclude "Select weather file" as it will be a top-level menu sub_links = [ @@ -162,7 +169,8 @@ def create_navbar(): styles=nav_link_styles, ) for page in dash.page_registry.values() - if page[Variables.NAME.col_name] not in ["404", "Changelog", "Select weather file"] + if page[Variables.NAME.col_name] + not in ["404", "Changelog", "Select weather file"] ] parent_group = dmc.NavLink( diff --git a/tests/test_summary.py b/tests/test_summary.py index 402a4af..f2fbc38 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -94,4 +94,4 @@ def test_unit_switch(page: Page): info_section = page.locator("#location-info") expect(info_section.get_by_text("°F")).to_be_visible expect(info_section.get_by_text("ft")).to_be_visible - expect(info_section.get_by_text("kBtu/ft2")).to_be_visible \ No newline at end of file + expect(info_section.get_by_text("kBtu/ft2")).to_be_visible From 6a385a9e1a2a661b671511d373948dc673efa17b Mon Sep 17 00:00:00 2001 From: Federico Tartarini Date: Thu, 6 Nov 2025 11:24:11 +1100 Subject: [PATCH 3/3] refactor(layout): update filter section labels and improve button placement --- pages/lib/layout.py | 34 +++++++++++----------------------- pages/sun.py | 4 ++-- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/pages/lib/layout.py b/pages/lib/layout.py index 0400572..93374f3 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -47,14 +47,7 @@ def create_tools_filter_components(): return dmc.Stack( id=ElementIds.TOOLS_MONTH_HOUR_SECTION, children=[ - dmc.Divider(label="Filter function", size="xs", color="blue"), - dmc.Button( - "Apply month and hour filter", - id=ElementIds.TOOLS_APPLY_MONTH_HOUR_FILTER, - color="blue", - variant="filled", - size="xs", - ), + dmc.Divider(label="Filters", size="xs", color="blue"), # Month controls dmc.Text("Month Range:", size="xs", c="dimmed"), dcc.RangeSlider( @@ -111,6 +104,13 @@ def create_tools_filter_components(): ], justify="flex-end", ), + dmc.Button( + "Apply month and hour filter", + id=ElementIds.TOOLS_APPLY_MONTH_HOUR_FILTER, + color="blue", + variant="filled", + size="xs", + ), ], gap="xs", p="xs", @@ -190,16 +190,9 @@ def create_navbar(): gap="xs", p="xs", children=[ - dmc.Divider(label="Unit function", size="xs", color="blue"), + dmc.Divider(label="Units and Ranges", size="xs", color="blue"), dmc.Tooltip( - label=dmc.Stack( - gap="xs", - children=[ - dmc.Text( - "You can choose value ranges between Global and Local" - ), - ], - ), + label=dmc.Text("You can choose value ranges between Global and Local"), position="right", withArrow=True, children=dmc.SegmentedControl( @@ -216,12 +209,7 @@ def create_navbar(): ), ), dmc.Tooltip( - label=dmc.Stack( - gap="xs", - children=[ - dmc.Text("You can choose units between SI and IP"), - ], - ), + label=dmc.Text("You can choose units between SI and IP"), position="right", withArrow=True, children=dmc.SegmentedControl( diff --git a/pages/sun.py b/pages/sun.py index c717677..2f7c831 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -286,7 +286,7 @@ def sun_path_chart(_, view, var, global_local, global_filter_data, df, meta, si_ units = "" if var == "None" else generate_units(si_ip) if view == "polar": return dcc.Graph( - style={"maxWidth": "50em", "height": "32.5em"}, + style={"maxWidth": "50em", "height": "520px"}, config=generate_chart_name( TabNames.SPHERICAL_SUNPATH, meta, custom_inputs, units ), @@ -294,7 +294,7 @@ def sun_path_chart(_, view, var, global_local, global_filter_data, df, meta, si_ ) else: return dcc.Graph( - style={"maxWidth": "50em", "height": "32.5em"}, + style={"maxWidth": "50em", "height": "520px"}, config=generate_chart_name( TabNames.CARTESIAN_SUNPATH, meta, custom_inputs, units ),