From b5d9e43e193ca751b26f126fd0d8e07365d433c6 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Tue, 19 Aug 2025 16:52:39 +1000 Subject: [PATCH 01/66] docs: Add the content of Fork & branch processing --- docs/contributing/contributing.md | 59 +++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index fa8fb538..88d3af06 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -10,6 +10,65 @@ First off, thanks for taking the time to contribute! If you have a general feedback about our project, please do not open an issue but instead please fill in this [form](https://forms.gle/LRUq3vsFnE1QCLiA6) +## Fork & branch processing + +First fork the origin repository to your own github repository, then clone the repository to your local computer. + +```bash +git clone https://github.com/Your Account name/clima.git +cd clima +``` + +Set up the upstream repository and check the output respositories. + +```bash +git remote add upstream https://github.com/CenterForTheBuiltEnvironment/clima.git + +git remote -v +``` + +The terminal should output a list: + +- `origin → your Fork repository` +- `upstream → origin repository` + +Check all branches. + +```bash +git branch -a +``` + +The terminal will show a list of branches: + +```bash +> * main + remotes/origin/HEAD -> origin/main + remotes/origin/development + remotes/origin/main +``` + +Pull the development branch first, and if the terminal does not notices you that you should try the second command. + +```bash +git checktout development + +git checkout -b development origin/development +``` + +Create a new branch in the development branch. + +```bash +git checkout -b (your branch name) +``` + +Finally update and push to your repository branch if you modify the files. + +```bash +git push origin (your branch name) +``` + + + ## Code of Conduct Available [here](code_of_conduct.md) From 852e350615f5a57c589d3f46786127833f40ac7a Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Tue, 19 Aug 2025 17:44:44 +1000 Subject: [PATCH 02/66] Add Code style and Testing --- docs/contributing/contributing.md | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 88d3af06..9dbccdea 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -77,6 +77,38 @@ Available [here](code_of_conduct.md) We use Black.exe to format the code. +Install Black: + +```bash +pip install black +``` + +Format your code before committing: + +```bash +black . +``` + +## Testing + +Before submitting a Pull Request, please make sure: +- All tests should pass. +- Make sure you have installed project dependencies: + +```bash +npm install + +pip install -r requirements.txt +``` + +From the root directory, run: + +```bash +cd tests/node + +npx cypress run +``` + ## Submitting changes Please send a Pull Request with a clear list of what you've done. Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this: From f9a72d031805889dd7c5189c4264282d50a02a12 Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Tue, 19 Aug 2025 18:18:42 +1000 Subject: [PATCH 03/66] docs: Add the "Pull Request Regulation" part. --- docs/contributing/contributing.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 9dbccdea..c29715ee 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -67,6 +67,29 @@ Finally update and push to your repository branch if you modify the files. git push origin (your branch name) ``` +## Pull Request Regulation +1.Time to submit PR: +User requirements/issues have been addressed or discussed in Issue and consensus has been reached. +Changes have been minimised (small steps/phased submission) to avoid "mega PRs". + +2.Classification of Common PR Types: +a. Main (Master):Stable branch, merge code that passes review and CI; merge and release every time, +b. Develop: Continuous Integration branch for daily integration with multiple collaborators. +c. feature/*: feature development branch, cut out from main or develop, send PR to merge in after completing the feature. +d. Fix/*: defect repair branch, the same process as feature +e. Release/*: release preparation branch for freezing versions, fixing documentation, doing regressions and tagging. +f. docs/*, chore/*, refactor/*, test/*: documentation, miscellaneous, refactor, test type branches. +g. Style: style modification (does not affect the function): code formatting, space adjustment, naming rules unity. +h. Refactor: Code Refactoring: Refactor existing code to improve maintainability. +i. Test: Add or modify tests: add unit tests, integration tests, or modify test logic. +j. Chore: Build Configuration, Dependency Management, CI/CD Configuration Updates. +k. Perf: Performance Optimisation: Optimising code execution efficiency or memory usage. +l. Ci: CI Configuration Related: Changing Continuous Integration Configurations for Github Actions, Travis, Jenkins, etc. +m. Build: build system related: modify build scripts, packaging configuration. +n. Revert: Rollback Commit: Undoing a Previous Commit +o. Security: Security fixes, fixing security vulnerabilities, updating dependencies to prevent attacks. +p. Deps: Dependency Management: Dependency Management/Adding, updating, and removing dependency libraries +q. Infra: Infrastructure related: changes to development environments, containers, server configurations, etc. ## Code of Conduct From 9143a2ca32c24f8a6e849d668accefc7749fa077 Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Tue, 19 Aug 2025 18:20:08 +1000 Subject: [PATCH 04/66] docs: Fixed the "pip" to "pipenv". --- docs/contributing/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index c29715ee..0227bc12 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -103,7 +103,7 @@ We use Black.exe to format the code. Install Black: ```bash -pip install black +pipenv install black ``` Format your code before committing: @@ -121,7 +121,7 @@ Before submitting a Pull Request, please make sure: ```bash npm install -pip install -r requirements.txt +pipenv install -r requirements.txt ``` From the root directory, run: From 560ceeded255717a4939b868dbad7ba1c63aafe9 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Tue, 19 Aug 2025 18:37:32 +1000 Subject: [PATCH 05/66] docs: Fixed the content of the Pull Request Regulation format --- docs/contributing/contributing.md | 47 ++++++++++++++++--------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 0227bc12..d85d8ccd 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -68,28 +68,31 @@ git push origin (your branch name) ``` ## Pull Request Regulation -1.Time to submit PR: -User requirements/issues have been addressed or discussed in Issue and consensus has been reached. -Changes have been minimised (small steps/phased submission) to avoid "mega PRs". - -2.Classification of Common PR Types: -a. Main (Master):Stable branch, merge code that passes review and CI; merge and release every time, -b. Develop: Continuous Integration branch for daily integration with multiple collaborators. -c. feature/*: feature development branch, cut out from main or develop, send PR to merge in after completing the feature. -d. Fix/*: defect repair branch, the same process as feature -e. Release/*: release preparation branch for freezing versions, fixing documentation, doing regressions and tagging. -f. docs/*, chore/*, refactor/*, test/*: documentation, miscellaneous, refactor, test type branches. -g. Style: style modification (does not affect the function): code formatting, space adjustment, naming rules unity. -h. Refactor: Code Refactoring: Refactor existing code to improve maintainability. -i. Test: Add or modify tests: add unit tests, integration tests, or modify test logic. -j. Chore: Build Configuration, Dependency Management, CI/CD Configuration Updates. -k. Perf: Performance Optimisation: Optimising code execution efficiency or memory usage. -l. Ci: CI Configuration Related: Changing Continuous Integration Configurations for Github Actions, Travis, Jenkins, etc. -m. Build: build system related: modify build scripts, packaging configuration. -n. Revert: Rollback Commit: Undoing a Previous Commit -o. Security: Security fixes, fixing security vulnerabilities, updating dependencies to prevent attacks. -p. Deps: Dependency Management: Dependency Management/Adding, updating, and removing dependency libraries -q. Infra: Infrastructure related: changes to development environments, containers, server configurations, etc. +**Time to submit PR:** + +- User requirements/issues have been addressed or discussed in Issue and consensus has been reached. +- Changes have been minimised (small steps/phased submission) to avoid "mega PRs". + +**Classification of Common PR Types:** + +- `Main (Master)`: Stable branch, merge code that passes review and CI; merge and release every time, + +- `Develop`: Continuous Integration branch for daily integration with multiple collaborators. +- `Feature/*`: feature development branch, cut out from main or develop, send PR to merge in after completing the feature. +- `Fix/*`: defect repair branch, the same process as feature +- `Release/*`: release preparation branch for freezing versions, fixing documentation, doing regressions and tagging. +- `docs/*`, `chore/*`, `refactor/*`, `test/*`: documentation, miscellaneous, refactor, test type branches. +- `Style`: style modification (does not affect the function): code formatting, space adjustment, naming rules unity. +- `Refactor`: Code Refactoring: Refactor existing code to improve maintainability. +- `Test`: Add or modify tests: add unit tests, integration tests, or modify test logic. +- `Chore`: Build Configuration, Dependency Management, CI/CD Configuration Updates. +- `Perf`: Performance Optimisation: Optimising code execution efficiency or memory usage. +- `Ci`: CI Configuration Related: Changing Continuous Integration Configurations for Github Actions, Travis, Jenkins, etc. +- `Build`: build system related: modify build scripts, packaging configuration. +- `Revert`: Rollback Commit: Undoing a Previous Commit +- `Security`: Security fixes, fixing security vulnerabilities, updating dependencies to prevent attacks. +- `Deps`: Dependency Management: Dependency Management/Adding, updating, and removing dependency libraries +- `Infra`: Infrastructure related: changes to development environments, containers, server configurations, etc. ## Code of Conduct From 88dedfe4c1d24f50e16cd92b30fab46c5783695e Mon Sep 17 00:00:00 2001 From: FengW01 Date: Wed, 20 Aug 2025 14:33:48 +1000 Subject: [PATCH 06/66] docs(contributing): update introduction section of How to contribute guide --- docs/contributing/contributing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 0227bc12..7eef07ef 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -5,6 +5,8 @@ description: Guide on how to contribute to this project # How to contribute First off, thanks for taking the time to contribute! +We use GitHub as our main collaboration platform. Please work from the `development` branch, create small feature branches, and open focused pull requests. Follow Conventional Commit messages (e.g., `feat:`, `fix:`, `docs:`), format Python code with Black, and add tests where needed. Never merge your own PR—wait for review and address all comments (including AI reviewer suggestions). Use Issues and Projects to track tasks and discussions. + ## General Feedback From 2fa3a606e0968c6dc7290eb117269d516d2265ec Mon Sep 17 00:00:00 2001 From: FengW01 Date: Wed, 20 Aug 2025 16:01:16 +1000 Subject: [PATCH 07/66] docs(contributing): update introduction section of How to contribute guide --- docs/contributing/contributing.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index d6de72eb..68413525 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -147,6 +147,10 @@ $ git commit -m "A brief summary of the commit > A paragraph describing what changed and its impact." ``` +## Hint +This project requires Python 3.11. Do not use Python 3.12 or newer, as it may cause dependency incompatibilities, build failure or runtime errors + + ## Thanks Thank you again for being interested in this project! You are awesome! From 48edb0e63473479ca78b363749a5e001eb133bbd Mon Sep 17 00:00:00 2001 From: FengW01 Date: Wed, 20 Aug 2025 16:11:08 +1000 Subject: [PATCH 08/66] docs: update contributing guide --- docs/contributing/contributing.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 68413525..b759057c 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -7,6 +7,8 @@ description: Guide on how to contribute to this project First off, thanks for taking the time to contribute! We use GitHub as our main collaboration platform. Please work from the `development` branch, create small feature branches, and open focused pull requests. Follow Conventional Commit messages (e.g., `feat:`, `fix:`, `docs:`), format Python code with Black, and add tests where needed. Never merge your own PR—wait for review and address all comments (including AI reviewer suggestions). Use Issues and Projects to track tasks and discussions. +> This project requires Python 3.11. Do not use Python 3.12 or newer, as it may cause dependency incompatibilities, build failure or runtime errors + ## General Feedback @@ -147,9 +149,6 @@ $ git commit -m "A brief summary of the commit > A paragraph describing what changed and its impact." ``` -## Hint -This project requires Python 3.11. Do not use Python 3.12 or newer, as it may cause dependency incompatibilities, build failure or runtime errors - ## Thanks From 730e00a082b0d8304b9b1d901f8eca32d2c98a83 Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Wed, 20 Aug 2025 17:10:06 +1000 Subject: [PATCH 09/66] docs: A strategy module has been added to the Pull Request section, along with examples of code modifications. --- docs/contributing/contributing.md | 187 ++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index b759057c..af6c1a16 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -98,6 +98,193 @@ git push origin (your branch name) - `Deps`: Dependency Management: Dependency Management/Adding, updating, and removing dependency libraries - `Infra`: Infrastructure related: changes to development environments, containers, server configurations, etc. +**Pull Request Strategy** +- `Constant centralization`: Added `ElementIds/ComponentProperty/ClassNames` to `components.py`. Introduce `ElementIds/ComponentProperty` for all UI IDs, replace magic strings with constants. This enables single-source-of-truth and prevents typos. No business logic changed. Verified by running callback smoke tests and snapshot tests. This is an example of modifications made in the `t_rh.py` file. + +`Orginal t_rh.py file(extract)` +```python +@callback( + Output("yearly-chart", "children"), + [ + Input("df-store", "modified_timestamp"), + Input("global-local-radio-input", "value"), + Input("dropdown", "value"), + ], + [ + State("df-store", "data"), + State("meta-store", "data"), + State("si-ip-unit-store", "data"), + ], +) +``` +`Component.py: store repeated string` +```python +class ClassNames: + CONTAINER_COL = "container-col" + CONTAINER_COL_FULL_WIDTH = " container-col full-width" + TEXT_NEXT_TO_INPUT = "text-next-to-input" + + +class ElementIds: + DAILY = "daily" + DF_STORE = "df-store" + DROPDOWN = "dropdown" + HEATMAP = "heatmap" + GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + META_STORE = "meta-store" + SI_IP_UNIT_STORE = "si-ip-unit-store" + TABLE_TMP_HUM = "table-tmp-hum" + YEARLY_CHART = "yearly-chart" + + +class IdButtons: + DAILY_CHART_LABEL = "daily-chart-label" + + +class ComponentProperty: + CHILDREN = "children" + DATA = "data" + MODIFIED_TIMESTAMP = "modified_timestamp" + VALUE = "value" + + +class Text: + DAILY_CHART = "Daily chart" + YEARLY_CHART = "Yearly_chart" + HEATMAP_CHART = "Heatmap chart" + DESCRIPTIVE_STATISTICS = "Descriptive statistics" + + +class Type: + CIRCLE = "circle" +``` +`fixed t_rh.py(extract)` +```python +from components import ElementIds, ClassNames, Text, IdButtons, Type, ComponentProperty +@callback( + Output(ElementIds.YEARLY_CHART, ComponentProperty.CHILDREN), + [ + Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), + Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), + ], + [ + State(ElementIds.DF_STORE, ComponentProperty.DATA), + State(ElementIds.META_STORE, ComponentProperty.DATA), + State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), + ], +) +``` +- `Time field/timestamp harmonisation`: Unify time periods (such as hour, month) to `Colnames.hour` or `Colnames.month`. Added the `column_names.py` helper function and replaced the temporary parsing logic. Backward compatibility with old strings through a fault-tolerant parser; added round-trip testing in a multi-time zone environment. Below are three examples. + +`Create a new class to fix the time field` +```python +from enum import Enum + +class ColNames(str, Enum): + """ + DataFrame column name enumeration class, + avoiding hard-coded strings + """ + # Time-related columns + YEAR = "year" + MONTH = "month" + DAY = "day" + HOUR = "hour" + MINUTE = "minute" + + # Weather data column + DBT = "DBT" # dry bulb temperature + DPT = "DPT" # dew point temperature + RH = "RH" # relative humidity + P_ATM = "p_atm" # atmospheric pressure + + # Radiation-related column + EXTR_HOR_RAD = "extr_hor_rad" # Extraterrestrial Horizontal Radiation + HOR_IR_RAD = "hor_ir_rad" # Horizontal Infrared Radiation + GLOB_HOR_RAD = "glob_hor_rad" # Global Horizontal Radiation + DIR_NOR_RAD = "dir_nor_rad" # Direct Normal Radiation + DIF_HOR_RAD = "dif_hor_rad" # Diffuse Horizontal Radiation + + # Lighting-related columns + GLOB_HOR_ILL = "glob_hor_ill" # Global Horizontal Illuminance + DIR_NOR_ILL = "dir_nor_ill" # Direct Normal Illuminance + DIF_HOR_ILL = "dif_hor_ill" # Diffuse Horizontal Illuminance + + # Others + ZLUMI = "Zlumi" # Luminance + WIND_DIR = "wind_dir" # Wind Direction + WIND_SPEED = "wind_speed" # Wind Speed + TOT_SKY_COVER = "tot_sky_cover" # Total Sky Cover + OSKYCOVER = "Oskycover" # Opaque Sky Cover + VIS = "Vis" # Visibility + CHEIGHT = "Cheight" # Cloud Height + PWobs = "PWobs" # Precipitation Observation + PWcodes = "PWcodes" # Precipitation Codes + Pwater = "Pwater" # Precipitation Water + AsolOptD = "AsolOptD" # Aerosol Optical Depth + SnowD = "SnowD" # Snow Depth + DaySSnow = "DaySSnow" # Daily Snow + + # Calculation column + FAKE_YEAR = "fake_year" + MONTH_NAMES = "month_names" + UTC_TIME = "UTC_time" + DOY = "DOY" + ``` +`Example1` +```python +# Time filtering +# Before reconstruction +def violin(df, var, global_local, si_ip): + """Return day night violin based on the 'var' col""" + mask_day = (df["hour"] >= 8) & (df["hour"] < 20) + mask_night = (df["hour"] < 8) | (df["hour"] >= 20) + +# After reconstruction +from .column_names import ColNames + +def violin(df, var, global_local, si_ip): + """Return day night violin based on the 'var' col""" + mask_day = (df[ColNames.HOUR] >= 8) & (df[ColNames.HOUR] < 20) + mask_night = (df[ColNames.HOUR] < 8) | (df[ColNames.HOUR] >= 20) +``` +`Example2` +```python +# DataFrame grouping operations +# Before reconstruction +def daily_profile(df, var, global_local, si_ip): + var_month_ave = df.groupby(["month", "hour"])[var].median().reset_index() + + for i in range(12): + fig.add_trace( + go.Scatter( + x=df.loc[df["month"] == i + 1, "hour"], + y=df.loc[df["month"] == i + 1, var], + # ... other props + ) + ) + +# After reconstruction +from .column_names import ColNames + +def daily_profile(df, var, global_local, si_ip): + var_month_ave = df.groupby([ColNames.MONTH, ColNames.HOUR])[var] + .median() + .reset_index() + + for i in range(12): + fig.add_trace( + go.Scatter( + x=df.loc[df[ColNames.MONTH] == i + 1, ColNames.HOUR], + y=df.loc[df[ColNames.MONTH] == i + 1, var], + # ... other props + ) + ) +``` + +- `Progressive replacement/grey scale release`: Phase 1: Add constants; Phase 2: Batch replace references; Phase 3: Remove legacy code. The `FEATURE_USE_CONSTANT_IDS` feature switch allows for instant rollback. + ## Code of Conduct From 79db946c263aeb78b0495b930e7f07598d821231 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 23 Aug 2025 14:28:32 +1000 Subject: [PATCH 10/66] feat: add Global Column Names class to replace string --- pages/lib/global_column_names.py | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 pages/lib/global_column_names.py diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py new file mode 100644 index 00000000..f7e2c180 --- /dev/null +++ b/pages/lib/global_column_names.py @@ -0,0 +1,48 @@ +from enum import Enum + +class ColNames(str, Enum): + # ==================== Time related column ==================== + YEAR = "year" # year + MONTH = "month" # month + DAY = "day" # day + HOUR = "hour" # hour + MINUTE = "minute" # minute + + # ==================== Meteorological data column ==================== + DBT = "DBT" # Dry Bulb Temperature + DPT = "DPT" # Dew Point Temperature + RH = "RH" # Relative Humidity + P_ATM = "p_atm" # Atmospheric Pressure + + # ==================== Radiation-related column ==================== + EXTR_HOR_RAD = "extr_hor_rad" # Extraterrestrial Horizontal Radiation + HOR_IR_RAD = "hor_ir_rad" # Horizontal Infrared Radiation + GLOB_HOR_RAD = "glob_hor_rad" # Global Horizontal Radiation + DIR_NOR_RAD = "dir_nor_rad" # Direct Normal Radiation + DIF_HOR_RAD = "dif_hor_rad" # Diffuse Horizontal Radiation + + # ==================== Lighting-related columns ==================== + GLOB_HOR_ILL = "glob_hor_ill" # Global Horizontal Illuminance + DIR_NOR_ILL = "dir_nor_ill" # Direct Normal Illuminance + DIF_HOR_ILL = "dif_hor_ill" # Diffuse Horizontal Illuminance + + # ==================== Other ==================== + ZLUMI = "Zlumi" # Luminance + WIND_DIR = "wind_dir" # Wind Direction + WIND_SPEED = "wind_speed" # Wind Speed + TOT_SKY_COVER = "tot_sky_cover" # Total Sky Cover + OSKYCOVER = "Oskycover" # Opaque Sky Cover + VIS = "Vis" # Visibility + CHEIGHT = "Cheight" # Cloud Height + PWobs = "PWobs" # Precipitation Observation + PWcodes = "PWcodes" # Precipitation Codes + Pwater = "Pwater" # Precipitation Water + AsolOptD = "AsolOptD" # Aerosol Optical Depth + SnowD = "SnowD" # Snow Depth + DaySSnow = "DaySSnow" # Daily Snow + + # ==================== Calculation column ==================== + FAKE_YEAR = "fake_year" # Fake Year + MONTH_NAMES = "month_names" # Month names + UTC_TIME = "UTC_time" # UTC Time + DOY = "DOY" # Day of Year \ No newline at end of file From 01e95a61196ae81932d21a090b100641faa1cf24 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Sat, 23 Aug 2025 14:30:50 +1000 Subject: [PATCH 11/66] code sanisitation(First version) --- pages/components.py | 33 +++++++++++++++++++ pages/t_rh.py | 78 ++++++++++++++++++++++----------------------- 2 files changed, 72 insertions(+), 39 deletions(-) create mode 100644 pages/components.py diff --git a/pages/components.py b/pages/components.py new file mode 100644 index 00000000..685de21e --- /dev/null +++ b/pages/components.py @@ -0,0 +1,33 @@ + + + +class ElementIds: + DAILY = "daily" + DF_STORE = "df-store" + DROPDOWN = "dropdown" + HEATMAP = "heatmap" + GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + META_STORE = "meta-store" + SI_IP_UNIT_STORE = "si-ip-unit-store" + TABLE_TMP_HUM = "table-tmp-hum" + YEARLY_CHART = "yearly-chart" + + +class IdButtons: + DAILY_CHART_LABEL = "daily-chart-label" + + +class ComponentProperty: + CHILDREN = "children" + DATA = "data" + MODIFIED_TIMESTAMP = "modified_timestamp" + VALUE = "value" + + +class Text: + DAILY_CHART = "Daily chart" + YEARLY_CHART = "Yearly_chart" + HEATMAP_CHART = "Heatmap chart" + DESCRIPTIVE_STATISTICS = "Descriptive statistics" +class Type: + CIRCLE = "circle" diff --git a/pages/t_rh.py b/pages/t_rh.py index c3d90f82..c3f04dfb 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -1,6 +1,6 @@ import dash from dash_extensions.enrich import Output, Input, State, dcc, html, callback - +from pages.components import ElementIds, Text, IdButtons, Type, ComponentProperty from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import dropdown_names from pages.lib.template_graphs import heatmap, yearly_profile, daily_profile @@ -37,7 +37,7 @@ def layout(): className="text-next-to-input", children=["Select a variable: "] ), dropdown( - id="dropdown", + id=ElementIds.DROPDOWN, className="dropdown-t-rh", options={var: dropdown_names[var] for var in var_to_plot}, value=dropdown_names[var_to_plot[0]], @@ -49,46 +49,46 @@ def layout(): children=[ html.Div( children=title_with_link( - text="Yearly chart", + text=Text.YEARLY_CHART, id_button="yearly-chart-label", doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( - type="circle", - children=html.Div(id="yearly-chart"), + type=Type.CIRCLE, + children=html.Div(id=ElementIds.YEARLY_CHART), ), html.Div( children=title_with_link( - text="Daily chart", - id_button="daily-chart-label", + text=Text.DAILY_CHART, + id_button=IdButtons.DAILY_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( - type="circle", - children=html.Div(id="daily"), + type=Type.CIRCLE, + children=html.Div(id=ElementIds.DAILY), ), html.Div( children=title_with_link( - text="Heatmap chart", + text=Text.HEATMAP_CHART, id_button="heatmap-chart-label", doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( - type="circle", - children=html.Div(id="heatmap"), + type=Type.CIRCLE, + children=html.Div(id=ElementIds.HEATMAP), ), html.Div( children=title_with_tooltip( - text="Descriptive statistics", + text=Text.DESCRIPTIVE_STATISTICS, tooltip_text="count, mean, std, min, max, and percentiles", id_button="table-tmp-rh", ), ), html.Div( - id="table-tmp-hum", + id=ElementIds.TABLE_TMP_HUM, ), ], ), @@ -97,16 +97,16 @@ def layout(): @callback( - Output("yearly-chart", "children"), + Output(ElementIds.YEARLY_CHART, ComponentProperty.CHILDREN), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), - Input("dropdown", "value"), + Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), + Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.DF_STORE, ComponentProperty.DATA), + State(ElementIds.META_STORE, ComponentProperty.DATA), + State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), ], ) def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): @@ -129,16 +129,16 @@ def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): @callback( - Output("daily", "children"), + Output(ElementIds.DAILY, ComponentProperty.CHILDREN), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), - Input("dropdown", "value"), + Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), + Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.DF_STORE, ComponentProperty.DATA), + State(ElementIds.META_STORE, ComponentProperty.DATA), + State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), ], ) def update_daily(_, global_local, dd_value, df, meta, si_ip): @@ -167,16 +167,16 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): @callback( - [Output("heatmap", "children")], + [Output(ElementIds.HEATMAP, ComponentProperty.CHILDREN)], [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), - Input("dropdown", "value"), + Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), + Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.DF_STORE, ComponentProperty.DATA), + State(ElementIds.META_STORE, ComponentProperty.DATA), + State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), ], ) def update_heatmap(_, global_local, dd_value, df, meta, si_ip): @@ -206,12 +206,12 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): @callback( - Output("table-tmp-hum", "children"), + Output(ElementIds.TABLE_TMP_HUM, ComponentProperty.CHILDREN), [ - Input("df-store", "modified_timestamp"), - Input("dropdown", "value"), + Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), + Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), ], - [State("df-store", "data"), State("si-ip-unit-store", "data")], + [State(ElementIds.DF_STORE, ComponentProperty.DATA), State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA)], ) def update_table(_, dd_value, df, si_ip): """Update the contents of tab three. Passing in general info (df, meta).""" From 0b20c6a997067e3285e24f979994ba7e361fa13c Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 23 Aug 2025 14:41:31 +1000 Subject: [PATCH 12/66] fix: updated the comment --- pages/lib/global_column_names.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index f7e2c180..62a7a63d 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -26,7 +26,7 @@ class ColNames(str, Enum): DIR_NOR_ILL = "dir_nor_ill" # Direct Normal Illuminance DIF_HOR_ILL = "dif_hor_ill" # Diffuse Horizontal Illuminance - # ==================== Other ==================== + # ==================== Other columns ==================== ZLUMI = "Zlumi" # Luminance WIND_DIR = "wind_dir" # Wind Direction WIND_SPEED = "wind_speed" # Wind Speed From 84bb3ed0d7bee7ac39c0f314593c4b94911b256c Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Sat, 23 Aug 2025 15:06:07 +1000 Subject: [PATCH 13/66] code sanisitation(Version 2) --- pages/components.py | 33 ---------- pages/lib/global_column_names.py | 105 +++++++++++++++++++------------ pages/t_rh.py | 12 ++-- 3 files changed, 72 insertions(+), 78 deletions(-) delete mode 100644 pages/components.py diff --git a/pages/components.py b/pages/components.py deleted file mode 100644 index 685de21e..00000000 --- a/pages/components.py +++ /dev/null @@ -1,33 +0,0 @@ - - - -class ElementIds: - DAILY = "daily" - DF_STORE = "df-store" - DROPDOWN = "dropdown" - HEATMAP = "heatmap" - GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - META_STORE = "meta-store" - SI_IP_UNIT_STORE = "si-ip-unit-store" - TABLE_TMP_HUM = "table-tmp-hum" - YEARLY_CHART = "yearly-chart" - - -class IdButtons: - DAILY_CHART_LABEL = "daily-chart-label" - - -class ComponentProperty: - CHILDREN = "children" - DATA = "data" - MODIFIED_TIMESTAMP = "modified_timestamp" - VALUE = "value" - - -class Text: - DAILY_CHART = "Daily chart" - YEARLY_CHART = "Yearly_chart" - HEATMAP_CHART = "Heatmap chart" - DESCRIPTIVE_STATISTICS = "Descriptive statistics" -class Type: - CIRCLE = "circle" diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 62a7a63d..35a3797e 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -1,48 +1,75 @@ from enum import Enum + class ColNames(str, Enum): # ==================== Time related column ==================== - YEAR = "year" # year - MONTH = "month" # month - DAY = "day" # day - HOUR = "hour" # hour - MINUTE = "minute" # minute - + YEAR = "year" # year + MONTH = "month" # month + DAY = "day" # day + HOUR = "hour" # hour + MINUTE = "minute" # minute + # ==================== Meteorological data column ==================== - DBT = "DBT" # Dry Bulb Temperature - DPT = "DPT" # Dew Point Temperature - RH = "RH" # Relative Humidity - P_ATM = "p_atm" # Atmospheric Pressure - + DBT = "DBT" # Dry Bulb Temperature + DPT = "DPT" # Dew Point Temperature + RH = "RH" # Relative Humidity + P_ATM = "p_atm" # Atmospheric Pressure + # ==================== Radiation-related column ==================== - EXTR_HOR_RAD = "extr_hor_rad" # Extraterrestrial Horizontal Radiation - HOR_IR_RAD = "hor_ir_rad" # Horizontal Infrared Radiation - GLOB_HOR_RAD = "glob_hor_rad" # Global Horizontal Radiation - DIR_NOR_RAD = "dir_nor_rad" # Direct Normal Radiation - DIF_HOR_RAD = "dif_hor_rad" # Diffuse Horizontal Radiation - + EXTR_HOR_RAD = "extr_hor_rad" # Extraterrestrial Horizontal Radiation + HOR_IR_RAD = "hor_ir_rad" # Horizontal Infrared Radiation + GLOB_HOR_RAD = "glob_hor_rad" # Global Horizontal Radiation + DIR_NOR_RAD = "dir_nor_rad" # Direct Normal Radiation + DIF_HOR_RAD = "dif_hor_rad" # Diffuse Horizontal Radiation + # ==================== Lighting-related columns ==================== - GLOB_HOR_ILL = "glob_hor_ill" # Global Horizontal Illuminance - DIR_NOR_ILL = "dir_nor_ill" # Direct Normal Illuminance - DIF_HOR_ILL = "dif_hor_ill" # Diffuse Horizontal Illuminance - + GLOB_HOR_ILL = "glob_hor_ill" # Global Horizontal Illuminance + DIR_NOR_ILL = "dir_nor_ill" # Direct Normal Illuminance + DIF_HOR_ILL = "dif_hor_ill" # Diffuse Horizontal Illuminance + # ==================== Other columns ==================== - ZLUMI = "Zlumi" # Luminance - WIND_DIR = "wind_dir" # Wind Direction - WIND_SPEED = "wind_speed" # Wind Speed - TOT_SKY_COVER = "tot_sky_cover" # Total Sky Cover - OSKYCOVER = "Oskycover" # Opaque Sky Cover - VIS = "Vis" # Visibility - CHEIGHT = "Cheight" # Cloud Height - PWobs = "PWobs" # Precipitation Observation - PWcodes = "PWcodes" # Precipitation Codes - Pwater = "Pwater" # Precipitation Water - AsolOptD = "AsolOptD" # Aerosol Optical Depth - SnowD = "SnowD" # Snow Depth - DaySSnow = "DaySSnow" # Daily Snow - + ZLUMI = "Zlumi" # Luminance + WIND_DIR = "wind_dir" # Wind Direction + WIND_SPEED = "wind_speed" # Wind Speed + TOT_SKY_COVER = "tot_sky_cover" # Total Sky Cover + OSKYCOVER = "Oskycover" # Opaque Sky Cover + VIS = "Vis" # Visibility + CHEIGHT = "Cheight" # Cloud Height + PWobs = "PWobs" # Precipitation Observation + PWcodes = "PWcodes" # Precipitation Codes + Pwater = "Pwater" # Precipitation Water + AsolOptD = "AsolOptD" # Aerosol Optical Depth + SnowD = "SnowD" # Snow Depth + DaySSnow = "DaySSnow" # Daily Snow + # ==================== Calculation column ==================== - FAKE_YEAR = "fake_year" # Fake Year - MONTH_NAMES = "month_names" # Month names - UTC_TIME = "UTC_time" # UTC Time - DOY = "DOY" # Day of Year \ No newline at end of file + FAKE_YEAR = "fake_year" # Fake Year + MONTH_NAMES = "month_names" # Month names + UTC_TIME = "UTC_time" # UTC Time + DOY = "DOY" # Day of Year + + +class ElementIds: + # ==================== Defines the unique ID constant for each element in the front-end page ==================== + DAILY = "daily" + DF_STORE = "df-store" + DROPDOWN = "dropdown" + HEATMAP = "heatmap" + GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + META_STORE = "meta-store" + SI_IP_UNIT_STORE = "si-ip-unit-store" + TABLE_TMP_HUM = "table-tmp-hum" + YEARLY_CHART = "yearly-chart" + + +class ComponentProperty: + # ==================== Define common attribute name constants for components ==================== + CHILDREN = "children" + DATA = "data" + MODIFIED_TIMESTAMP = "modified_timestamp" + VALUE = "value" + + +class Type: + # ==================== Defines type constants available for UI components ==================== + CIRCLE = "circle" diff --git a/pages/t_rh.py b/pages/t_rh.py index c3f04dfb..0a6477fe 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -1,6 +1,6 @@ import dash from dash_extensions.enrich import Output, Input, State, dcc, html, callback -from pages.components import ElementIds, Text, IdButtons, Type, ComponentProperty +from lib.global_column_names import ElementIds, Type, ComponentProperty from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import dropdown_names from pages.lib.template_graphs import heatmap, yearly_profile, daily_profile @@ -49,7 +49,7 @@ def layout(): children=[ html.Div( children=title_with_link( - text=Text.YEARLY_CHART, + text="Yearly_chart", id_button="yearly-chart-label", doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), @@ -60,8 +60,8 @@ def layout(): ), html.Div( children=title_with_link( - text=Text.DAILY_CHART, - id_button=IdButtons.DAILY_CHART_LABEL, + text="Daily chart", + id_button="daily-chart-label", doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), @@ -71,7 +71,7 @@ def layout(): ), html.Div( children=title_with_link( - text=Text.HEATMAP_CHART, + text="Heatmap chart", id_button="heatmap-chart-label", doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), @@ -82,7 +82,7 @@ def layout(): ), html.Div( children=title_with_tooltip( - text=Text.DESCRIPTIVE_STATISTICS, + text="Descriptive statistics", tooltip_text="count, mean, std, min, max, and percentiles", id_button="table-tmp-rh", ), From 825febb473c7d7154ca9c1765b03a77aa1a6665f Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 23 Aug 2025 15:28:53 +1000 Subject: [PATCH 14/66] fix: replaced the string that using Class ColNames --- pages/t_rh.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pages/t_rh.py b/pages/t_rh.py index 0a6477fe..400c99f4 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -1,9 +1,9 @@ import dash from dash_extensions.enrich import Output, Input, State, dcc, html, callback -from lib.global_column_names import ElementIds, Type, ComponentProperty from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import dropdown_names from pages.lib.template_graphs import heatmap, yearly_profile, daily_profile +from pages.lib.global_column_names import ElementIds, Type, ComponentProperty, ColNames from pages.lib.utils import ( generate_chart_name, generate_units, @@ -111,7 +111,7 @@ def layout(): ) def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): if dd_value == dropdown_names[var_to_plot[0]]: - dbt_yearly = yearly_profile(df, "DBT", global_local, si_ip) + dbt_yearly = yearly_profile(df, ColNames.DBT, global_local, si_ip) dbt_yearly.update_layout(xaxis=dict(rangeslider=dict(visible=True))) units = generate_units_degree(si_ip) return dcc.Graph( @@ -119,7 +119,7 @@ def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): figure=dbt_yearly, ) else: - rh_yearly = yearly_profile(df, "RH", global_local, si_ip) + rh_yearly = yearly_profile(df, ColNames.RH, global_local, si_ip) rh_yearly.update_layout(xaxis=dict(rangeslider=dict(visible=True))) units = generate_units(si_ip) return dcc.Graph( @@ -147,8 +147,8 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("DryBulbTemperature_daily", meta, units), figure=daily_profile( - df[["DBT", "hour", "UTC_time", "month_names", "day", "month"]], - "DBT", + df[[ColNames.DBT, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY, ColNames.MONTH]], + ColNames.DBT, global_local, si_ip, ), @@ -158,8 +158,8 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("RelativeHumidity_daily", meta, units), figure=daily_profile( - df[["RH", "hour", "UTC_time", "month_names", "day", "month"]], - "RH", + df[[ColNames.RH, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY, ColNames.MONTH]], + ColNames.RH, global_local, si_ip, ), @@ -186,8 +186,8 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("DryBulbTemperature_heatmap", meta, units), figure=heatmap( - df[["DBT", "hour", "UTC_time", "month_names", "day"]], - "DBT", + df[[ColNames.DBT, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY]], + ColNames.DBT, global_local, si_ip, ), @@ -197,8 +197,8 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("RelativeHumidity_heatmap", meta, units), figure=heatmap( - df[["RH", "hour", "UTC_time", "month_names", "day"]], - "RH", + df[[ColNames.RH, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY]], + ColNames.RH, global_local, si_ip, ), @@ -216,5 +216,5 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): def update_table(_, dd_value, df, si_ip): """Update the contents of tab three. Passing in general info (df, meta).""" return summary_table_tmp_rh_tab( - df[["month", "hour", dd_value, "month_names"]], dd_value, si_ip + df[[ColNames.MONTH, ColNames.HOUR, dd_value, ColNames.MONTH_NAMES]], dd_value, si_ip ) From b4a31daa33744fc8519833733ff74362330874e5 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 23 Aug 2025 16:05:35 +1000 Subject: [PATCH 15/66] fix: replaced the string that using Class ColNames in natural_ventilation.py --- pages/natural_ventilation.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 32c62cdb..7dcfe8fa 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -18,6 +18,7 @@ container_col_center_one_of_three, ) from pages.lib.template_graphs import filter_df_by_month_and_hour +from pages.lib.global_column_names import ColNames from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -326,8 +327,8 @@ def nv_heatmap( month, hour, invert_month, invert_hour ) - var = "DBT" - filter_var = "DPT" + var = ColNames.DBT + filter_var = ColNames.DPT if dbt_data_filter and (min_dbt_val <= max_dbt_val): df.loc[(df[var] < min_dbt_val) | (df[var] > max_dbt_val), var] = None @@ -335,7 +336,7 @@ def nv_heatmap( if dpt_data_filter: df.loc[(df[filter_var] < -200) | (df[filter_var] > max_dpt_val), var] = None - if df.dropna(subset=["month"]).shape[0] == 0: + if df.dropna(subset=[ColNames.MONTH]).shape[0] == 0: return ( dbc.Alert( "Natural ventilation is not available in this location under these" @@ -386,15 +387,15 @@ def nv_heatmap( fig = go.Figure( data=go.Heatmap( - y=df["hour"] - 0.5, # Offset by 0.5 to center the hour labels - x=df["UTC_time"].dt.date, + y=df[ColNames.HOUR] - 0.5, # Offset by 0.5 to center the hour labels + x=df[ColNames.UTC_TIME].dt.date, z=df[var], colorscale=var_color, zmin=range_z[0], zmax=range_z[1], connectgaps=False, hoverongaps=False, - customdata=np.stack((df["month_names"], df["day"]), axis=-1), + customdata=np.stack((df[ColNames.MONTH_NAMES], df[ColNames.DAY]), axis=-1), hovertemplate=( "" + var @@ -512,7 +513,7 @@ def nv_bar_chart( ) # this should be the total after filtering by time - tot_month_hours = df.groupby(df["UTC_time"].dt.month)["nv_allowed"].sum().values + tot_month_hours = df.groupby(df[ColNames.UTC_TIME].dt.month)["nv_allowed"].sum().values if dbt_data_filter and (min_dbt_val <= max_dbt_val): df.loc[(df[var] < min_dbt_val) | (df[var] > max_dbt_val), "nv_allowed"] = 0 @@ -522,7 +523,7 @@ def nv_bar_chart( n_hours_nv_allowed = ( df.dropna(subset="nv_allowed") - .groupby(df["UTC_time"].dt.month)["nv_allowed"] + .groupby(df[ColNames.UTC_TIME].dt.month)["nv_allowed"] .sum() .values ) @@ -532,7 +533,7 @@ def nv_bar_chart( if len(normalize) == 0: fig = go.Figure( go.Bar( - x=df["month_names"].unique(), + x=df[ColNames.MONTH_NAMES].unique(), y=n_hours_nv_allowed, name="", marker_color=color_in, @@ -554,7 +555,7 @@ def nv_bar_chart( else: trace1 = go.Bar( - x=df["month_names"].unique(), + x=df[ColNames.MONTH_NAMES].unique(), y=per_time_nv_allowed, name="", marker_color=color_in, From d2f413921f4d4d3aac53cdb405be1105ac9b3b8e Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 23 Aug 2025 16:32:05 +1000 Subject: [PATCH 16/66] fix: replaced the string that using Class ColNames in extract_df.py and template_graphs.py --- pages/lib/extract_df.py | 35 ++++++------ pages/lib/template_graphs.py | 100 +++++++++++++++++------------------ 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/pages/lib/extract_df.py b/pages/lib/extract_df.py index a0def908..c4c9a3b0 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -18,6 +18,7 @@ from pages.lib.global_scheme import month_lst from pages.lib.utils import code_timer +from pages.lib.global_column_names import ColNames @code_timer @@ -148,7 +149,7 @@ def create_df(lst, file_name): # from EnergyPlus files extract info about reference years if not location_info["period"]: - years = epw_df["year"].astype("int").unique() + years = epw_df[ColNames.YEAR].astype("int").unique() if len(years) == 1: year_rounded_up = int(math.ceil(years[0] / 10.0)) * 10 location_info["period"] = f"{year_rounded_up - 10}-{year_rounded_up}" @@ -158,22 +159,22 @@ def create_df(lst, file_name): location_info["period"] = f"{min_year}-{max_year}" # Add fake_year - epw_df["fake_year"] = "year" + epw_df[ColNames.FAKE_YEAR] = ColNames.YEAR # Add in month names month_look_up = {ix + 1: month for ix, month in enumerate(month_lst)} - epw_df["month_names"] = epw_df["month"].astype("int").map(month_look_up) + epw_df[ColNames.MONTH_NAMES] = epw_df[ColNames.MONTH].astype("int").map(month_look_up) # Change to int type - epw_df[["year", "day", "month", "hour"]] = epw_df[ - ["year", "day", "month", "hour"] + epw_df[[ColNames.YEAR, ColNames.DAY, ColNames.MONTH, ColNames.HOUR]] = epw_df[ + [ColNames.YEAR, ColNames.DAY, ColNames.MONTH, ColNames.HOUR] ].astype(int) # Add in DOY - df_doy = epw_df.groupby(["month", "day"])["hour"].count().reset_index() - df_doy["DOY"] = df_doy.index + 1 + df_doy = epw_df.groupby([ColNames.MONTH, ColNames.DAY])[ColNames.HOUR].count().reset_index() + df_doy[ColNames.DOY] = df_doy.index + 1 epw_df = pd.merge( - epw_df, df_doy[["month", "day", "DOY"]], on=["month", "day"], how="left" + epw_df, df_doy[[ColNames.MONTH, ColNames.DAY, ColNames.DOY]], on=[ColNames.MONTH, ColNames.DAY], how="left" ) change_to_float = [ @@ -209,7 +210,7 @@ def create_df(lst, file_name): times = pd.date_range( "2019-01-01 00:00:00", "2020-01-01", inclusive="left", freq="h", tz="UTC" ) - epw_df["UTC_time"] = pd.to_datetime(times) + epw_df[ColNames.UTC_TIME] = pd.to_datetime(times) delta = timedelta(days=0, hours=location_info["time_zone"] - 1, minutes=0) times = times - delta epw_df["times"] = times @@ -251,8 +252,8 @@ def create_df(lst, file_name): epw_df = epw_df.join(mrt_df) - epw_df["MRT"] = epw_df["delta_mrt"] + epw_df["DBT"] - epw_df["wind_speed_utci"] = epw_df["wind_speed"] + epw_df["MRT"] = epw_df["delta_mrt"] + epw_df[ColNames.DBT] + epw_df["wind_speed_utci"] = epw_df[ColNames.WIND_SPEED] epw_df["wind_speed_utci"] = epw_df["wind_speed_utci"].mask( epw_df["wind_speed_utci"] >= 17, 16.9 ) @@ -263,16 +264,16 @@ def create_df(lst, file_name): epw_df["wind_speed_utci"] >= 0, 0.5 ) epw_df["utci_noSun_Wind"] = utci( - epw_df["DBT"], epw_df["DBT"], epw_df["wind_speed_utci"], epw_df["RH"] + epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df["wind_speed_utci"], epw_df[ColNames.RH] ) epw_df["utci_noSun_noWind"] = utci( - epw_df["DBT"], epw_df["DBT"], epw_df["wind_speed_utci_0"], epw_df["RH"] + epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df["wind_speed_utci_0"], epw_df[ColNames.RH] ) epw_df["utci_Sun_Wind"] = utci( - epw_df["DBT"], epw_df["MRT"], epw_df["wind_speed_utci"], epw_df["RH"] + epw_df[ColNames.DBT], epw_df["MRT"], epw_df["wind_speed_utci"], epw_df[ColNames.RH] ) epw_df["utci_Sun_noWind"] = utci( - epw_df["DBT"], epw_df["MRT"], epw_df["wind_speed_utci_0"], epw_df["RH"] + epw_df[ColNames.DBT], epw_df["MRT"], epw_df["wind_speed_utci_0"], epw_df[ColNames.RH] ) utci_bins = [-999, -40, -27, -13, 0, 9, 26, 32, 38, 46, 999] @@ -291,13 +292,13 @@ def create_df(lst, file_name): ) # Add psy values - ta_rh = np.vectorize(psy.psy_ta_rh)(epw_df["DBT"], epw_df["RH"]) + ta_rh = np.vectorize(psy.psy_ta_rh)(epw_df[ColNames.DBT], epw_df[ColNames.RH]) psy_df = pd.DataFrame.from_records(ta_rh) psy_df = psy_df.set_index(epw_df.times) epw_df = epw_df.join(psy_df) # calculate adaptive data - dbt_day_ave = epw_df.groupby(["DOY"])["DBT"].mean().to_list() + dbt_day_ave = epw_df.groupby([ColNames.DOY])[ColNames.DBT].mean().to_list() n = 7 epw_df["adaptive_comfort"] = np.nan epw_df["adaptive_cmf_80_low"] = np.nan diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index 5f06e73c..e5acbe95 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -9,14 +9,14 @@ from pages.lib.global_scheme import mapping_dictionary import dash_bootstrap_components as dbc from .global_scheme import month_lst, template, tight_margins - +from pages.lib.global_column_names import ColNames from .utils import code_timer, determine_month_and_hour_filter def violin(df, var, global_local, si_ip): """Return day night violin based on the 'var' col""" - mask_day = (df["hour"] >= 8) & (df["hour"] < 20) - mask_night = (df["hour"] < 8) | (df["hour"] >= 20) + mask_day = (df[ColNames.HOUR] >= 8) & (df[ColNames.HOUR] < 20) + mask_night = (df[ColNames.HOUR] < 8) | (df[ColNames.HOUR] >= 20) var_unit = mapping_dictionary[var][si_ip]["unit"] var_range = mapping_dictionary[var][si_ip]["range"] var_name = mapping_dictionary[var]["name"] @@ -32,7 +32,7 @@ def violin(df, var, global_local, si_ip): fig = go.Figure() fig.add_trace( go.Violin( - x=df["fake_year"], + x=df[ColNames.FAKE_YEAR], y=data_day, line_color="#ffaa00", name="Day", @@ -44,7 +44,7 @@ def violin(df, var, global_local, si_ip): fig.add_trace( go.Violin( - x=df["fake_year"], + x=df[ColNames.FAKE_YEAR], y=data_night, line_color="#00264d", name="Night", @@ -107,14 +107,14 @@ def yearly_profile(df, var, global_local, si_ip): ) trace1 = go.Bar( - x=df["UTC_time"].dt.date.unique(), + x=df[ColNames.UTC_TIME].dt.date.unique(), y=dbt_day["max"] - dbt_day["min"], base=dbt_day["min"], marker_color=var_single_color, marker_opacity=0.3, name=var_name + " Range", customdata=np.stack( - (dbt_day["mean"], df.iloc[::24, :]["month_names"], df.iloc[::24, :]["day"]), + (dbt_day["mean"], df.iloc[::24, :][ColNames.MONTH_NAMES], df.iloc[::24, :][ColNames.DAY]), axis=-1, ), hovertemplate=( @@ -129,14 +129,14 @@ def yearly_profile(df, var, global_local, si_ip): ) trace2 = go.Scatter( - x=df["UTC_time"].dt.date.unique(), + x=df[ColNames.UTC_TIME].dt.date.unique(), y=dbt_day["mean"], name="Average " + var_name, mode="lines", marker_color=var_single_color, marker_opacity=1, customdata=np.stack( - (dbt_day["mean"], df.iloc[::24, :]["month_names"], df.iloc[::24, :]["day"]), + (dbt_day["mean"], df.iloc[::24, :][ColNames.MONTH_NAMES], df.iloc[::24, :][ColNames.DAY]), axis=-1, ), hovertemplate=( @@ -146,16 +146,16 @@ def yearly_profile(df, var, global_local, si_ip): ), ) - if var == "DBT": + if var == ColNames.DBT: # plot ashrae adaptive comfort limits (80%) - lo80 = df.groupby("DOY")["adaptive_cmf_80_low"].mean().values - hi80 = df.groupby("DOY")["adaptive_cmf_80_up"].mean().values - rmt = df.groupby("DOY")["adaptive_cmf_rmt"].mean().values + lo80 = df.groupby(ColNames.DOY)["adaptive_cmf_80_low"].mean().values + hi80 = df.groupby(ColNames.DOY)["adaptive_cmf_80_up"].mean().values + rmt = df.groupby(ColNames.DOY)["adaptive_cmf_rmt"].mean().values # set color https://github.com/CenterForTheBuiltEnvironment/clima/issues/113 implementation var_bar_colors = np.where((rmt > 40) | (rmt < 10), "lightgray", "darkgray") trace3 = go.Bar( - x=df["UTC_time"].dt.date.unique(), + x=df[ColNames.UTC_TIME].dt.date.unique(), y=hi80 - lo80, base=lo80, name="ASHRAE adaptive comfort (80%)", @@ -167,11 +167,11 @@ def yearly_profile(df, var, global_local, si_ip): ) # plot ashrae adaptive comfort limits (90%) - lo90 = df.groupby("DOY")["adaptive_cmf_90_low"].mean().values - hi90 = df.groupby("DOY")["adaptive_cmf_90_up"].mean().values + lo90 = df.groupby(ColNames.DOY)["adaptive_cmf_90_low"].mean().values + hi90 = df.groupby(ColNames.DOY)["adaptive_cmf_90_up"].mean().values trace4 = go.Bar( - x=df["UTC_time"].dt.date.unique(), + x=df[ColNames.UTC_TIME].dt.date.unique(), y=hi90 - lo90, base=lo90, name="ASHRAE adaptive comfort (90%)", @@ -183,7 +183,7 @@ def yearly_profile(df, var, global_local, si_ip): ) data = [trace3, trace4, trace1, trace2] - elif var == "RH": + elif var == ColNames.RH: # plot relative Humidity limits (30-70%) lo_rh = [30] * 365 hi_rh = [70] * 365 @@ -191,7 +191,7 @@ def yearly_profile(df, var, global_local, si_ip): hi_rh_df = pd.DataFrame({"hiRH": hi_rh}) trace3 = go.Bar( - x=df["UTC_time"].dt.date.unique(), + x=df[ColNames.UTC_TIME].dt.date.unique(), y=hi_rh_df["hiRH"] - lo_rh_df["loRH"], base=lo_rh_df["loRH"], name="humidity comfort band", @@ -252,7 +252,7 @@ def daily_profile(df, var, global_local, si_ip): range_y = [data_min, data_max] var_single_color = var_color[len(var_color) // 2] - var_month_ave = df.groupby(["month", "hour"])[var].median().reset_index() + var_month_ave = df.groupby([ColNames.MONTH, ColNames.HOUR])[var].median().reset_index() fig = make_subplots( rows=1, cols=12, @@ -263,15 +263,15 @@ def daily_profile(df, var, global_local, si_ip): for i in range(12): fig.add_trace( go.Scatter( - x=df.loc[df["month"] == i + 1, "hour"], - y=df.loc[df["month"] == i + 1, var], + x=df.loc[df[ColNames.MONTH] == i + 1, ColNames.HOUR], + y=df.loc[df[ColNames.MONTH] == i + 1, var], mode="markers", marker_color=var_single_color, opacity=0.5, marker_size=3, name=month_lst[i], showlegend=False, - customdata=df.loc[df["month"] == i + 1, "month_names"], + customdata=df.loc[df[ColNames.MONTH] == i + 1, ColNames.MONTH_NAMES], hovertemplate=( "" + var @@ -286,8 +286,8 @@ def daily_profile(df, var, global_local, si_ip): fig.add_trace( go.Scatter( - x=var_month_ave.loc[var_month_ave["month"] == i + 1, "hour"], - y=var_month_ave.loc[var_month_ave["month"] == i + 1, var], + x=var_month_ave.loc[var_month_ave[ColNames.MONTH] == i + 1, ColNames.HOUR], + y=var_month_ave.loc[var_month_ave[ColNames.MONTH] == i + 1, var], mode="lines", line_color=var_single_color, line_width=3, @@ -343,7 +343,7 @@ def heatmap_with_filter( month, hour, invert_month, invert_hour ) - if df.dropna(subset=["month"]).shape[0] == 0: + if df.dropna(subset=[ColNames.MONTH]).shape[0] == 0: return ( dbc.Alert( "No data is available in this location under these conditions. Please " @@ -364,13 +364,13 @@ def heatmap_with_filter( range_z = [data_min, data_max] fig = go.Figure( data=go.Heatmap( - y=df["hour"] - 0.5, # Offset by 0.5 to center the hour labels - x=df["UTC_time"].dt.date, + y=df[ColNames.HOUR] - 0.5, # Offset by 0.5 to center the hour labels + x=df[ColNames.UTC_TIME].dt.date, z=df[var], colorscale=var_color, zmin=range_z[0], zmax=range_z[1], - customdata=np.stack((df["month_names"], df["day"]), axis=-1), + customdata=np.stack((df[ColNames.MONTH_NAMES], df[ColNames.DAY]), axis=-1), hovertemplate=( "" + var @@ -423,13 +423,13 @@ def heatmap(df, var, global_local, si_ip): range_z = [data_min, data_max] fig = go.Figure( data=go.Heatmap( - y=df["hour"], - x=df["UTC_time"].dt.date, + y=df[ColNames.HOUR], + x=df[ColNames.UTC_TIME].dt.date, z=df[var], colorscale=var_color, zmin=range_z[0], zmax=range_z[1], - customdata=np.stack((df["month_names"], df["day"]), axis=-1), + customdata=np.stack((df[ColNames.MONTH_NAMES], df[ColNames.DAY]), axis=-1), hovertemplate=( "" + var @@ -478,16 +478,16 @@ def wind_rose(df, title, month, hour, labels, si_ip): start_hour = hour[0] end_hour = hour[1] if start_month <= end_month: - df = df.loc[(df["month"] >= start_month) & (df["month"] <= end_month)] + df = df.loc[(df[ColNames.MONTH] >= start_month) & (df[ColNames.MONTH] <= end_month)] else: - df = df.loc[(df["month"] <= end_month) | (df["month"] >= start_month)] + df = df.loc[(df[ColNames.MONTH] <= end_month) | (df[ColNames.MONTH] >= start_month)] if start_hour <= end_hour: - df = df.loc[(df["hour"] > start_hour) & (df["hour"] <= end_hour)] + df = df.loc[(df[ColNames.HOUR] > start_hour) & (df[ColNames.HOUR] <= end_hour)] else: - df = df.loc[(df["hour"] <= end_hour) | (df["hour"] >= start_hour)] + df = df.loc[(df[ColNames.HOUR] <= end_hour) | (df[ColNames.HOUR] >= start_hour)] - spd_colors = mapping_dictionary["wind_speed"]["color"] - spd_unit = mapping_dictionary["wind_speed"][si_ip]["unit"] + spd_colors = mapping_dictionary[ColNames.WIND_SPEED]["color"] + spd_unit = mapping_dictionary[ColNames.WIND_SPEED][si_ip]["unit"] spd_bins = [-1, 0.5, 1.5, 3.3, 5.5, 7.9, 10.7, 13.8, 17.1, 20.7, np.inf] if si_ip == UnitSystem.IP: spd_bins = convert_bins(spd_bins) @@ -501,10 +501,10 @@ def wind_rose(df, title, month, hour, labels, si_ip): # Create a temporary DataFrame with binned data df_binned = df.assign( WindSpd_bins=lambda d: pd.cut( - d["wind_speed"], bins=spd_bins, labels=spd_labels, right=True + d[ColNames.WIND_SPEED], bins=spd_bins, labels=spd_labels, right=True ), WindDir_bins=lambda d: pd.cut( - d["wind_dir"], bins=dir_bins, labels=dir_labels, right=False + d[ColNames.WIND_DIR], bins=dir_bins, labels=dir_labels, right=False ), ) @@ -618,7 +618,7 @@ def thermal_stress_stacked_barchart( month, hour, invert_month, invert_hour ) - if df.dropna(subset=["month"]).shape[0] == 0: + if df.dropna(subset=[ColNames.MONTH]).shape[0] == 0: return ( dbc.Alert( "No data is available in this location under these conditions. Please " @@ -631,12 +631,12 @@ def thermal_stress_stacked_barchart( isNormalized = True if len(normalize) != 0 else False if isNormalized: new_df = ( - df.groupby("month")[var].value_counts(normalize=True).unstack(var).fillna(0) + df.groupby(ColNames.MONTH)[var].value_counts(normalize=True).unstack(var).fillna(0) ) new_df = new_df.set_axis(categories, axis=1) new_df.reset_index(inplace=True) else: - new_df = df.groupby("month")[var].value_counts().unstack(var).fillna(0) + new_df = df.groupby(ColNames.MONTH)[var].value_counts().unstack(var).fillna(0) new_df = new_df.set_axis(categories, axis=1) new_df.reset_index(inplace=True) @@ -740,13 +740,13 @@ def barchart(df, var, time_filter_info, data_filter_info, normalize, si_ip): query = ( f"month=={str(i)} and ({filter_var}>={min_val} and {filter_var}<={max_val})" ) - a = new_df.query(query)["DOY"].count() + a = new_df.query(query)[ColNames.DOY].count() month_in.append(a) query = f"month=={str(i)} and ({filter_var}<{min_val})" - b = new_df.query(query)["DOY"].count() + b = new_df.query(query)[ColNames.DOY].count() month_below.append(b) query = f"month=={str(i)} and {filter_var}>{max_val}" - c = new_df.query(query)["DOY"].count() + c = new_df.query(query)[ColNames.DOY].count() month_above.append(c) go.Figure() @@ -830,17 +830,17 @@ def filter_df_by_month_and_hour( if time_filter: if start_month <= end_month: - mask = (df["month"] < start_month) | (df["month"] > end_month) + mask = (df[ColNames.MONTH] < start_month) | (df[ColNames.MONTH] > end_month) df.loc[mask, var] = None else: - mask = (df["month"] >= end_month) & (df["month"] <= start_month) + mask = (df[ColNames.MONTH] >= end_month) & (df[ColNames.MONTH] <= start_month) df.loc[mask, var] = None if start_hour <= end_hour: - mask = (df["hour"] <= start_hour) | (df["hour"] > end_hour) + mask = (df[ColNames.HOUR] <= start_hour) | (df[ColNames.HOUR] > end_hour) df.loc[mask, var] = None else: - mask = (df["hour"] > end_hour) & (df["hour"] <= start_hour) + mask = (df[ColNames.HOUR] > end_hour) & (df[ColNames.HOUR] <= start_hour) df.loc[mask, var] = None return df From 9b3ccaccc4a8f234bd0f971e23742d40f3add967 Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Sat, 23 Aug 2025 16:36:50 +1000 Subject: [PATCH 17/66] docs: update pull request regulation of contributing.md --- docs/contributing/contributing.md | 257 +++++------------------------- 1 file changed, 39 insertions(+), 218 deletions(-) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index af6c1a16..6fc210af 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -23,7 +23,7 @@ git clone https://github.com/Your Account name/clima.git cd clima ``` -Set up the upstream repository and check the output respositories. +Set up the upstream repository and check the output repositories. ```bash git remote add upstream https://github.com/CenterForTheBuiltEnvironment/clima.git @@ -51,7 +51,7 @@ The terminal will show a list of branches: remotes/origin/main ``` -Pull the development branch first, and if the terminal does not notices you that you should try the second command. +Pull the development branch first, and if the terminal does not notice you that you should try the second command. ```bash git checktout development @@ -71,221 +71,6 @@ Finally update and push to your repository branch if you modify the files. git push origin (your branch name) ``` -## Pull Request Regulation -**Time to submit PR:** - -- User requirements/issues have been addressed or discussed in Issue and consensus has been reached. -- Changes have been minimised (small steps/phased submission) to avoid "mega PRs". - -**Classification of Common PR Types:** - -- `Main (Master)`: Stable branch, merge code that passes review and CI; merge and release every time, - -- `Develop`: Continuous Integration branch for daily integration with multiple collaborators. -- `Feature/*`: feature development branch, cut out from main or develop, send PR to merge in after completing the feature. -- `Fix/*`: defect repair branch, the same process as feature -- `Release/*`: release preparation branch for freezing versions, fixing documentation, doing regressions and tagging. -- `docs/*`, `chore/*`, `refactor/*`, `test/*`: documentation, miscellaneous, refactor, test type branches. -- `Style`: style modification (does not affect the function): code formatting, space adjustment, naming rules unity. -- `Refactor`: Code Refactoring: Refactor existing code to improve maintainability. -- `Test`: Add or modify tests: add unit tests, integration tests, or modify test logic. -- `Chore`: Build Configuration, Dependency Management, CI/CD Configuration Updates. -- `Perf`: Performance Optimisation: Optimising code execution efficiency or memory usage. -- `Ci`: CI Configuration Related: Changing Continuous Integration Configurations for Github Actions, Travis, Jenkins, etc. -- `Build`: build system related: modify build scripts, packaging configuration. -- `Revert`: Rollback Commit: Undoing a Previous Commit -- `Security`: Security fixes, fixing security vulnerabilities, updating dependencies to prevent attacks. -- `Deps`: Dependency Management: Dependency Management/Adding, updating, and removing dependency libraries -- `Infra`: Infrastructure related: changes to development environments, containers, server configurations, etc. - -**Pull Request Strategy** -- `Constant centralization`: Added `ElementIds/ComponentProperty/ClassNames` to `components.py`. Introduce `ElementIds/ComponentProperty` for all UI IDs, replace magic strings with constants. This enables single-source-of-truth and prevents typos. No business logic changed. Verified by running callback smoke tests and snapshot tests. This is an example of modifications made in the `t_rh.py` file. - -`Orginal t_rh.py file(extract)` -```python -@callback( - Output("yearly-chart", "children"), - [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), - Input("dropdown", "value"), - ], - [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), - ], -) -``` -`Component.py: store repeated string` -```python -class ClassNames: - CONTAINER_COL = "container-col" - CONTAINER_COL_FULL_WIDTH = " container-col full-width" - TEXT_NEXT_TO_INPUT = "text-next-to-input" - - -class ElementIds: - DAILY = "daily" - DF_STORE = "df-store" - DROPDOWN = "dropdown" - HEATMAP = "heatmap" - GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - META_STORE = "meta-store" - SI_IP_UNIT_STORE = "si-ip-unit-store" - TABLE_TMP_HUM = "table-tmp-hum" - YEARLY_CHART = "yearly-chart" - - -class IdButtons: - DAILY_CHART_LABEL = "daily-chart-label" - - -class ComponentProperty: - CHILDREN = "children" - DATA = "data" - MODIFIED_TIMESTAMP = "modified_timestamp" - VALUE = "value" - - -class Text: - DAILY_CHART = "Daily chart" - YEARLY_CHART = "Yearly_chart" - HEATMAP_CHART = "Heatmap chart" - DESCRIPTIVE_STATISTICS = "Descriptive statistics" - - -class Type: - CIRCLE = "circle" -``` -`fixed t_rh.py(extract)` -```python -from components import ElementIds, ClassNames, Text, IdButtons, Type, ComponentProperty -@callback( - Output(ElementIds.YEARLY_CHART, ComponentProperty.CHILDREN), - [ - Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), - Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), - ], - [ - State(ElementIds.DF_STORE, ComponentProperty.DATA), - State(ElementIds.META_STORE, ComponentProperty.DATA), - State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), - ], -) -``` -- `Time field/timestamp harmonisation`: Unify time periods (such as hour, month) to `Colnames.hour` or `Colnames.month`. Added the `column_names.py` helper function and replaced the temporary parsing logic. Backward compatibility with old strings through a fault-tolerant parser; added round-trip testing in a multi-time zone environment. Below are three examples. - -`Create a new class to fix the time field` -```python -from enum import Enum - -class ColNames(str, Enum): - """ - DataFrame column name enumeration class, - avoiding hard-coded strings - """ - # Time-related columns - YEAR = "year" - MONTH = "month" - DAY = "day" - HOUR = "hour" - MINUTE = "minute" - - # Weather data column - DBT = "DBT" # dry bulb temperature - DPT = "DPT" # dew point temperature - RH = "RH" # relative humidity - P_ATM = "p_atm" # atmospheric pressure - - # Radiation-related column - EXTR_HOR_RAD = "extr_hor_rad" # Extraterrestrial Horizontal Radiation - HOR_IR_RAD = "hor_ir_rad" # Horizontal Infrared Radiation - GLOB_HOR_RAD = "glob_hor_rad" # Global Horizontal Radiation - DIR_NOR_RAD = "dir_nor_rad" # Direct Normal Radiation - DIF_HOR_RAD = "dif_hor_rad" # Diffuse Horizontal Radiation - - # Lighting-related columns - GLOB_HOR_ILL = "glob_hor_ill" # Global Horizontal Illuminance - DIR_NOR_ILL = "dir_nor_ill" # Direct Normal Illuminance - DIF_HOR_ILL = "dif_hor_ill" # Diffuse Horizontal Illuminance - - # Others - ZLUMI = "Zlumi" # Luminance - WIND_DIR = "wind_dir" # Wind Direction - WIND_SPEED = "wind_speed" # Wind Speed - TOT_SKY_COVER = "tot_sky_cover" # Total Sky Cover - OSKYCOVER = "Oskycover" # Opaque Sky Cover - VIS = "Vis" # Visibility - CHEIGHT = "Cheight" # Cloud Height - PWobs = "PWobs" # Precipitation Observation - PWcodes = "PWcodes" # Precipitation Codes - Pwater = "Pwater" # Precipitation Water - AsolOptD = "AsolOptD" # Aerosol Optical Depth - SnowD = "SnowD" # Snow Depth - DaySSnow = "DaySSnow" # Daily Snow - - # Calculation column - FAKE_YEAR = "fake_year" - MONTH_NAMES = "month_names" - UTC_TIME = "UTC_time" - DOY = "DOY" - ``` -`Example1` -```python -# Time filtering -# Before reconstruction -def violin(df, var, global_local, si_ip): - """Return day night violin based on the 'var' col""" - mask_day = (df["hour"] >= 8) & (df["hour"] < 20) - mask_night = (df["hour"] < 8) | (df["hour"] >= 20) - -# After reconstruction -from .column_names import ColNames - -def violin(df, var, global_local, si_ip): - """Return day night violin based on the 'var' col""" - mask_day = (df[ColNames.HOUR] >= 8) & (df[ColNames.HOUR] < 20) - mask_night = (df[ColNames.HOUR] < 8) | (df[ColNames.HOUR] >= 20) -``` -`Example2` -```python -# DataFrame grouping operations -# Before reconstruction -def daily_profile(df, var, global_local, si_ip): - var_month_ave = df.groupby(["month", "hour"])[var].median().reset_index() - - for i in range(12): - fig.add_trace( - go.Scatter( - x=df.loc[df["month"] == i + 1, "hour"], - y=df.loc[df["month"] == i + 1, var], - # ... other props - ) - ) - -# After reconstruction -from .column_names import ColNames - -def daily_profile(df, var, global_local, si_ip): - var_month_ave = df.groupby([ColNames.MONTH, ColNames.HOUR])[var] - .median() - .reset_index() - - for i in range(12): - fig.add_trace( - go.Scatter( - x=df.loc[df[ColNames.MONTH] == i + 1, ColNames.HOUR], - y=df.loc[df[ColNames.MONTH] == i + 1, var], - # ... other props - ) - ) -``` - -- `Progressive replacement/grey scale release`: Phase 1: Add constants; Phase 2: Batch replace references; Phase 3: Remove legacy code. The `FEATURE_USE_CONSTANT_IDS` feature switch allows for instant rollback. - - ## Code of Conduct Available [here](code_of_conduct.md) @@ -310,7 +95,7 @@ black . Before submitting a Pull Request, please make sure: - All tests should pass. -- Make sure you have installed project dependencies: +- You have installed project dependencies: ```bash npm install @@ -336,6 +121,42 @@ $ git commit -m "A brief summary of the commit > A paragraph describing what changed and its impact." ``` +> Detailed requirements for submitting a PR are described in the [Pull Request Regulation](#pull-request-regulation) section below + +Classification of Common Commit Types: + +- `Main (Master)`: Stable branch, merge code that passes review and CI; merge and release every time, +- `Develop`: Continuous Integration branch for daily integration with multiple collaborators. +- `Feature/*`: feature development branch, cut out from main or develop, send PR to merge in after completing the feature. +- `Fix/*`: defect repair branch, the same process as feature +- `Release/*`: release preparation branch for freezing versions, fixing documentation, doing regressions and tagging. +- `docs/*`, `chore/*`, `refactor/*`, `test/*`: documentation, miscellaneous, refactor, test type branches. +- `Style`: style modification (does not affect the function): code formatting, space adjustment, naming rules unity. +- `Refactor`: Code Refactoring: Refactor existing code to improve maintainability. +- `Test`: Add or modify tests: add unit tests, integration tests, or modify test logic. +- `Chore`: Build Configuration, Dependency Management, CI/CD Configuration Updates. +- `Perf`: Performance Optimisation: Optimising code execution efficiency or memory usage. +- `Ci`: CI Configuration Related: Changing Continuous Integration Configurations for Github Actions, Travis, Jenkins, etc. +- `Build`: build system related: modify build scripts, packaging configuration. +- `Revert`: Rollback Commit: Undoing a Previous Commit +- `Security`: Security fixes, fixing security vulnerabilities, updating dependencies to prevent attacks. +- `Deps`: Dependency Management: Dependency Management/Adding, updating, and removing dependency libraries +- `Infra`: Infrastructure related: changes to development environments, containers, server configurations, etc. + +## Pull Request Regulation +**Time to submit PR:** + +- User requirements/issues have been addressed or discussed in Issue and consensus has been reached. +- Changes have been minimised (small steps/phased submission) to avoid "mega PRs". + +**The pull request should include the following information:** + +- **Description:** Provide a brief summary of the changes, related issues, and motivation. List any required dependencies. **Fixes # (issue)** + +- **Type of Change:** Bug fix (non-breaking); New feature (non-breaking); Breaking change; Documentation update. + +- **Testing:** Describe how you tested your changes and how we can reproduce them. Include test details if necessary. + ## Thanks From 38e4da19a4bc1aa2113aefa539de15a7b6a292e6 Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Sat, 23 Aug 2025 16:38:19 +1000 Subject: [PATCH 18/66] fix: replaced the string that using Class ColNames in wind.py and charts_data_explorer.py --- pages/lib/charts_data_explorer.py | 9 +++++---- pages/wind.py | 31 ++++++++++++++++--------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/pages/lib/charts_data_explorer.py b/pages/lib/charts_data_explorer.py index 2a085bd3..d1534057 100644 --- a/pages/lib/charts_data_explorer.py +++ b/pages/lib/charts_data_explorer.py @@ -5,6 +5,7 @@ import plotly.express as px import plotly.graph_objects as go from pages.lib.global_scheme import template, mapping_dictionary, month_lst +from pages.lib.global_column_names import ColNames def custom_heatmap(df, global_local, var, time_filter_info, data_filter_info, si_ip): @@ -60,15 +61,15 @@ def custom_heatmap(df, global_local, var, time_filter_info, data_filter_info, si fig = go.Figure( data=go.Heatmap( - y=df["hour"], - x=df["DOY"], + y=df[ColNames.HOUR], + x=df[ColNames.DOY], z=df[var], colorscale=var_color, zmin=range_z[0], zmax=range_z[1], connectgaps=False, hoverongaps=False, - customdata=np.stack((df["month_names"], df["day"]), axis=-1), + customdata=np.stack((df[ColNames.MONTH_NAMES], df[ColNames.DAY]), axis=-1), hovertemplate=( "" + var @@ -132,7 +133,7 @@ def three_var_graph( else: df.loc[(df[filter_var] >= max_val) & (df[filter_var] <= min_val)] = None - if df.dropna(subset=["month"]).shape[0] == 0: + if df.dropna(subset=[ColNames.MONTH]).shape[0] == 0: return None title = ( diff --git a/pages/wind.py b/pages/wind.py index ff90ce5a..fe0d3da8 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -5,6 +5,7 @@ from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import month_lst, container_row_center_full from pages.lib.template_graphs import heatmap, wind_rose +from pages.lib.global_column_names import ColNames from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -376,7 +377,7 @@ def update_annual_wind_rose(_, df, meta, si_ip): # wind speed @callback( - Output("wind-speed", "children"), + Output(ColNames.WIND_SPEED, "children"), # General [ Input("df-store", "modified_timestamp"), @@ -391,10 +392,10 @@ def update_annual_wind_rose(_, df, meta, si_ip): def update_tab_wind_speed(_, global_local, df, meta, si_ip): """Update the contents of tab five. Passing in the info from the sliders and the general info (df, meta).""" - speed = heatmap(df, "wind_speed", global_local, si_ip) + speed = heatmap(df, ColNames.WIND_SPEED, global_local, si_ip) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("wind_speed", meta, units), + config=generate_chart_name(ColNames.WIND_SPEED, meta, units), figure=speed, ) @@ -415,7 +416,7 @@ def update_tab_wind_speed(_, global_local, df, meta, si_ip): def update_tab_wind_direction(global_local, df, meta, si_ip): """Update the contents of tab five. Passing in the info from the sliders and the general info (df, meta).""" - direction = heatmap(df, "wind_dir", global_local, si_ip) + direction = heatmap(df, ColNames.WIND_DIR, global_local, si_ip) units = generate_units(si_ip) return dcc.Graph( config=generate_chart_name("wind_direction", meta, units), @@ -452,13 +453,13 @@ def update_custom_wind_rose( # Wind Rose Graphs if start_month <= end_month: - df = df.loc[(df["month"] >= start_month) & (df["month"] <= end_month)] + df = df.loc[(df[ColNames.MONTH] >= start_month) & (df[ColNames.MONTH] <= end_month)] else: - df = df.loc[(df["month"] <= end_month) | (df["month"] >= start_month)] + df = df.loc[(df[ColNames.MONTH] <= end_month) | (df[ColNames.MONTH] >= start_month)] if start_hour <= end_hour: - df = df.loc[(df["hour"] >= start_hour) & (df["hour"] <= end_hour)] + df = df.loc[(df[ColNames.HOUR] >= start_hour) & (df[ColNames.HOUR] <= end_hour)] else: - df = df.loc[(df["hour"] <= end_hour) | (df["hour"] >= start_hour)] + df = df.loc[(df[ColNames.HOUR] <= end_hour) | (df[ColNames.HOUR] >= start_hour)] custom = wind_rose( df, "", [start_month, end_month], [start_hour, end_hour], True, si_ip ) @@ -507,25 +508,25 @@ def update_seasonal_graphs(_, df, meta, si_ip): # Text winter_df = df.loc[ - (df["month"] <= winter_months[1]) | (df["month"] >= winter_months[0]) + (df[ColNames.MONTH] <= winter_months[1]) | (df[ColNames.MONTH] >= winter_months[0]) ] query_calm_wind = "wind_speed == 0" winter_total_count = winter_df.shape[0] winter_calm_count = winter_df.query(query_calm_wind).shape[0] spring_df = df.loc[ - (df["month"] >= spring_months[0]) & (df["month"] <= spring_months[1]) + (df[ColNames.MONTH] >= spring_months[0]) & (df[ColNames.MONTH] <= spring_months[1]) ] spring_total_count = spring_df.shape[0] spring_calm_count = spring_df.query(query_calm_wind).shape[0] summer_df = df.loc[ - (df["month"] >= summer_months[0]) & (df["month"] <= summer_months[1]) + (df[ColNames.MONTH] >= summer_months[0]) & (df[ColNames.MONTH] <= summer_months[1]) ] summer_total_count = summer_df.shape[0] summer_calm_count = summer_df.query(query_calm_wind).shape[0] - fall_df = df.loc[(df["month"] >= fall_months[0]) & (df["month"] <= fall_months[1])] + fall_df = df.loc[(df[ColNames.MONTH] >= fall_months[0]) & (df[ColNames.MONTH] <= fall_months[1])] fall_total_count = fall_df.shape[0] fall_calm_count = fall_df.query(query_calm_wind).shape[0] @@ -621,18 +622,18 @@ def update_daily_graphs(_, df, meta, si_ip): # Text query_calm_wind = "wind_speed == 0" morning_df = df.loc[ - (df["hour"] >= morning_times[0]) & (df["hour"] <= morning_times[1]) + (df[ColNames.HOUR] >= morning_times[0]) & (df[ColNames.HOUR] <= morning_times[1]) ] morning_total_count = morning_df.shape[0] morning_calm_count = morning_df.query(query_calm_wind).shape[0] noon_df = df.loc[ - (df["hour"] >= morning_times[0]) & (df["hour"] <= morning_times[1]) + (df[ColNames.HOUR] >= morning_times[0]) & (df[ColNames.HOUR] <= morning_times[1]) ] noon_total_count = noon_df.shape[0] noon_calm_count = noon_df.query(query_calm_wind).shape[0] - night_df = df.loc[(df["hour"] <= night_times[1]) | (df["hour"] >= night_times[0])] + night_df = df.loc[(df[ColNames.HOUR] <= night_times[1]) | (df[ColNames.HOUR] >= night_times[0])] night_total_count = night_df.shape[0] night_calm_count = night_df.query(query_calm_wind).shape[0] From 813821840edb526970161892af592c41a048454f Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 23 Aug 2025 16:56:03 +1000 Subject: [PATCH 19/66] fix: replaced the string that using Class ColNames in charts_sun.py --- pages/lib/charts_sun.py | 45 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/pages/lib/charts_sun.py b/pages/lib/charts_sun.py index 73d43a24..103e11b4 100644 --- a/pages/lib/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -15,14 +15,15 @@ ) from plotly.subplots import make_subplots from pvlib import solarposition +from pages.lib.global_column_names import ColNames def monthly_solar(epw_df, si_ip): g_h_rad_month_ave = ( - epw_df.groupby(["month", "hour"])["glob_hor_rad"].median().reset_index() + epw_df.groupby([ColNames.MONTH, ColNames.HOUR])[ColNames.GLOB_HOR_RAD].median().reset_index() ) dif_h_rad_month_ave = ( - epw_df.groupby(["month", "hour"])["dif_hor_rad"].median().reset_index() + epw_df.groupby([ColNames.MONTH, ColNames.HOUR])[ColNames.DIF_HOR_RAD].median().reset_index() ) fig = make_subplots( rows=1, @@ -37,9 +38,9 @@ def monthly_solar(epw_df, si_ip): fig.add_trace( go.Scatter( - x=g_h_rad_month_ave.loc[g_h_rad_month_ave["month"] == i + 1, "hour"], + x=g_h_rad_month_ave.loc[g_h_rad_month_ave[ColNames.MONTH] == i + 1, ColNames.HOUR], y=g_h_rad_month_ave.loc[ - g_h_rad_month_ave["month"] == i + 1, "glob_hor_rad" + g_h_rad_month_ave[ColNames.MONTH] == i + 1, ColNames.GLOB_HOR_RAD ], fill="tozeroy", mode="lines", @@ -47,12 +48,12 @@ def monthly_solar(epw_df, si_ip): line_width=2, name="Global", showlegend=is_first, - customdata=epw_df.loc[epw_df["month"] == i + 1, "month_names"], + customdata=epw_df.loc[epw_df[ColNames.MONTH] == i + 1, ColNames.MONTH_NAMES], hovertemplate=( "" + "Global Horizontal Solar Radiation" + ": %{y:.2f} " - + mapping_dictionary["glob_hor_rad"][si_ip]["unit"] + + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip]["unit"] + "
" + "Month: %{customdata}
" + "Hour: %{x}:00
" @@ -66,10 +67,10 @@ def monthly_solar(epw_df, si_ip): fig.add_trace( go.Scatter( x=dif_h_rad_month_ave.loc[ - dif_h_rad_month_ave["month"] == i + 1, "hour" + dif_h_rad_month_ave[ColNames.MONTH] == i + 1, ColNames.HOUR ], y=dif_h_rad_month_ave.loc[ - dif_h_rad_month_ave["month"] == i + 1, "dif_hor_rad" + dif_h_rad_month_ave[ColNames.MONTH] == i + 1, ColNames.DIF_HOR_RAD ], fill="tozeroy", mode="lines", @@ -77,12 +78,12 @@ def monthly_solar(epw_df, si_ip): line_width=2, name="Diffuse", showlegend=is_first, - customdata=epw_df.loc[epw_df["month"] == i + 1, "month_names"], + customdata=epw_df.loc[epw_df[ColNames.MONTH] == i + 1, ColNames.MONTH_NAMES], hovertemplate=( "" + "Diffuse Horizontal Solar Radiation" + ": %{y:.2f} " - + mapping_dictionary["dif_hor_rad"][si_ip]["unit"] + + mapping_dictionary[ColNames.DIF_HOR_RAD][si_ip]["unit"] + "
" + "Month: %{customdata}
" + "Hour: %{x}:00
" @@ -173,9 +174,9 @@ def polar_graph(df, meta, global_local, var, si_ip): marker_line_width=0, customdata=np.stack( ( - solpos["day"], - solpos["month_names"], - solpos["hour"], + solpos[ColNames.DAY], + solpos[ColNames.MONTH_NAMES], + solpos[ColNames.HOUR], solpos["elevation"], solpos["azimuth"], ), @@ -209,9 +210,9 @@ def polar_graph(df, meta, global_local, var, si_ip): ), customdata=np.stack( ( - solpos["day"], - solpos["month_names"], - solpos["hour"], + solpos[ColNames.DAY], + solpos[ColNames.MONTH_NAMES], + solpos[ColNames.HOUR], solpos["elevation"], solpos["azimuth"], solpos[var], @@ -352,9 +353,9 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): marker_line_width=0, customdata=np.stack( ( - df["day"], - df["month_names"], - df["hour"], + df[ColNames.DAY], + df[ColNames.MONTH_NAMES], + df[ColNames.HOUR], df["elevation"], df["azimuth"], ), @@ -388,9 +389,9 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): ), customdata=np.stack( ( - df["day"], - df["month_names"], - df["hour"], + df[ColNames.DAY], + df[ColNames.MONTH_NAMES], + df[ColNames.HOUR], df["elevation"], df["azimuth"], df[var], From 2ab0f0cae33d8ae514fe05cc5a14309025b16a5d Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Sat, 23 Aug 2025 17:12:03 +1000 Subject: [PATCH 20/66] fix: replaced the string that using Class ColNames in utils.py and psy-chart.py and summary.py --- pages/lib/utils.py | 11 ++++++----- pages/psy-chart.py | 33 +++++++++++++++++---------------- pages/summary.py | 17 +++++++++-------- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/pages/lib/utils.py b/pages/lib/utils.py index 860a0409..89038bae 100644 --- a/pages/lib/utils.py +++ b/pages/lib/utils.py @@ -8,6 +8,7 @@ from config import UnitSystem from pages.lib.global_scheme import fig_config, mapping_dictionary, month_lst +from pages.lib.global_column_names import ColNames def code_timer(func): @@ -215,13 +216,13 @@ def title_with_link( def summary_table_tmp_rh_tab(df, value, si_ip): df_summary = ( - df.groupby(["month_names", "month"])[value] + df.groupby([ColNames.MONTH_NAMES, ColNames.MONTH])[value] .describe(percentiles=[0.01, 0.25, 0.5, 0.75, 0.99]) .round(2) ) - df_summary = df_summary.reset_index(level="month_names").sort_index() + df_summary = df_summary.reset_index(level=ColNames.MONTH_NAMES).sort_index() df_summary = df_summary.drop(["count"], axis=1) - df_summary = df_summary.rename(columns={"month_names": "month"}) + df_summary = df_summary.rename(columns={ColNames.MONTH_NAMES: ColNames.MONTH}) df_sum = ( df[value] @@ -229,7 +230,7 @@ def summary_table_tmp_rh_tab(df, value, si_ip): .round(2) .to_frame() ) - df_sum = df_sum.T.assign(count="Year").rename(columns={"count": "month"}) + df_sum = df_sum.T.assign(count="Year").rename(columns={"count": ColNames.MONTH}) df_summary = pd.concat([df_summary, df_sum]) @@ -240,7 +241,7 @@ def summary_table_tmp_rh_tab(df, value, si_ip): ) return dash_table.DataTable( columns=[ - {"name": i, "id": i} if i == "month" else {"name": f"{i} ({unit})", "id": i} + {"name": i, "id": i} if i == ColNames.MONTH else {"name": f"{i} ({unit})", "id": i} for i in df_summary.columns ], style_table={"overflowX": "auto"}, diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 2c46b885..0836c4d1 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -12,6 +12,7 @@ from pythermalcomfort import psychrometrics as psy from config import PageUrls, DocLinks, PageInfo, UnitSystem +from pages.lib.global_column_names import ColNames from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, @@ -173,7 +174,7 @@ def inputs(): dropdown( id="psy-var-dropdown", options=dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -312,13 +313,13 @@ def update_psych_chart( if global_local == "global": # Set Global values for Max and minimum - var_range_x = mapping_dictionary["DBT"][si_ip]["range"] + var_range_x = mapping_dictionary[ColNames.DBT][si_ip]["range"] var_range_y = mapping_dictionary["hr"][si_ip]["range"] else: # Set maximum and minimum according to data - data_max = 5 * ceil(df["DBT"].max() / 5) - data_min = 5 * floor(df["DBT"].min() / 5) + data_max = 5 * ceil(df[ColNames.DBT].max() / 5) + data_min = 5 * floor(df[ColNames.DBT].min() / 5) var_range_x = [data_min, data_max] data_max = round(df["hr"].max(), 4) @@ -363,7 +364,7 @@ def update_psych_chart( showlegend=False, mode="lines", name="", - hovertemplate="RH " + str(rh) + "%", + hovertemplate=ColNames.RH + str(rh) + "%", line=dict(width=1, color="lightgrey"), ) ) @@ -374,7 +375,7 @@ def update_psych_chart( if var == "None": fig.add_trace( go.Scatter( - x=df["DBT"], + x=df[ColNames.DBT], y=df_hr_multiply, showlegend=False, mode="markers", @@ -384,16 +385,16 @@ def update_psych_chart( showscale=False, opacity=0.2, ), - hovertemplate=mapping_dictionary["DBT"]["name"] + hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + ": %{x:.2f}" - + mapping_dictionary["DBT"]["name"], + + mapping_dictionary[ColNames.DBT]["name"], name="", ) ) elif var == "Frequency": fig.add_trace( go.Histogram2d( - x=df["DBT"], + x=df[ColNames.DBT], y=df_hr_multiply, name="", colorscale=var_color, @@ -437,7 +438,7 @@ def update_psych_chart( fig.add_trace( go.Scatter( - x=df["DBT"], + x=df[ColNames.DBT], y=df_hr_multiply, showlegend=False, mode="markers", @@ -449,14 +450,14 @@ def update_psych_chart( colorscale=var_color, colorbar=var_colorbar, ), - customdata=np.stack((df["RH"], df["h"], df[var], df["t_dp"]), axis=-1), - hovertemplate=mapping_dictionary["DBT"]["name"] + customdata=np.stack((df[ColNames.RH], df["h"], df[var], df["t_dp"]), axis=-1), + hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + ": %{x:.2f}" - + mapping_dictionary["DBT"][si_ip]["unit"] + + mapping_dictionary[ColNames.DBT][si_ip]["unit"] + "
" - + mapping_dictionary["RH"]["name"] + + mapping_dictionary[ColNames.RH]["name"] + ": %{customdata[0]:.2f}" - + mapping_dictionary["RH"][si_ip]["unit"] + + mapping_dictionary[ColNames.RH][si_ip]["unit"] + "
" + mapping_dictionary["h"]["name"] + ": %{customdata[1]:.2f}" @@ -474,7 +475,7 @@ def update_psych_chart( ) ) - xtitle_name = "Temperature" + " " + mapping_dictionary["DBT"][si_ip]["unit"] + xtitle_name = "Temperature" + " " + mapping_dictionary[ColNames.DBT][si_ip]["unit"] ytitle_name = "Humidity Ratio" + " " + mapping_dictionary["hr"][si_ip]["unit"] fig.update_layout(template=template, margin=tight_margins) fig.update_xaxes( diff --git a/pages/summary.py b/pages/summary.py index c6611c11..20547b8d 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -11,6 +11,7 @@ from pages.lib.extract_df import get_data from pages.lib.global_scheme import template, tight_margins, mapping_dictionary from pages.lib.template_graphs import violin +from pages.lib.global_column_names import ColNames from pages.lib.utils import ( generate_chart_name, generate_units, @@ -262,7 +263,7 @@ def update_location_info(ts, df, meta, si_ip): total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" total_diffuse_rad = f"Percentage of diffuse horizontal solar radiation: {round(df['dif_hor_rad'].sum() / df['glob_hor_rad'].sum() * 100, 1)} %" - tmp_unit = mapping_dictionary["DBT"][si_ip]["unit"] + tmp_unit = mapping_dictionary[ColNames.DBT][si_ip]["unit"] average_yearly_tmp = ( f"Average yearly temperature: {df['DBT'].mean().round(1)} " + tmp_unit ) @@ -339,14 +340,14 @@ def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ hdd_array = [] cdd_array = [] - months = df["month_names"].unique() + months = df[ColNames.MONTH_NAMES].unique() for i in range(1, 13): query_month = "month==" # calculates HDD per month query = query_month + str(i) + " and DBT<=" + str(hdd_setpoint) - a = df.query(query)["DBT"].sub(hdd_setpoint) + a = df.query(query)[ColNames.DBT].sub(hdd_setpoint) hdd = a.sum(axis=0, skipna=True) hdd = hdd / 24 hdd = int(hdd) @@ -354,7 +355,7 @@ def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ # calculates CDD per month query = query_month + str(i) + " and DBT>=" + str(cdd_setpoint) - a = df.query(query)["DBT"].sub(cdd_setpoint) + a = df.query(query)[ColNames.DBT].sub(cdd_setpoint) cdd = a.sum(axis=0, skipna=True) cdd = cdd / 24 cdd = int(cdd) @@ -429,7 +430,7 @@ def update_violin_tdb(ts, global_local, df, meta, si_ip): id="tdb-profile-graph", className="violin-container", config=generate_chart_name("DryBulbTemperature", meta, units), - figure=violin(df, "DBT", global_local, si_ip), + figure=violin(df, ColNames.DBT, global_local, si_ip), ) @@ -452,7 +453,7 @@ def update_tab_wind(ts, global_local, df, meta, si_ip): id="wind-profile-graph", className="violin-container", config=generate_chart_name("WindSpeed", meta, units), - figure=violin(df, "wind_speed", global_local, si_ip), + figure=violin(df, ColNames.WIND_SPEED, global_local, si_ip), ) @@ -475,7 +476,7 @@ def update_tab_rh(ts, global_local, df, meta, si_ip): id="rh-profile-graph", className="violin-container", config=generate_chart_name("RelativeHumidity", meta, units), - figure=violin(df, "RH", global_local, si_ip), + figure=violin(df, ColNames.RH, global_local, si_ip), ) @@ -498,7 +499,7 @@ def update_tab_gh_rad(ts, global_local, df, meta, si_ip): id="gh_rad-profile-graph", className="violin-container", config=generate_chart_name("GlobalHorizontalRadiation", meta, units), - figure=violin(df, "glob_hor_rad", global_local, si_ip), + figure=violin(df, ColNames.GLOB_HOR_RAD, global_local, si_ip), ) From be943a6844ee66c2f1a317a5e7a305375c5abd61 Mon Sep 17 00:00:00 2001 From: Kira-Liu00 Date: Sat, 23 Aug 2025 17:28:27 +1000 Subject: [PATCH 21/66] fix: Updated ElementIds of global_column_names.py --- pages/components.py | 31 +++++++++++++++++++++++++++++++ pages/lib/global_column_names.py | 27 ++++++++++++++++++++++++++- pages/outdoor.py | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 pages/components.py diff --git a/pages/components.py b/pages/components.py new file mode 100644 index 00000000..1432930d --- /dev/null +++ b/pages/components.py @@ -0,0 +1,31 @@ + +class ElementIds: + DAILY = "daily" + DF_STORE = "df-store" + DROPDOWN = "dropdown" + HEATMAP = "heatmap" + GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + META_STORE = "meta-store" + SI_IP_UNIT_STORE = "si-ip-unit-store" + TABLE_TMP_HUM = "table-tmp-hum" + YEARLY_CHART = "yearly-chart" + + +class IdButtons: + DAILY_CHART_LABEL = "daily-chart-label" + + +class ComponentProperty: + CHILDREN = "children" + DATA = "data" + MODIFIED_TIMESTAMP = "modified_timestamp" + VALUE = "value" + + +class Text: + DAILY_CHART = "Daily chart" + YEARLY_CHART = "Yearly_chart" + HEATMAP_CHART = "Heatmap chart" + DESCRIPTIVE_STATISTICS = "Descriptive statistics" +class Type: + CIRCLE = "circle" diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 35a3797e..a39bd60a 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -51,17 +51,41 @@ class ColNames(str, Enum): class ElementIds: # ==================== Defines the unique ID constant for each element in the front-end page ==================== + BEST_CONDITION_TEXT = "outdoor-comfort-output" DAILY = "daily" DF_STORE = "df-store" DROPDOWN = "dropdown" - HEATMAP = "heatmap" GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + HEATMAP = "heatmap" + HOUR_SLIDER = "outdoor-comfort-hour-slider" + IMAGE_SELECTION = "image-selection" + INVERT_HOUR = "invert-hour-outdoor-comfort" + INVERT_MONTH = "invert-month-outdoor-comfort" META_STORE = "meta-store" + MONTH_SLIDER = "outdoor-comfort-month-slider" + NORMALIZE_SWITCH = "outdoor-comfort-switches-input" + PSYCH_CHART = "psych-chart" + PSY_COLOR_BY_DROPDOWN = "psy-color-by-dropdown" + PSY_DATA_FILTER_BTN = "data-filter" + PSY_FILTER_VAR_DROPDOWN = "psy-var-dropdown" + PSY_HOUR_SLIDER = "psy-hour-slider" + PSY_INVERT_HOUR = "invert-hour-psy" + PSY_INVERT_MONTH = "invert-month-psy" + PSY_MAX_VAL_INPUT = "psy-max-val" + PSY_MIN_VAL_INPUT = "psy-min-val" + PSY_MONTH_SLIDER = "psy-month-slider" + PSY_TIME_FILTER_BTN = "month-hour-filter" + SCENARIO_DROPDOWN = "tab7-dropdown" SI_IP_UNIT_STORE = "si-ip-unit-store" TABLE_TMP_HUM = "table-tmp-hum" + TIME_FILTER_BTN = "month-hour-filter-outdoor-comfort" + UTCI_CATEGORY_HEATMAP = "utci-category-heatmap" + UTCI_HEATMAP = "utci-heatmap" + UTCI_SUMMARY_CHART = "utci-summary-chart" YEARLY_CHART = "yearly-chart" + class ComponentProperty: # ==================== Define common attribute name constants for components ==================== CHILDREN = "children" @@ -70,6 +94,7 @@ class ComponentProperty: VALUE = "value" + class Type: # ==================== Defines type constants available for UI components ==================== CIRCLE = "circle" diff --git a/pages/outdoor.py b/pages/outdoor.py index 59153930..72c57cc0 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -6,6 +6,7 @@ import numpy as np from config import PageUrls, DocLinks, PageInfo +from pages.lib.global_column_names import ElementIds, ComponentProperty, Type from pages.lib.global_scheme import ( outdoor_dropdown_names, ) From 2452ebe6f497ffdd96d34ca96aa92130dccab441 Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Sat, 23 Aug 2025 17:45:05 +1000 Subject: [PATCH 22/66] fix: delete components.py --- pages/components.py | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 pages/components.py diff --git a/pages/components.py b/pages/components.py deleted file mode 100644 index 1432930d..00000000 --- a/pages/components.py +++ /dev/null @@ -1,31 +0,0 @@ - -class ElementIds: - DAILY = "daily" - DF_STORE = "df-store" - DROPDOWN = "dropdown" - HEATMAP = "heatmap" - GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - META_STORE = "meta-store" - SI_IP_UNIT_STORE = "si-ip-unit-store" - TABLE_TMP_HUM = "table-tmp-hum" - YEARLY_CHART = "yearly-chart" - - -class IdButtons: - DAILY_CHART_LABEL = "daily-chart-label" - - -class ComponentProperty: - CHILDREN = "children" - DATA = "data" - MODIFIED_TIMESTAMP = "modified_timestamp" - VALUE = "value" - - -class Text: - DAILY_CHART = "Daily chart" - YEARLY_CHART = "Yearly_chart" - HEATMAP_CHART = "Heatmap chart" - DESCRIPTIVE_STATISTICS = "Descriptive statistics" -class Type: - CIRCLE = "circle" From 33f4865e2401e28f9bc176f7f7c6bb0a033853c8 Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Sat, 23 Aug 2025 18:06:40 +1000 Subject: [PATCH 23/66] Fix:Updated the ElementIDs of the explorer.py and global_column_names.py --- pages/explorer.py | 215 ++++++++++++++++--------------- pages/lib/global_column_names.py | 46 ++++++- 2 files changed, 153 insertions(+), 108 deletions(-) diff --git a/pages/explorer.py b/pages/explorer.py index f2c899bd..20518b54 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -12,6 +12,7 @@ two_var_graph, three_var_graph, ) +from pages.lib.global_column_names import ElementIds from pages.lib.global_scheme import ( fig_config, dropdown_names, @@ -65,7 +66,7 @@ def section_one_inputs(): children=[ html.H4(className="text-next-to-input", children=["Select a variable: "]), dropdown( - id="sec1-var-dropdown", + id=ElementIds.SEC1_VAR_DROPDOWN, options=explore_dropdown_names, value="DBT", ), @@ -88,7 +89,7 @@ def section_one(): ), dcc.Loading( type="circle", - children=html.Div(id="yearly-explore", className="full-width"), + children=html.Div(id=ElementIds.YEARLY_EXPLORE, className="full-width"), ), html.Div( children=title_with_link( @@ -98,7 +99,7 @@ def section_one(): ), ), dcc.Loading( - html.Div(className="full-width", id="query-daily"), + html.Div(className="full-width", id=ElementIds.QUERY_DAILY), type="circle", ), html.Div( @@ -109,7 +110,7 @@ def section_one(): ), ), dcc.Loading( - html.Div(className="full-width", id="query-heatmap"), + html.Div(className="full-width", id=ElementIds.QUERY_HEATMAP), type="circle", ), html.Div( @@ -128,7 +129,7 @@ def section_one(): dbc.Button( "Apply month and hour filter", color="primary", - id="sec1-time-filter-input", + id=ElementIds.SEC1_TIME_FILTER_INPUT, className="mb-2", n_clicks=0, ), @@ -140,7 +141,7 @@ def section_one(): html.H6("Month Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="sec1-month-slider", + id=ElementIds.SEC1_MONTH_SLIDER, min=1, max=12, step=1, @@ -159,7 +160,7 @@ def section_one(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-explore-descriptive", + id=ElementIds.INVERT_MONTH_EXPLORE_DESCRIPTIVE, labelStyle={"flex": "30%"}, ), ], @@ -170,7 +171,7 @@ def section_one(): html.H6("Hour Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="sec1-hour-slider", + id=ElementIds.SEC1_HOUR_SLIDER, min=0, max=24, step=1, @@ -189,7 +190,7 @@ def section_one(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-explore-descriptive", + id=ElementIds.INVERT_HOUR_EXPLORE_DESCRIPTIVE, labelStyle={"flex": "30%"}, ), ], @@ -199,7 +200,7 @@ def section_one(): ], ), html.Div( - id="table-data-explorer", + id=ElementIds.TABLE_DATA_EXPLORER, ), ], ) @@ -230,7 +231,7 @@ def section_two_inputs(): style={"flex": "30%"}, ), dropdown( - id="sec2-var-dropdown", + id=ElementIds.SEC2_VAR_DROPDOWN, options=explore_dropdown_names, value="RH", style={"flex": "70%"}, @@ -245,7 +246,7 @@ def section_two_inputs(): dbc.Button( "Apply month and hour filter", color="primary", - id="sec2-time-filter-input", + id=ElementIds.SEC2_TIME_FILTER_INPUT, className="mb-2", n_clicks=0, ), @@ -257,7 +258,7 @@ def section_two_inputs(): html.H6("Month Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="sec2-month-slider", + id=ElementIds.SEC2_MONTH_SLIDER, min=1, max=12, step=1, @@ -276,7 +277,7 @@ def section_two_inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-explore-heatmap", + id=ElementIds.INVERT_MONTH_EXPLORE_HEATMAP, labelStyle={"flex": "30%"}, ), ], @@ -287,7 +288,7 @@ def section_two_inputs(): html.H6("Hour Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="sec2-hour-slider", + id=ElementIds.SEC2_HOUR_SLIDER, min=0, max=24, step=1, @@ -306,7 +307,7 @@ def section_two_inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-explore-heatmap", + id=ElementIds.INVERT_HOUR_EXPLORE_HEATMAP, labelStyle={"flex": "30%"}, ), ], @@ -319,7 +320,7 @@ def section_two_inputs(): dbc.Button( "Apply filter", color="primary", - id="sec2-data-filter-input", + id=ElementIds.SEC2_DATA_FILTER_INPUT, className="mb-2", n_clicks=0, ), @@ -331,7 +332,7 @@ def section_two_inputs(): style={"flex": "30%"}, ), dropdown( - id="sec2-data-filter-var", + id=ElementIds.SEC2_DATA_FILTER_VAR, options=explore_dropdown_names, value="RH", style={"flex": "70%"}, @@ -345,7 +346,7 @@ def section_two_inputs(): children=["Min Value:"], style={"flex": "30%"} ), dbc.Input( - id="sec2-min-val", + id=ElementIds.SEC2_MIN_VAL, placeholder="Enter a number for the min val", type="number", value=0, @@ -361,7 +362,7 @@ def section_two_inputs(): children=["Max Value:"], style={"flex": "30%"} ), dbc.Input( - id="sec2-max-val", + id=ElementIds.SEC2_MAX_VAL, placeholder="Enter a number for the max val", type="number", value=100, @@ -381,26 +382,28 @@ def section_two_inputs(): def section_two(): """Return the two graphs in section two.""" return html.Div( - id="tab6-sec2-container", + id=ElementIds.TAB6_SEC2_CONTAINER, className="container-col justify-center full-width", children=[ section_two_inputs(), dcc.Loading( type="circle", - children=html.Div(className="full-width", id="custom-heatmap"), + children=html.Div(className="full-width", id=ElementIds.CUSTOM_HEATMAP), ), dbc.Checklist( options=[ {"label": "Normalize", "value": "normal"}, ], value=[], - id="normalize", + id=ElementIds.NORMALIZE, ), dcc.Loading( type="circle", children=[ dcc.Graph( - className="full-width", id="custom-summary", config=fig_config + className="full-width", + id=ElementIds.CUSTOM_SUMMARY, + config=fig_config, ), ], ), @@ -421,7 +424,7 @@ def section_three_inputs(): children=[ html.H6(style={"flex": "30%"}, children=["X Variable:"]), dropdown( - id="tab6-sec3-var-x-dropdown", + id=ElementIds.TAB6_SEC3_VAR_X_DROPDOWN, options=explore_dropdown_names, value="DBT", style={"flex": "70%"}, @@ -433,7 +436,7 @@ def section_three_inputs(): children=[ html.H6(style={"flex": "30%"}, children=["Y Variable:"]), dropdown( - id="tab6-sec3-var-y-dropdown", + id=ElementIds.TAB6_SEC3_VAR_Y_DROPDOWN, options=explore_dropdown_names, value="RH", style={"flex": "70%"}, @@ -445,7 +448,7 @@ def section_three_inputs(): children=[ html.H6(style={"flex": "30%"}, children=["Color By:"]), dropdown( - id="tab6-sec3-colorby-dropdown", + id=ElementIds.TAB6_SEC3_COLORBY_DROPDOWN, options=explore_dropdown_names, value="glob_hor_rad", style={"flex": "70%"}, @@ -460,7 +463,7 @@ def section_three_inputs(): dbc.Button( "Apply month and hour filter", color="primary", - id="tab6-sec3-time-filter-input", + id=ElementIds.TAB6_SEC3_TIME_FILTER_INPUT, className="mb-2", n_clicks=0, ), @@ -470,7 +473,7 @@ def section_three_inputs(): html.H6("Month Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="tab6-sec3-query-month-slider", + id=ElementIds.TAB6_SEC3_QUERY_MONTH_SLIDER, min=1, max=12, step=1, @@ -489,7 +492,7 @@ def section_three_inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-explore-more-charts", + id=ElementIds.INVERT_MONTH_EXPLORE_MORE_CHARTS, labelStyle={"flex": "30%"}, ), ], @@ -500,7 +503,7 @@ def section_three_inputs(): html.H6("Hour Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="tab6-sec3-query-hour-slider", + id=ElementIds.TAB6_SEC3_QUERY_HOUR_SLIDER, min=0, max=24, step=1, @@ -519,7 +522,7 @@ def section_three_inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-explore-more-charts", + id=ElementIds.INVERT_HOUR_EXPLORE_MORE_CHARTS, labelStyle={"flex": "30%"}, ), ], @@ -532,7 +535,7 @@ def section_three_inputs(): dbc.Button( "Apply filter", color="primary", - id="tab6-sec3-data-filter-input", + id=ElementIds.TAB6_SEC3_DATA_FILTER_INPUT, className="mb-2", n_clicks=0, ), @@ -543,7 +546,7 @@ def section_three_inputs(): children=["Filter Variable:"], style={"flex": "30%"} ), dropdown( - id="tab6-sec3-filter-var-dropdown", + id=ElementIds.TAB6_SEC3_FILTER_VAR_DROPDOWN, options=explore_dropdown_names, value="RH", style={"flex": "70%"}, @@ -556,7 +559,7 @@ def section_three_inputs(): html.H6(children=["Min Value:"], style={"flex": "30%"}), dbc.Input( className="num-input", - id="tab6-sec3-min-val", + id=ElementIds.TAB6_SEC3_MIN_VAL, placeholder="Enter a number for the min val", type="number", step=1, @@ -571,7 +574,7 @@ def section_three_inputs(): html.H6(children=["Max Value:"], style={"flex": "30%"}), dbc.Input( className="num-input", - id="tab6-sec3-max-val", + id=ElementIds.TAB6_SEC3_MAX_VAL, placeholder="Enter a number for the max val", type="number", value=100, @@ -600,11 +603,11 @@ def section_three(): ), section_three_inputs(), dcc.Loading( - html.Div(id="three-var"), + html.Div(id=ElementIds.THREE_VAR), type="circle", ), dcc.Loading( - html.Div(id="two-var"), + html.Div(id=ElementIds.TWO_VAR), type="circle", ), ], @@ -620,17 +623,17 @@ def layout(): @callback( - Output("yearly-explore", "children"), + Output(ElementIds.YEARLY_EXPLORE, "children"), # Section One [ Input("df-store", "modified_timestamp"), - Input("sec1-var-dropdown", "value"), - Input("global-local-radio-input", "value"), + Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_EXPLORER_DF_STORE, "data"), + State(ElementIds.META_STORE, "data"), + State(ElementIds.SI_IP_UNIT_STORE, "data"), ], ) def update_tab_yearly(_, var, global_local, df, meta, si_ip): @@ -653,16 +656,16 @@ def update_tab_yearly(_, var, global_local, df, meta, si_ip): @callback( - Output("query-daily", "children"), + Output(ElementIds.QUERY_DAILY, "children"), [ - Input("df-store", "modified_timestamp"), - Input("sec1-var-dropdown", "value"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), + Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.DF_STORE, "data"), + State(ElementIds.META_STORE, "data"), + State(ElementIds.SI_IP_UNIT_STORE, "data"), ], ) def update_tab_daily(_, var, global_local, df, meta, si_ip): @@ -678,16 +681,16 @@ def update_tab_daily(_, var, global_local, df, meta, si_ip): @callback( - Output("query-heatmap", "children"), + Output(ElementIds.QUERY_HEATMAP, "children"), [ - Input("df-store", "modified_timestamp"), - Input("sec1-var-dropdown", "value"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), + Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_EXPLORER_DF_STORE, "data"), + State(ElementIds.META_STORE, "data"), + State(ElementIds.SI_IP_UNIT_STORE, "data"), ], ) def update_tab_heatmap(_, var, global_local, df, meta, si_ip): @@ -704,31 +707,31 @@ def update_tab_heatmap(_, var, global_local, df, meta, si_ip): @callback( [ - Output("custom-heatmap", "children"), - Output("custom-summary", "style"), - Output("custom-summary", "figure"), - Output("normalize", "style"), + Output(ElementIds.CUSTOM_HEATMAP, "children"), + Output(ElementIds.CUSTOM_SUMMARY, "style"), + Output(ElementIds.CUSTOM_SUMMARY, "figure"), + Output(ElementIds.NORMALIZE, "style"), ], [ - Input("df-store", "modified_timestamp"), - Input("sec2-var-dropdown", "value"), - Input("sec2-time-filter-input", "n_clicks"), - Input("sec2-data-filter-input", "n_clicks"), - Input("normalize", "value"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), + Input(ElementIds.SEC2_VAR_DROPDOWN, "value"), + Input(ElementIds.SEC2_TIME_FILTER_INPUT, "n_clicks"), + Input(ElementIds.SEC2_DATA_FILTER_INPUT, "n_clicks"), + Input(ElementIds.NORMALIZE, "value"), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), ], # General [ - State("df-store", "data"), - State("sec2-month-slider", "value"), - State("sec2-hour-slider", "value"), - State("sec2-data-filter-var", "value"), - State("sec2-min-val", "value"), - State("sec2-max-val", "value"), - State("meta-store", "data"), - State("invert-month-explore-heatmap", "value"), - State("invert-hour-explore-heatmap", "value"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_EXPLORER_DF_STORE, "data"), + State(ElementIds.SEC2_MONTH_SLIDER, "value"), + State(ElementIds.SEC2_HOUR_SLIDER, "value"), + State(ElementIds.SEC2_DATA_FILTER_VAR, "value"), + State(ElementIds.SEC2_MIN_VAL, "value"), + State(ElementIds.SEC2_MAX_VAL, "value"), + State(ElementIds.META_STORE, "data"), + State(ElementIds.INVERT_MONTH_EXPLORE_HEATMAP, "value"), + State(ElementIds.INVERT_HOUR_EXPLORE_HEATMAP, "value"), + State(ElementIds.SI_IP_UNIT_STORE, "data"), ], ) def update_heatmap( @@ -820,27 +823,27 @@ def update_heatmap( @callback( - [Output("three-var", "children"), Output("two-var", "children")], + [Output(ElementIds.THREE_VAR, "children"), Output(ElementIds.TWO_VAR, "children")], [ - Input("df-store", "modified_timestamp"), - Input("tab6-sec3-var-x-dropdown", "value"), - Input("tab6-sec3-var-y-dropdown", "value"), - Input("tab6-sec3-colorby-dropdown", "value"), - Input("tab6-sec3-time-filter-input", "n_clicks"), - Input("tab6-sec3-data-filter-input", "n_clicks"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), + Input(ElementIds.TAB6_SEC3_VAR_X_DROPDOWN, "value"), + Input(ElementIds.TAB6_SEC3_VAR_Y_DROPDOWN, "value"), + Input(ElementIds.TAB6_SEC3_COLORBY_DROPDOWN, "value"), + Input(ElementIds.TAB6_SEC3_TIME_FILTER_INPUT, "n_clicks"), + Input(ElementIds.TAB6_SEC3_DATA_FILTER_INPUT, "n_clicks"), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("tab6-sec3-query-month-slider", "value"), - State("tab6-sec3-query-hour-slider", "value"), - State("tab6-sec3-filter-var-dropdown", "value"), - State("tab6-sec3-min-val", "value"), - State("tab6-sec3-max-val", "value"), - State("meta-store", "data"), - State("invert-month-explore-more-charts", "value"), - State("invert-hour-explore-more-charts", "value"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_EXPLORER_DF_STORE, "data"), + State(ElementIds.TAB6_SEC3_QUERY_MONTH_SLIDER, "value"), + State(ElementIds.TAB6_SEC3_QUERY_HOUR_SLIDER, "value"), + State(ElementIds.TAB6_SEC3_FILTER_VAR_DROPDOWN, "value"), + State(ElementIds.TAB6_SEC3_MIN_VAL, "value"), + State(ElementIds.TAB6_SEC3_MAX_VAL, "value"), + State(ElementIds.META_STORE, "data"), + State(ElementIds.INVERT_MONTH_EXPLORE_MORE_CHARTS, "value"), + State(ElementIds.INVERT_HOUR_EXPLORE_MORE_CHARTS, "value"), + State(ElementIds.SI_IP_UNIT_STORE, "data"), ], ) def update_more_charts( @@ -912,19 +915,19 @@ def update_more_charts( @callback( - Output("table-data-explorer", "children"), + Output(ElementIds.TABLE_DATA_EXPLORER, "children"), [ - Input("df-store", "modified_timestamp"), - Input("sec1-var-dropdown", "value"), - Input("sec1-time-filter-input", "n_clicks"), + Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), + Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), + Input(ElementIds.SEC1_TIME_FILTER_INPUT, "n_clicks"), ], [ - State("df-store", "data"), - State("si-ip-unit-store", "data"), - State("sec1-month-slider", "value"), - State("sec1-hour-slider", "value"), - State("invert-month-explore-descriptive", "value"), - State("invert-hour-explore-descriptive", "value"), + State(ElementIds.ID_EXPLORER_DF_STORE, "data"), + State(ElementIds.SI_IP_UNIT_STORE, "data"), + State(ElementIds.SEC1_MONTH_SLIDER, "value"), + State(ElementIds.SEC1_HOUR_SLIDER, "value"), + State(ElementIds.INVERT_MONTH_EXPLORE_DESCRIPTIVE, "value"), + State(ElementIds.INVERT_HOUR_EXPLORE_DESCRIPTIVE, "value"), ], ) def update_table( diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index a39bd60a..c57c6686 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -54,13 +54,28 @@ class ElementIds: BEST_CONDITION_TEXT = "outdoor-comfort-output" DAILY = "daily" DF_STORE = "df-store" + ID_EXPLORER_DF_STORE = "df-store" DROPDOWN = "dropdown" + CUSTOM_SUMMARY = "custom-summary" + CUSTOM_HEATMAP = "custom-heatmap" GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" HEATMAP = "heatmap" + MAIN_NV_SECTION = "main-nv-section" + NORMALIZE = "normalize" + NV_HEATMAP_CHART = "nv-heatmap-chart" HOUR_SLIDER = "outdoor-comfort-hour-slider" IMAGE_SELECTION = "image-selection" INVERT_HOUR = "invert-hour-outdoor-comfort" INVERT_MONTH = "invert-month-outdoor-comfort" + ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + INVERT_HOUR_EXPLORE_DESCRIPTIVE = "invert-hour-explore-descriptive" + INVERT_MONTH_EXPLORE_DESCRIPTIVE = "invert-month-explore-descriptive" + INVERT_MONTH_EXPLORE_HEATMAP = "invert-month-explore-heatmap" + INVERT_HOUR_EXPLORE_HEATMAP = "invert-hour-explore-heatmap" + INVERT_MONTH_EXPLORE_MORE_CHARTS = "invert-month-explore-more-charts" + INVERT_HOUR_EXPLORE_MORE_CHARTS = "invert-hour-explore-more-charts" + ID_EXPLORER_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_NATURAL_SI_IP_UNIT_STORE = "si-ip-unit-store" META_STORE = "meta-store" MONTH_SLIDER = "outdoor-comfort-month-slider" NORMALIZE_SWITCH = "outdoor-comfort-switches-input" @@ -75,15 +90,43 @@ class ElementIds: PSY_MIN_VAL_INPUT = "psy-min-val" PSY_MONTH_SLIDER = "psy-month-slider" PSY_TIME_FILTER_BTN = "month-hour-filter" + SEC1_HOUR_SLIDER = "sec1-hour-slider" + SEC1_MONTH_SLIDER = "sec1-month-slider" + SEC1_TIME_FILTER_INPUT = "sec1-time-filter-input" + SEC1_VAR_DROPDOWN = "sec1-var-dropdown" + SEC2_DATA_FILTER_INPUT = "sec2-data-filter-input" + SEC2_DATA_FILTER_VAR = "sec2-data-filter-var" + SEC2_VAR_DROPDOWN = "sec2-var-dropdown" + SEC2_HOUR_SLIDER = "sec2-hour-slider" + SEC2_TIME_FILTER_INPUT = "sec2-time-filter-input" + SEC2_MONTH_SLIDER = "sec2-month-slider" + SEC2_MIN_VAL = "sec2-min-val" + SEC2_MAX_VAL = "sec2-max-val" SCENARIO_DROPDOWN = "tab7-dropdown" SI_IP_UNIT_STORE = "si-ip-unit-store" TABLE_TMP_HUM = "table-tmp-hum" + TABLE_DATA_EXPLORER = "table-data-explorer" + TAB6_SEC2_CONTAINER = "tab6-sec2-container" + TAB6_SEC3_DATA_FILTER_INPUT = "tab6-sec3-data-filter-input" + TAB6_SEC3_FILTER_VAR_DROPDOWN = "tab6-sec3-filter-var-dropdown" + TAB6_SEC3_MIN_VAL = "tab6-sec3-min-val" + TAB6_SEC3_MAX_VAL = "tab6-sec3-max-val" + TAB6_SEC3_TIME_FILTER_INPUT = "tab6-sec3-time-filter-input" + TAB6_SEC3_QUERY_HOUR_SLIDER = "tab6-sec3-query-hour-slider" + TAB6_SEC3_QUERY_MONTH_SLIDER = "tab6-sec3-query-month-slider" + TAB6_SEC3_VAR_X_DROPDOWN = "tab6-sec3-var-x-dropdown" + TAB6_SEC3_VAR_Y_DROPDOWN = "tab6-sec3-var-y-dropdown" + TAB6_SEC3_COLORBY_DROPDOWN = "tab6-sec3-colorby-dropdown" TIME_FILTER_BTN = "month-hour-filter-outdoor-comfort" + TWO_VAR = "two-var" + THREE_VAR = "three-var" + QUERY_DAILY = "query-daily" + QUERY_HEATMAP = "query-heatmap" UTCI_CATEGORY_HEATMAP = "utci-category-heatmap" UTCI_HEATMAP = "utci-heatmap" UTCI_SUMMARY_CHART = "utci-summary-chart" YEARLY_CHART = "yearly-chart" - + YEARLY_EXPLORE = "yearly-explore" class ComponentProperty: @@ -94,7 +137,6 @@ class ComponentProperty: VALUE = "value" - class Type: # ==================== Defines type constants available for UI components ==================== CIRCLE = "circle" From cf56585c20b4517bfcbe3e219af5cfb6f3a375b0 Mon Sep 17 00:00:00 2001 From: Kira-Liu00 Date: Sat, 23 Aug 2025 18:18:44 +1000 Subject: [PATCH 24/66] fix: Updated ElementIds of the psy-chart and global _column _names.py --- pages/lib/global_column_names.py | 4 ++-- pages/psy-chart.py | 34 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index a39bd60a..4289d43b 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -75,6 +75,7 @@ class ElementIds: PSY_MIN_VAL_INPUT = "psy-min-val" PSY_MONTH_SLIDER = "psy-month-slider" PSY_TIME_FILTER_BTN = "month-hour-filter" + PSY_CHART_BTN = "psy-chart-btn" SCENARIO_DROPDOWN = "tab7-dropdown" SI_IP_UNIT_STORE = "si-ip-unit-store" TABLE_TMP_HUM = "table-tmp-hum" @@ -83,8 +84,7 @@ class ElementIds: UTCI_HEATMAP = "utci-heatmap" UTCI_SUMMARY_CHART = "utci-summary-chart" YEARLY_CHART = "yearly-chart" - - + class ComponentProperty: # ==================== Define common attribute name constants for components ==================== diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 0836c4d1..61b85a58 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -12,7 +12,7 @@ from pythermalcomfort import psychrometrics as psy from config import PageUrls, DocLinks, PageInfo, UnitSystem -from pages.lib.global_column_names import ColNames +from pages.lib.global_column_names import ElementIds, ComponentProperty, Type from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, @@ -174,7 +174,7 @@ def inputs(): dropdown( id="psy-var-dropdown", options=dropdown_names, - value=ColNames.RH, + value="RH", style={"flex": "70%"}, ), ], @@ -313,13 +313,13 @@ def update_psych_chart( if global_local == "global": # Set Global values for Max and minimum - var_range_x = mapping_dictionary[ColNames.DBT][si_ip]["range"] + var_range_x = mapping_dictionary["DBT"][si_ip]["range"] var_range_y = mapping_dictionary["hr"][si_ip]["range"] else: # Set maximum and minimum according to data - data_max = 5 * ceil(df[ColNames.DBT].max() / 5) - data_min = 5 * floor(df[ColNames.DBT].min() / 5) + data_max = 5 * ceil(df["DBT"].max() / 5) + data_min = 5 * floor(df["DBT"].min() / 5) var_range_x = [data_min, data_max] data_max = round(df["hr"].max(), 4) @@ -364,7 +364,7 @@ def update_psych_chart( showlegend=False, mode="lines", name="", - hovertemplate=ColNames.RH + str(rh) + "%", + hovertemplate="RH " + str(rh) + "%", line=dict(width=1, color="lightgrey"), ) ) @@ -375,7 +375,7 @@ def update_psych_chart( if var == "None": fig.add_trace( go.Scatter( - x=df[ColNames.DBT], + x=df["DBT"], y=df_hr_multiply, showlegend=False, mode="markers", @@ -385,16 +385,16 @@ def update_psych_chart( showscale=False, opacity=0.2, ), - hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + hovertemplate=mapping_dictionary["DBT"]["name"] + ": %{x:.2f}" - + mapping_dictionary[ColNames.DBT]["name"], + + mapping_dictionary["DBT"]["name"], name="", ) ) elif var == "Frequency": fig.add_trace( go.Histogram2d( - x=df[ColNames.DBT], + x=df["DBT"], y=df_hr_multiply, name="", colorscale=var_color, @@ -438,7 +438,7 @@ def update_psych_chart( fig.add_trace( go.Scatter( - x=df[ColNames.DBT], + x=df["DBT"], y=df_hr_multiply, showlegend=False, mode="markers", @@ -450,14 +450,14 @@ def update_psych_chart( colorscale=var_color, colorbar=var_colorbar, ), - customdata=np.stack((df[ColNames.RH], df["h"], df[var], df["t_dp"]), axis=-1), - hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + customdata=np.stack((df["RH"], df["h"], df[var], df["t_dp"]), axis=-1), + hovertemplate=mapping_dictionary["DBT"]["name"] + ": %{x:.2f}" - + mapping_dictionary[ColNames.DBT][si_ip]["unit"] + + mapping_dictionary["DBT"][si_ip]["unit"] + "
" - + mapping_dictionary[ColNames.RH]["name"] + + mapping_dictionary["RH"]["name"] + ": %{customdata[0]:.2f}" - + mapping_dictionary[ColNames.RH][si_ip]["unit"] + + mapping_dictionary["RH"][si_ip]["unit"] + "
" + mapping_dictionary["h"]["name"] + ": %{customdata[1]:.2f}" @@ -475,7 +475,7 @@ def update_psych_chart( ) ) - xtitle_name = "Temperature" + " " + mapping_dictionary[ColNames.DBT][si_ip]["unit"] + xtitle_name = "Temperature" + " " + mapping_dictionary["DBT"][si_ip]["unit"] ytitle_name = "Humidity Ratio" + " " + mapping_dictionary["hr"][si_ip]["unit"] fig.update_layout(template=template, margin=tight_margins) fig.update_xaxes( From 2b82d7977cc99fa75b450fdf3f4d471de963415c Mon Sep 17 00:00:00 2001 From: Kira-Liu00 Date: Sat, 23 Aug 2025 18:36:55 +1000 Subject: [PATCH 25/66] Fix: Updated the ElementIds of global_column_names.py --- pages/lib/global_column_names.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index c57c6686..ba7e69dd 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -80,6 +80,7 @@ class ElementIds: MONTH_SLIDER = "outdoor-comfort-month-slider" NORMALIZE_SWITCH = "outdoor-comfort-switches-input" PSYCH_CHART = "psych-chart" + PSY_CHART_BTN = "psy-chart-btn" PSY_COLOR_BY_DROPDOWN = "psy-color-by-dropdown" PSY_DATA_FILTER_BTN = "data-filter" PSY_FILTER_VAR_DROPDOWN = "psy-var-dropdown" @@ -90,6 +91,7 @@ class ElementIds: PSY_MIN_VAL_INPUT = "psy-min-val" PSY_MONTH_SLIDER = "psy-month-slider" PSY_TIME_FILTER_BTN = "month-hour-filter" + SEC1_HOUR_SLIDER = "sec1-hour-slider" SEC1_MONTH_SLIDER = "sec1-month-slider" SEC1_TIME_FILTER_INPUT = "sec1-time-filter-input" From 34f1ad3c10534cc27a4951f0e581d2165004850d Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Sat, 23 Aug 2025 19:02:53 +1000 Subject: [PATCH 26/66] Fix:Updated the ElementIDs of the explorer.py, global_column_names.py and natural_ventilation.py. --- pages/explorer.py | 32 ++++----- pages/lib/global_column_names.py | 17 +++++ pages/natural_ventilation.py | 115 +++++++++++++++++-------------- 3 files changed, 95 insertions(+), 69 deletions(-) diff --git a/pages/explorer.py b/pages/explorer.py index 20518b54..5a5ed6e0 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -632,8 +632,8 @@ def layout(): ], [ State(ElementIds.ID_EXPLORER_DF_STORE, "data"), - State(ElementIds.META_STORE, "data"), - State(ElementIds.SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_EXPLORER_META_STORE, "data"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_yearly(_, var, global_local, df, meta, si_ip): @@ -660,12 +660,12 @@ def update_tab_yearly(_, var, global_local, df, meta, si_ip): [ Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State(ElementIds.DF_STORE, "data"), - State(ElementIds.META_STORE, "data"), - State(ElementIds.SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_EXPLORER_DF_STORE, "data"), + State(ElementIds.ID_EXPLORER_META_STORE, "data"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_daily(_, var, global_local, df, meta, si_ip): @@ -685,12 +685,12 @@ def update_tab_daily(_, var, global_local, df, meta, si_ip): [ Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_NATURAL_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ State(ElementIds.ID_EXPLORER_DF_STORE, "data"), - State(ElementIds.META_STORE, "data"), - State(ElementIds.SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_EXPLORER_META_STORE, "data"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_heatmap(_, var, global_local, df, meta, si_ip): @@ -718,7 +718,7 @@ def update_tab_heatmap(_, var, global_local, df, meta, si_ip): Input(ElementIds.SEC2_TIME_FILTER_INPUT, "n_clicks"), Input(ElementIds.SEC2_DATA_FILTER_INPUT, "n_clicks"), Input(ElementIds.NORMALIZE, "value"), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], # General [ @@ -728,10 +728,10 @@ def update_tab_heatmap(_, var, global_local, df, meta, si_ip): State(ElementIds.SEC2_DATA_FILTER_VAR, "value"), State(ElementIds.SEC2_MIN_VAL, "value"), State(ElementIds.SEC2_MAX_VAL, "value"), - State(ElementIds.META_STORE, "data"), + State(ElementIds.ID_EXPLORER_META_STORE, "data"), State(ElementIds.INVERT_MONTH_EXPLORE_HEATMAP, "value"), State(ElementIds.INVERT_HOUR_EXPLORE_HEATMAP, "value"), - State(ElementIds.SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), ], ) def update_heatmap( @@ -831,7 +831,7 @@ def update_heatmap( Input(ElementIds.TAB6_SEC3_COLORBY_DROPDOWN, "value"), Input(ElementIds.TAB6_SEC3_TIME_FILTER_INPUT, "n_clicks"), Input(ElementIds.TAB6_SEC3_DATA_FILTER_INPUT, "n_clicks"), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ State(ElementIds.ID_EXPLORER_DF_STORE, "data"), @@ -840,10 +840,10 @@ def update_heatmap( State(ElementIds.TAB6_SEC3_FILTER_VAR_DROPDOWN, "value"), State(ElementIds.TAB6_SEC3_MIN_VAL, "value"), State(ElementIds.TAB6_SEC3_MAX_VAL, "value"), - State(ElementIds.META_STORE, "data"), + State(ElementIds.ID_EXPLORER_META_STORE, "data"), State(ElementIds.INVERT_MONTH_EXPLORE_MORE_CHARTS, "value"), State(ElementIds.INVERT_HOUR_EXPLORE_MORE_CHARTS, "value"), - State(ElementIds.SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), ], ) def update_more_charts( @@ -923,7 +923,7 @@ def update_more_charts( ], [ State(ElementIds.ID_EXPLORER_DF_STORE, "data"), - State(ElementIds.SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), State(ElementIds.SEC1_MONTH_SLIDER, "value"), State(ElementIds.SEC1_HOUR_SLIDER, "value"), State(ElementIds.INVERT_MONTH_EXPLORE_DESCRIPTIVE, "value"), diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index c57c6686..38ccf6d5 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -54,19 +54,33 @@ class ElementIds: BEST_CONDITION_TEXT = "outdoor-comfort-output" DAILY = "daily" DF_STORE = "df-store" + ENABLE_CONDENSATION = "enable-condensation" ID_EXPLORER_DF_STORE = "df-store" + ID_EXPLORER_META_STORE = "meta-store" + ID_NATURAL_DF_STORE = "df-store" + ID_NATURAL_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" DROPDOWN = "dropdown" CUSTOM_SUMMARY = "custom-summary" CUSTOM_HEATMAP = "custom-heatmap" GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" HEATMAP = "heatmap" MAIN_NV_SECTION = "main-nv-section" + ID_NATURAL_META_STORE = "meta-store" NORMALIZE = "normalize" + NV_BAR_CHART = "nv-bar-chart" + NV_DBT_FILTER = "nv-dbt-filter" + NV_DPT_FILTER = "nv-dpt-filter" + NV_DPT_MAX_VAL = "nv-dpt-max-val" + NV_TDB_MIN_VAL = "nv-tdb-min-val" + NV_TDB_MAX_VAL = "nv-tdb-max-val" NV_HEATMAP_CHART = "nv-heatmap-chart" HOUR_SLIDER = "outdoor-comfort-hour-slider" + NV_MONTH_HOUR_FILTER = "nv-month-hour-filter" + NV_MONTH_SLIDER = "nv-month-slider" IMAGE_SELECTION = "image-selection" INVERT_HOUR = "invert-hour-outdoor-comfort" INVERT_MONTH = "invert-month-outdoor-comfort" + INVERT_MONTH_NV = "invert-month-nv" ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" INVERT_HOUR_EXPLORE_DESCRIPTIVE = "invert-hour-explore-descriptive" INVERT_MONTH_EXPLORE_DESCRIPTIVE = "invert-month-explore-descriptive" @@ -74,11 +88,13 @@ class ElementIds: INVERT_HOUR_EXPLORE_HEATMAP = "invert-hour-explore-heatmap" INVERT_MONTH_EXPLORE_MORE_CHARTS = "invert-month-explore-more-charts" INVERT_HOUR_EXPLORE_MORE_CHARTS = "invert-hour-explore-more-charts" + INVERT_HOUR_NV = "invert-hour-nv" ID_EXPLORER_SI_IP_UNIT_STORE = "si-ip-unit-store" ID_NATURAL_SI_IP_UNIT_STORE = "si-ip-unit-store" META_STORE = "meta-store" MONTH_SLIDER = "outdoor-comfort-month-slider" NORMALIZE_SWITCH = "outdoor-comfort-switches-input" + NV_HOUR_SLIDER = "nv-hour-slider" PSYCH_CHART = "psych-chart" PSY_COLOR_BY_DROPDOWN = "psy-color-by-dropdown" PSY_DATA_FILTER_BTN = "data-filter" @@ -104,6 +120,7 @@ class ElementIds: SEC2_MAX_VAL = "sec2-max-val" SCENARIO_DROPDOWN = "tab7-dropdown" SI_IP_UNIT_STORE = "si-ip-unit-store" + SWITCHES_INPUT = "switches-input" TABLE_TMP_HUM = "table-tmp-hum" TABLE_DATA_EXPLORER = "table-data-explorer" TAB6_SEC2_CONTAINER = "tab6-sec2-container" diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 7dcfe8fa..b9dbac83 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -1,3 +1,4 @@ + import math import dash @@ -18,7 +19,7 @@ container_col_center_one_of_three, ) from pages.lib.template_graphs import filter_df_by_month_and_hour -from pages.lib.global_column_names import ColNames +from pages.lib.global_column_names import ColNames, ElementIds from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -41,14 +42,17 @@ def layout(): return html.Div( className="container-col", - id="main-nv-section", + id=ElementIds.MAIN_NV_SECTION, children=[ # ], ) -@callback(Output("main-nv-section", "children"), [Input("si-ip-radio-input", "value")]) +@callback( + Output(ElementIds.MAIN_NV_SECTION, "children"), + [Input(ElementIds.ID_NATURAL_SI_IP_UNIT_STORE, "value")], +) def update_layout(si_ip): if si_ip == UnitSystem.IP: tdb_set_min = 50 @@ -70,7 +74,7 @@ def update_layout(si_ip): inputs_tab(tdb_set_min, tdb_set_max, dpt_set), dcc.Loading( html.Div( - id="nv-heatmap-chart", + id=ElementIds.NV_HEATMAP_CHART, style={"marginTop": "1rem"}, ), type="circle", @@ -83,7 +87,7 @@ def update_layout(si_ip): {"label": "", "value": 1}, ], value=[1], - id="switches-input", + id=ElementIds.SWITCHES_INPUT, switch=True, style={ "padding": "1rem", @@ -105,7 +109,7 @@ def update_layout(si_ip): ), dcc.Loading( html.Div( - id="nv-bar-chart", + id=ElementIds.NV_BAR_CHART, style={"marginTop": "1rem"}, ), type="circle", @@ -123,7 +127,7 @@ def inputs_tab(t_min, t_max, d_set): dbc.Button( "Apply filter", color="primary", - id="nv-dbt-filter", + id=ElementIds.NV_DBT_FILTER, className="mb-2", n_clicks=1, ), @@ -133,7 +137,7 @@ def inputs_tab(t_min, t_max, d_set): children=[ html.H6(children=["Min Value:"], style={"flex": "30%"}), dbc.Input( - id="nv-tdb-min-val", + id=ElementIds.NV_TDB_MIN_VAL, placeholder="Enter a number for the min val", type="number", step=1, @@ -147,7 +151,7 @@ def inputs_tab(t_min, t_max, d_set): children=[ html.H6(children=["Max Value:"], style={"flex": "30%"}), dbc.Input( - id="nv-tdb-max-val", + id=ElementIds.NV_TDB_MAX_VAL, placeholder="Enter a number for the max val", type="number", value=t_max, @@ -164,7 +168,7 @@ def inputs_tab(t_min, t_max, d_set): dbc.Button( "Apply month and hour filter", color="primary", - id="nv-month-hour-filter", + id=ElementIds.NV_MONTH_HOUR_FILTER, className="mb-2", n_clicks=0, ), @@ -174,7 +178,7 @@ def inputs_tab(t_min, t_max, d_set): html.H6("Month Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="nv-month-slider", + id=ElementIds.NV_MONTH_SLIDER, min=1, max=12, step=1, @@ -193,7 +197,7 @@ def inputs_tab(t_min, t_max, d_set): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-nv", + id=ElementIds.INVERT_MONTH_NV, labelStyle={"flex": "30%"}, ), ], @@ -204,7 +208,7 @@ def inputs_tab(t_min, t_max, d_set): html.H6("Hour Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="nv-hour-slider", + id=ElementIds.NV_HOUR_SLIDER, min=0, max=24, step=1, @@ -223,7 +227,7 @@ def inputs_tab(t_min, t_max, d_set): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-nv", + id=ElementIds.INVERT_HOUR_NV, labelStyle={"flex": "30%"}, ), ], @@ -236,7 +240,7 @@ def inputs_tab(t_min, t_max, d_set): dbc.Button( "Apply filter", color="primary", - id="nv-dpt-filter", + id=ElementIds.NV_DPT_FILTER, className="mb-2", n_clicks=0, disabled=True, @@ -254,7 +258,7 @@ def inputs_tab(t_min, t_max, d_set): }, ], value=[], - id="enable-condensation", + id=ElementIds.ENABLE_CONDENSATION, ), html.Div( className=container_row_center_full, @@ -264,7 +268,7 @@ def inputs_tab(t_min, t_max, d_set): style={"marginRight": "1rem"}, ), dbc.Input( - id="nv-dpt-max-val", + id=ElementIds.NV_DPT_MAX_VAL, placeholder="Enter a number for the max val", type="number", value=d_set, @@ -280,26 +284,26 @@ def inputs_tab(t_min, t_max, d_set): @callback( - Output("nv-heatmap-chart", "children"), + Output(ElementIds.NV_HEATMAP_CHART, "children"), [ - Input("df-store", "modified_timestamp"), - Input("nv-month-hour-filter", "n_clicks"), - Input("nv-dbt-filter", "n_clicks"), - Input("nv-dpt-filter", "n_clicks"), - Input("global-local-radio-input", "value"), - Input("enable-condensation", "value"), + Input(ElementIds.ID_NATURAL_DF_STORE, "modified_timestamp"), + Input(ElementIds.NV_MONTH_HOUR_FILTER, "n_clicks"), + Input(ElementIds.NV_DBT_FILTER, "n_clicks"), + Input(ElementIds.NV_DPT_FILTER, "n_clicks"), + Input(ElementIds.ID_NATURAL_GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ENABLE_CONDENSATION, "value"), ], [ - State("df-store", "data"), - State("nv-month-slider", "value"), - State("nv-hour-slider", "value"), - State("nv-tdb-min-val", "value"), - State("nv-tdb-max-val", "value"), - State("nv-dpt-max-val", "value"), - State("meta-store", "data"), - State("invert-month-nv", "value"), - State("invert-hour-nv", "value"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_NATURAL_DF_STORE, "data"), + State(ElementIds.NV_MONTH_SLIDER, "value"), + State(ElementIds.NV_HOUR_SLIDER, "value"), + State(ElementIds.NV_TDB_MIN_VAL, "value"), + State(ElementIds.NV_TDB_MAX_VAL, "value"), + State(ElementIds.NV_DPT_MAX_VAL, "value"), + State(ElementIds.ID_NATURAL_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_NV, "value"), + State(ElementIds.INVERT_HOUR_NV, "value"), + State(ElementIds.ID_NATURAL_SI_IP_UNIT_STORE, "data"), ], ) def nv_heatmap( @@ -447,26 +451,26 @@ def nv_heatmap( @callback( - Output("nv-bar-chart", "children"), + Output(ElementIds.NV_BAR_CHART, "children"), [ - Input("df-store", "modified_timestamp"), - Input("nv-month-hour-filter", "n_clicks"), - Input("nv-dbt-filter", "n_clicks"), - Input("nv-dpt-filter", "n_clicks"), - Input("switches-input", "value"), - Input("enable-condensation", "value"), + Input(ElementIds.ID_NATURAL_DF_STORE, "modified_timestamp"), + Input(ElementIds.NV_MONTH_HOUR_FILTER, "n_clicks"), + Input(ElementIds.NV_DBT_FILTER, "n_clicks"), + Input(ElementIds.NV_DPT_FILTER, "n_clicks"), + Input(ElementIds.SWITCHES_INPUT, "value"), + Input(ElementIds.ENABLE_CONDENSATION, "value"), ], [ - State("df-store", "data"), - State("nv-month-slider", "value"), - State("nv-hour-slider", "value"), - State("nv-tdb-min-val", "value"), - State("nv-tdb-max-val", "value"), - State("nv-dpt-max-val", "value"), - State("meta-store", "data"), - State("invert-month-nv", "value"), - State("invert-hour-nv", "value"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_NATURAL_DF_STORE, "data"), + State(ElementIds.NV_MONTH_SLIDER, "value"), + State(ElementIds.NV_HOUR_SLIDER, "value"), + State(ElementIds.NV_TDB_MIN_VAL, "value"), + State(ElementIds.NV_TDB_MAX_VAL, "value"), + State(ElementIds.NV_DPT_MAX_VAL, "value"), + State(ElementIds.ID_NATURAL_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_NV, "value"), + State(ElementIds.INVERT_HOUR_NV, "value"), + State(ElementIds.ID_NATURAL_SI_IP_UNIT_STORE, "data"), ], ) def nv_bar_chart( @@ -513,7 +517,9 @@ def nv_bar_chart( ) # this should be the total after filtering by time - tot_month_hours = df.groupby(df[ColNames.UTC_TIME].dt.month)["nv_allowed"].sum().values + tot_month_hours = ( + df.groupby(df[ColNames.UTC_TIME].dt.month)["nv_allowed"].sum().values + ) if dbt_data_filter and (min_dbt_val <= max_dbt_val): df.loc[(df[var] < min_dbt_val) | (df[var] > max_dbt_val), "nv_allowed"] = 0 @@ -610,7 +616,10 @@ def nv_bar_chart( ) -@callback(Output("nv-dpt-filter", "disabled"), Input("enable-condensation", "value")) +@callback( + Output(ElementIds.NV_DPT_FILTER, "disabled"), + Input(ElementIds.ENABLE_CONDENSATION, "value"), +) def enable_disable_button_data_filter(state_checklist): if len(state_checklist) == 1: return False From 85d45a03de7d7a6a4f89e92cc65635a5746887b8 Mon Sep 17 00:00:00 2001 From: Kira-Liu00 Date: Sun, 24 Aug 2025 17:37:10 +1000 Subject: [PATCH 27/66] fix: Updated the ElementIds of outdoor.py, psy-chart.py, global_column_names.py and natural_ventilation.py --- pages/lib/global_column_names.py | 43 ++++++++----- pages/natural_ventilation.py | 2 +- pages/outdoor.py | 104 +++++++++++++++---------------- pages/psy-chart.py | 54 ++++++++-------- 4 files changed, 107 insertions(+), 96 deletions(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 31b21731..848da917 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -51,7 +51,7 @@ class ColNames(str, Enum): class ElementIds: # ==================== Defines the unique ID constant for each element in the front-end page ==================== - BEST_CONDITION_TEXT = "outdoor-comfort-output" + OUTDOOR_COMFORT_OUTPUT = "outdoor-comfort-output" DAILY = "daily" DF_STORE = "df-store" ENABLE_CONDENSATION = "enable-condensation" @@ -74,12 +74,12 @@ class ElementIds: NV_TDB_MIN_VAL = "nv-tdb-min-val" NV_TDB_MAX_VAL = "nv-tdb-max-val" NV_HEATMAP_CHART = "nv-heatmap-chart" - HOUR_SLIDER = "outdoor-comfort-hour-slider" + OUTDOOR_COMFORT_HOUR_SLIDER = "outdoor-comfort-hour-slider" NV_MONTH_HOUR_FILTER = "nv-month-hour-filter" NV_MONTH_SLIDER = "nv-month-slider" IMAGE_SELECTION = "image-selection" - INVERT_HOUR = "invert-hour-outdoor-comfort" - INVERT_MONTH = "invert-month-outdoor-comfort" + INVERT_HOUR_OUTDOOR_COMFORT = "invert-hour-outdoor-comfort" + INVERT_MONTH_OUTDOOR_COMFORT = "invert-month-outdoor-comfort" INVERT_MONTH_NV = "invert-month-nv" ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" INVERT_HOUR_EXPLORE_DESCRIPTIVE = "invert-hour-explore-descriptive" @@ -91,27 +91,31 @@ class ElementIds: INVERT_HOUR_NV = "invert-hour-nv" ID_EXPLORER_SI_IP_UNIT_STORE = "si-ip-unit-store" ID_NATURAL_SI_IP_UNIT_STORE = "si-ip-unit-store" + SI_IP_RADIO_INPUT = "si-ip-radio-input" + META_STORE = "meta-store" - MONTH_SLIDER = "outdoor-comfort-month-slider" - NORMALIZE_SWITCH = "outdoor-comfort-switches-input" + OUTDOOR_COMFORT_MONTH_SLIDER = "outdoor-comfort-month-slider" + OUTDOOR_COMFORT_SWITCHES_INPUT = "outdoor-comfort-switches-input" NV_HOUR_SLIDER = "nv-hour-slider" PSYCH_CHART = "psych-chart" PSY_CHART_BTN = "psy-chart-btn" PSY_COLOR_BY_DROPDOWN = "psy-color-by-dropdown" - PSY_DATA_FILTER_BTN = "data-filter" - PSY_FILTER_VAR_DROPDOWN = "psy-var-dropdown" + DATA_FILTER = "data-filter" + PSY_VAR_DROPDOWN = "psy-var-dropdown" PSY_HOUR_SLIDER = "psy-hour-slider" - PSY_INVERT_HOUR = "invert-hour-psy" - PSY_INVERT_MONTH = "invert-month-psy" - PSY_MAX_VAL_INPUT = "psy-max-val" - PSY_MIN_VAL_INPUT = "psy-min-val" + INVERT_HOUR_PSY = "invert-hour-psy" + INVERT_MONTH_PSY = "invert-month-psy" + PSY_MAX_VAL = "psy-max-val" + PSY_MIN_VAL = "psy-min-val" PSY_MONTH_SLIDER = "psy-month-slider" - PSY_TIME_FILTER_BTN = "month-hour-filter" + ID_OUTDOOR_MONTH_HOUR_FILTER = "month-hour-filter" + MONTH_HOUR_FILTER = "month-hour-filter" SEC1_HOUR_SLIDER = "sec1-hour-slider" SEC1_MONTH_SLIDER = "sec1-month-slider" SEC1_TIME_FILTER_INPUT = "sec1-time-filter-input" SEC1_VAR_DROPDOWN = "sec1-var-dropdown" + SDATA_FILTER = "data-filter" SEC2_DATA_FILTER_INPUT = "sec2-data-filter-input" SEC2_DATA_FILTER_VAR = "sec2-data-filter-var" SEC2_VAR_DROPDOWN = "sec2-var-dropdown" @@ -120,7 +124,7 @@ class ElementIds: SEC2_MONTH_SLIDER = "sec2-month-slider" SEC2_MIN_VAL = "sec2-min-val" SEC2_MAX_VAL = "sec2-max-val" - SCENARIO_DROPDOWN = "tab7-dropdown" + TAB7_DROPDOWN = "tab7-dropdown" SI_IP_UNIT_STORE = "si-ip-unit-store" SWITCHES_INPUT = "switches-input" TABLE_TMP_HUM = "table-tmp-hum" @@ -136,7 +140,7 @@ class ElementIds: TAB6_SEC3_VAR_X_DROPDOWN = "tab6-sec3-var-x-dropdown" TAB6_SEC3_VAR_Y_DROPDOWN = "tab6-sec3-var-y-dropdown" TAB6_SEC3_COLORBY_DROPDOWN = "tab6-sec3-colorby-dropdown" - TIME_FILTER_BTN = "month-hour-filter-outdoor-comfort" + MONTH_HOUR_FILTER_OUTDOOR_COMFORT = "month-hour-filter-outdoor-comfort" TWO_VAR = "two-var" THREE_VAR = "three-var" QUERY_DAILY = "query-daily" @@ -146,7 +150,14 @@ class ElementIds: UTCI_SUMMARY_CHART = "utci-summary-chart" YEARLY_CHART = "yearly-chart" YEARLY_EXPLORE = "yearly-explore" - + ID_OUTDOOR_DF_STORE = "df-store" + ID_OUTDOOR_META_STORE = "meta-store" + ID_OUTDOOR_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_PSY_CHART_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_OUTDOOR_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_PSY_CHART_DF_STORE = "df-store" + ID_PSY_CHART_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_PSY_CHART_META_STORE = "meta-store" class ComponentProperty: # ==================== Define common attribute name constants for components ==================== diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index b9dbac83..3c16861c 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -51,7 +51,7 @@ def layout(): @callback( Output(ElementIds.MAIN_NV_SECTION, "children"), - [Input(ElementIds.ID_NATURAL_SI_IP_UNIT_STORE, "value")], + [Input(ElementIds.SI_IP_RADIO_INPUT, "value")], ) def update_layout(si_ip): if si_ip == UnitSystem.IP: diff --git a/pages/outdoor.py b/pages/outdoor.py index 72c57cc0..d6a805be 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -48,12 +48,12 @@ def inputs_outdoor_comfort(): style={"flex": "30%"}, ), dropdown( - id="tab7-dropdown", + id=ElementIds.TAB7_DROPDOWN, style={"flex": "60%"}, options=outdoor_dropdown_names, value="utci_Sun_Wind", ), - html.Div(id="image-selection", style={"flex": "10%"}), + html.Div(id=ElementIds.IMAGE_SELECTION, style={"flex": "10%"}), ], ), ], @@ -68,7 +68,7 @@ def inputs_outdoor_comfort(): style={ "width": "100%", }, - id="month-hour-filter-outdoor-comfort", + id=ElementIds.MONTH_HOUR_FILTER_OUTDOOR_COMFORT, className="mb-2", n_clicks=0, ), @@ -78,7 +78,7 @@ def inputs_outdoor_comfort(): html.H6("Month Range", style={"flex": "5%"}), html.Div( dcc.RangeSlider( - id="outdoor-comfort-month-slider", + id=ElementIds.OUTDOOR_COMFORT_MONTH_SLIDER, min=1, max=12, step=1, @@ -97,7 +97,7 @@ def inputs_outdoor_comfort(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-outdoor-comfort", + id=ElementIds.INVERT_MONTH_OUTDOOR_COMFORT, labelStyle={"flex": "30%"}, ), ], @@ -108,7 +108,7 @@ def inputs_outdoor_comfort(): html.H6("Hour Range", style={"flex": "5%"}), html.Div( dcc.RangeSlider( - id="outdoor-comfort-hour-slider", + id=ElementIds.OUTDOOR_COMFORT_HOUR_SLIDER, min=0, max=24, step=1, @@ -127,7 +127,7 @@ def inputs_outdoor_comfort(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-outdoor-comfort", + id=ElementIds.INVERT_HOUR_OUTDOOR_COMFORT, labelStyle={"flex": "30%"}, ), ], @@ -141,7 +141,7 @@ def inputs_outdoor_comfort(): def outdoor_comfort_chart(): return html.Div( children=[ - html.Div(id="outdoor-comfort-output"), + html.Div(id=ElementIds.OUTDOOR_COMFORT_OUTPUT), html.Div( children=title_with_link( text="UTCI heatmap chart", @@ -150,7 +150,7 @@ def outdoor_comfort_chart(): ) ), dcc.Loading( - html.Div(id="utci-heatmap"), + html.Div(id=ElementIds.UTCI_HEATMAP), type="circle", ), html.Div( @@ -161,7 +161,7 @@ def outdoor_comfort_chart(): ) ), dcc.Loading( - html.Div(id="utci-category-heatmap"), + html.Div(id=ElementIds.UTCI_CATEGORY_HEATMAP), type="circle", ), html.Div( @@ -172,7 +172,7 @@ def outdoor_comfort_chart(): {"label": "", "value": 1}, ], value=[1], - id="outdoor-comfort-switches-input", + id=ElementIds.OUTDOOR_COMFORT_SWITCHES_INPUT, switch=True, style={ "padding": "1rem", @@ -193,7 +193,7 @@ def outdoor_comfort_chart(): ], ), dcc.Loading( - html.Div(id="utci-summary-chart"), + html.Div(id=ElementIds.UTCI_SUMMARY_CHART), type="circle", ), ], @@ -213,12 +213,12 @@ def layout(): @callback( - Output("outdoor-comfort-output", "children"), + Output(ElementIds.OUTDOOR_COMFORT_OUTPUT, "children"), [ - Input("df-store", "modified_timestamp"), + Input(ElementIds.ID_OUTDOOR_DF_STORE, "modified_timestamp"), ], [ - State("df-store", "data"), + State(ElementIds.ID_OUTDOOR_DF_STORE, "data"), ], ) def update_outdoor_comfort_output(_, df): @@ -245,21 +245,21 @@ def update_outdoor_comfort_output(_, df): @callback( - Output("utci-heatmap", "children"), + Output(ElementIds.UTCI_HEATMAP, "children"), [ - Input("df-store", "modified_timestamp"), - Input("tab7-dropdown", "value"), - Input("global-local-radio-input", "value"), - Input("month-hour-filter-outdoor-comfort", "n_clicks"), + Input(ElementIds.ID_OUTDOOR_DF_STORE, "modified_timestamp"), + Input(ElementIds.TAB7_DROPDOWN, "value"), + Input(ElementIds.ID_OUTDOOR_GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.MONTH_HOUR_FILTER_OUTDOOR_COMFORT, "n_clicks"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), - State("outdoor-comfort-month-slider", "value"), - State("outdoor-comfort-hour-slider", "value"), - State("invert-month-outdoor-comfort", "value"), - State("invert-hour-outdoor-comfort", "value"), + State(ElementIds.ID_OUTDOOR_DF_STORE, "data"), + State(ElementIds.ID_OUTDOOR_META_STORE, "data"), + State(ElementIds.ID_OUTDOOR_SI_IP_UNIT_STORE, "data"), + State(ElementIds.OUTDOOR_COMFORT_MONTH_SLIDER, "value"), + State(ElementIds.OUTDOOR_COMFORT_HOUR_SLIDER, "value"), + State(ElementIds.INVERT_MONTH_OUTDOOR_COMFORT, "value"), + State(ElementIds.INVERT_HOUR_OUTDOOR_COMFORT, "value"), ], ) def update_tab_utci_value( @@ -295,8 +295,8 @@ def update_tab_utci_value( @callback( - Output("image-selection", "children"), - Input("tab7-dropdown", "value"), + Output(ElementIds.IMAGE_SELECTION, "children"), + Input(ElementIds.TAB7_DROPDOWN, "value"), ) def change_image_based_on_selection(value): if value == "utci_Sun_Wind": @@ -312,21 +312,21 @@ def change_image_based_on_selection(value): @callback( - Output("utci-category-heatmap", "children"), + Output(ElementIds.UTCI_CATEGORY_HEATMAP, "children"), [ - Input("df-store", "modified_timestamp"), - Input("tab7-dropdown", "value"), - Input("global-local-radio-input", "value"), - Input("month-hour-filter-outdoor-comfort", "n_clicks"), + Input(ElementIds.ID_OUTDOOR_DF_STORE, "modified_timestamp"), + Input(ElementIds.TAB7_DROPDOWN, "value"), + Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.MONTH_HOUR_FILTER_OUTDOOR_COMFORT, "n_clicks"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), - State("outdoor-comfort-month-slider", "value"), - State("outdoor-comfort-hour-slider", "value"), - State("invert-month-outdoor-comfort", "value"), - State("invert-hour-outdoor-comfort", "value"), + State(ElementIds.ID_OUTDOOR_DF_STORE, "data"), + State(ElementIds.ID_OUTDOOR_META_STORE, "data"), + State(ElementIds.ID_OUTDOOR_SI_IP_UNIT_STORE, "data"), + State(ElementIds.OUTDOOR_COMFORT_MONTH_SLIDER, "value"), + State(ElementIds.OUTDOOR_COMFORT_HOUR_SLIDER, "value"), + State(ElementIds.INVERT_MONTH_OUTDOOR_COMFORT, "value"), + State(ElementIds.INVERT_HOUR_OUTDOOR_COMFORT, "value"), ], ) def update_tab_utci_category( @@ -382,20 +382,20 @@ def update_tab_utci_category( @callback( - Output("utci-summary-chart", "children"), + Output(ElementIds.UTCI_SUMMARY_CHART, "children"), [ - Input("tab7-dropdown", "value"), - Input("month-hour-filter-outdoor-comfort", "n_clicks"), - Input("outdoor-comfort-switches-input", "value"), + Input(ElementIds.TAB7_DROPDOWN, "value"), + Input(ElementIds.MONTH_HOUR_FILTER_OUTDOOR_COMFORT, "n_clicks"), + Input(ElementIds.OUTDOOR_COMFORT_SWITCHES_INPUT, "value"), ], [ - State("df-store", "data"), - State("outdoor-comfort-month-slider", "value"), - State("outdoor-comfort-hour-slider", "value"), - State("meta-store", "data"), - State("invert-month-outdoor-comfort", "value"), - State("invert-hour-outdoor-comfort", "value"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_OUTDOOR_DF_STORE, "data"), + State(ElementIds.OUTDOOR_COMFORT_MONTH_SLIDER, "value"), + State(ElementIds.OUTDOOR_COMFORT_HOUR_SLIDER, "value"), + State(ElementIds.ID_OUTDOOR_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_OUTDOOR_COMFORT, "value"), + State(ElementIds.INVERT_HOUR_OUTDOOR_COMFORT, "value"), + State(ElementIds.ID_OUTDOOR_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_utci_summary_chart( diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 61b85a58..a0ba9d1d 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -72,7 +72,7 @@ def inputs(): style={"flex": "30%"}, ), dropdown( - id="psy-color-by-dropdown", + id=ElementIds.PSY_COLOR_BY_DROPDOWN, options=psy_dropdown_names, value="Frequency", style={"flex": "70%"}, @@ -89,7 +89,7 @@ def inputs(): dbc.Button( "Apply month and hour filter", color="primary", - id="month-hour-filter", + id=ElementIds.MONTH_HOUR_FILTER, className="mb-2", n_clicks=0, ), @@ -99,7 +99,7 @@ def inputs(): html.H6("Month Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="psy-month-slider", + id=ElementIds.PSY_MONTH_SLIDER, min=1, max=12, step=1, @@ -118,7 +118,7 @@ def inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-psy", + id=ElementIds.INVERT_MONTH_PSY, labelStyle={"flex": "30%"}, ), ], @@ -129,7 +129,7 @@ def inputs(): html.H6("Hour Range", style={"flex": "20%"}), html.Div( dcc.RangeSlider( - id="psy-hour-slider", + id=ElementIds.PSY_HOUR_SLIDER, min=0, max=24, step=1, @@ -148,7 +148,7 @@ def inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-psy", + id=ElementIds.INVERT_HOUR_PSY, labelStyle={"flex": "30%"}, ), ], @@ -161,7 +161,7 @@ def inputs(): dbc.Button( "Apply filter", color="primary", - id="data-filter", + id=ElementIds.DATA_FILTER, className="mb-2", n_clicks=0, ), @@ -172,7 +172,7 @@ def inputs(): children=["Filter Variable:"], style={"flex": "30%"} ), dropdown( - id="psy-var-dropdown", + id=ElementIds.PSY_VAR_DROPDOWN , options=dropdown_names, value="RH", style={"flex": "70%"}, @@ -184,7 +184,7 @@ def inputs(): children=[ html.H6(children=["Min Value:"], style={"flex": "30%"}), dbc.Input( - id="psy-min-val", + id=ElementIds.PSY_MIN_VAL, placeholder="Enter a number for the min val", type="number", step=1, @@ -198,7 +198,7 @@ def inputs(): children=[ html.H6(children=["Max Value:"], style={"flex": "30%"}), dbc.Input( - id="psy-max-val", + id=ElementIds.PSY_MAX_VAL, placeholder="Enter a number for the max val", type="number", value=100, @@ -226,7 +226,7 @@ def layout(): type="circle", children=html.Div( className="container-col", - children=[inputs(), html.Div(id="psych-chart")], + children=[inputs(), html.Div(id="ElementIds.PSYCH_CHART")], ), ), ) @@ -234,25 +234,25 @@ def layout(): # psychrometric chart @callback( - Output("psych-chart", "children"), + Output(ElementIds.PSYCH_CHART, "children"), [ - Input("df-store", "modified_timestamp"), - Input("psy-color-by-dropdown", "value"), - Input("month-hour-filter", "n_clicks"), - Input("data-filter", "n_clicks"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_PSY_CHART_DF_STORE, "modified_timestamp"), + Input(ElementIds.PSY_COLOR_BY_DROPDOWN, "value"), + Input(ElementIds.MONTH_HOUR_FILTER, "n_clicks"), + Input(ElementIds.DATA_FILTER, "n_clicks"), + Input(ElementIds.ID_PSY_CHART_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("psy-month-slider", "value"), - State("psy-hour-slider", "value"), - State("psy-min-val", "value"), - State("psy-max-val", "value"), - State("psy-var-dropdown", "value"), - State("meta-store", "data"), - State("invert-month-psy", "value"), - State("invert-hour-psy", "value"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_PSY_CHART_DF_STORE, "data"), + State(ElementIds.PSY_MONTH_SLIDER, "value"), + State(ElementIds.PSY_HOUR_SLIDER, "value"), + State(ElementIds.PSY_MIN_VAL, "value"), + State(ElementIds.PSY_MAX_VAL, "value"), + State(ElementIds.PSY_VAR_DROPDOWN , "value"), + State(ElementIds.ID_PSY_CHART_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_PSY, "value"), + State(ElementIds.INVERT_HOUR_PSY, "value"), + State(ElementIds.ID_PSY_CHART_SI_IP_UNIT_STORE, "data"), ], ) def update_psych_chart( From 1e1cccfdfdd6362e9fe4ce490fb789c5f028ff32 Mon Sep 17 00:00:00 2001 From: FengW01 Date: Sun, 24 Aug 2025 17:52:21 +1000 Subject: [PATCH 28/66] Fix: update the sun.py, wind.py and global_column_names.py --- pages/lib/global_column_names.py | 48 ++++++++++ pages/psy-chart.py | 2 +- pages/sun.py | 79 ++++++++-------- pages/wind.py | 149 ++++++++++++++++--------------- 4 files changed, 164 insertions(+), 114 deletions(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 848da917..c959918e 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -159,6 +159,54 @@ class ElementIds: ID_PSY_CHART_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" ID_PSY_CHART_META_STORE = "meta-store" + CUSTOM_SUN_VIEW_DROPDOWN = "custom-sun-view-dropdown" + CUSTOM_SUN_VAR_DROPDOWN = "custom-sun-var-dropdown" + CUSTOM_SUNPATH = "custom-sunpath" + TAB_EXPLORE_DROPDOWN = "tab4-explore-dropdown" + TAB4_DAILY = "tab4-daily" + TAB4_HEATMAP = "tab4-heatmap" + STATIC_SECTION = "static-section" + TAB_FOUR_CONTAINER = "tab-four-container" + MONTHLY_SOLAR = "monthly-solar" + CLOUD_COVER = "cloud-cover" + ID_SUN_SI_IP_RADIO_INPUT = "si-ip-radio-input" + ID_SUN_DF_STORE = "df-store" + ID_SUN_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_SUN_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_SUN_META_STORE = "meta-store" + SLIDER_CONTAINER = "slider-container" + MONTH_SLIDER = "month-slider" + HOUR_SLIDER = "hour-slider" + WINTER_WIND_ROSE = "winter-wind-rose" + WINTER_WIND_ROSE_TEXT = "winter-wind-rose-text" + SPRING_WIND_ROSE = "spring-wind-rose" + SPRING_WIND_ROSE_TEXT = "spring-wind-rose-text" + SUMMER_WIND_ROSE = "summer-wind-rose" + SUMMER_WIND_ROSE_TEXT = "summer-wind-rose-text" + FALL_WIND_ROSE = "fall-wind-rose" + FALL_WIND_ROSE_TEXT = "fall-wind-rose-text" + TAB5_DAILY_CONTAINER = "tab5-daily-container" + DAILY_WIND_ROSE_OUTER_CONTAINER = "daily-wind-rose-outer-container" + MORNING_WIND_ROSE = "morning-wind-rose" + MORNING_WIND_ROSE_TEXT = "morning-wind-rose-text" + NOON_WIND_ROSE = "noon-wind-rose" + NOON_WIND_ROSE_TEXT = "noon-wind-rose-text" + NIGHT_WIND_ROSE = "night-wind-rose" + NIGHT_WIND_ROSE_TEXT = "night-wind-rose-text" + TAB5_CUSTOM_DROPDOWN_CONTAINER = "tab5-custom-dropdown-container" + TAB5_CUSTOM_START_MONTH = "tab5-custom-start-month" + TAB5_CUSTOM_START_HOUR = "tab5-custom-start-hour" + TAB5_CUSTOM_END_MONTH = "tab5-custom-end-month" + TAB5_CUSTOM_END_HOUR = "tab5-custom-end-hour" + CUSTOM_WIND_ROSE = "custom-wind-rose" + WIND_ROSE = "wind-rose" + WIND_SPEED = "wind-speed" + WIND_DIRECTION = "wind-direction" + ID_WIND_DF_STORE = "df-store" + ID_WIND_META_STORE = "meta-store" + ID_WIND_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_WINDGLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + class ComponentProperty: # ==================== Define common attribute name constants for components ==================== CHILDREN = "children" diff --git a/pages/psy-chart.py b/pages/psy-chart.py index a0ba9d1d..83ba076f 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -226,7 +226,7 @@ def layout(): type="circle", children=html.Div( className="container-col", - children=[inputs(), html.Div(id="ElementIds.PSYCH_CHART")], + children=[inputs(), html.Div(id=ElementIds.PSYCH_CHART)], ), ), ) diff --git a/pages/sun.py b/pages/sun.py index a77c4c7f..7d35fae8 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -1,4 +1,5 @@ from copy import deepcopy +from pages.lib.global_column_names import ElementIds import dash import dash_bootstrap_components as dbc @@ -71,7 +72,7 @@ def sun_path(): style={"width": "10rem"}, ), dropdown( - id="custom-sun-view-dropdown", + id= ElementIds.CUSTOM_SUN_VIEW_DROPDOWN, options={ "Spherical": "polar", "Cartesian": "cartesian", @@ -91,7 +92,7 @@ def sun_path(): style={"width": "10rem"}, ), dropdown( - id="custom-sun-var-dropdown", + id= ElementIds.CUSTOM_SUN_VAR_DROPDOWN, options=sc_dropdown_names, value="None", style={"width": "20rem"}, @@ -101,7 +102,7 @@ def sun_path(): dcc.Loading( type="circle", children=html.Div( - id="custom-sunpath", + id= ElementIds.CUSTOM_SUNPATH, ), ), ], @@ -129,17 +130,17 @@ def explore_daily_heatmap(): style={"width": "10rem"}, ), dropdown( - id="tab4-explore-dropdown", + id=ElementIds.TAB_EXPLORE_DROPDOWN, options=sun_cloud_tab_explore_dropdown_names, value="glob_hor_rad", style={"width": "20rem"}, ), ], ), - dcc.Loading(type="circle", children=html.Div(id="tab4-daily")), + dcc.Loading(type="circle", children=html.Div(id= ElementIds.TAB4_DAILY)), dcc.Loading( type="circle", - children=html.Div(id="tab4-heatmap"), + children=html.Div(id=ElementIds.TAB4_HEATMAP), ), ], ) @@ -147,7 +148,7 @@ def explore_daily_heatmap(): def static_section(): return html.Div( - id="static-section", + id= ElementIds.STATIC_SECTION, className="container-col full-width", children=[ # ... @@ -159,12 +160,12 @@ def layout(): """Contents of tab four.""" return html.Div( className="container-col", - id="tab-four-container", + id= ElementIds.TAB_FOUR_CONTAINER, children=[sun_path(), static_section(), explore_daily_heatmap()], ) -@callback(Output("static-section", "children"), [Input("si-ip-radio-input", "value")]) +@callback(Output(ElementIds.STATIC_SECTION, "children"), [Input(ElementIds.ID_SUN_SI_IP_RADIO_INPUT, "value")]) def update_static_section(si_ip): hor_unit = "Wh/m²" if si_ip == UnitSystem.IP: @@ -179,7 +180,7 @@ def update_static_section(si_ip): ), dcc.Loading( type="circle", - children=html.Div(id="monthly-solar"), + children=html.Div(id= ElementIds.MONTHLY_SOLAR), ), html.Div( children=title_with_link( @@ -190,23 +191,23 @@ def update_static_section(si_ip): ), dcc.Loading( type="circle", - children=html.Div(id="cloud-cover"), + children=html.Div(id= ElementIds.CLOUD_COVER), ), ] @callback( [ - Output("monthly-solar", "children"), - Output("cloud-cover", "children"), + Output(ElementIds.MONTHLY_SOLAR, "children"), + Output(ElementIds.CLOUD_COVER, "children"), ], [ - Input("df-store", "modified_timestamp"), + Input(ElementIds.ID_SUN_DF_STORE, "modified_timestamp"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUN_DF_STORE, "data"), + State(ElementIds.ID_SUN_META_STORE, "data"), + State(ElementIds.ID_SUN_SI_IP_UNIT_STORE, "data"), ], ) def monthly_and_cloud_chart(_, df, meta, si_ip): @@ -239,17 +240,17 @@ def monthly_and_cloud_chart(_, df, meta, si_ip): @callback( - Output("custom-sunpath", "children"), + Output(ElementIds.CUSTOM_SUNPATH, "children"), [ - Input("df-store", "modified_timestamp"), - Input("custom-sun-view-dropdown", "value"), - Input("custom-sun-var-dropdown", "value"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_SUN_DF_STORE, "modified_timestamp"), + Input(ElementIds.CUSTOM_SUN_VIEW_DROPDOWN, "value"), + Input(ElementIds.CUSTOM_SUN_VAR_DROPDOWN, "value"), + Input(ElementIds.ID_SUN_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUN_DF_STORE, "data"), + State(ElementIds.ID_SUN_META_STORE, "data"), + State(ElementIds.ID_SUN_SI_IP_UNIT_STORE, "data"), ], ) def sun_path_chart(_, view, var, global_local, df, meta, si_ip): @@ -269,16 +270,16 @@ def sun_path_chart(_, view, var, global_local, df, meta, si_ip): @callback( - Output("tab4-daily", "children"), + Output(ElementIds.TAB4_DAILY, "children"), [ - Input("df-store", "modified_timestamp"), - Input("tab4-explore-dropdown", "value"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_SUN_DF_STORE, "modified_timestamp"), + Input(ElementIds.TAB_EXPLORE_DROPDOWN, "value"), + Input(ElementIds.ID_SUN_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUN_DF_STORE, "data"), + State(ElementIds.ID_SUN_META_STORE, "data"), + State(ElementIds.ID_SUN_SI_IP_UNIT_STORE, "data"), ], ) def daily(_, var, global_local, df, meta, si_ip): @@ -292,16 +293,16 @@ def daily(_, var, global_local, df, meta, si_ip): @callback( - Output("tab4-heatmap", "children"), + Output(ElementIds.TAB4_HEATMAP, "children"), [ - Input("df-store", "modified_timestamp"), - Input("tab4-explore-dropdown", "value"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_SUN_DF_STORE, "modified_timestamp"), + Input(ElementIds.TAB_EXPLORE_DROPDOWN, "value"), + Input(ElementIds.ID_SUN_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUN_DF_STORE, "data"), + State(ElementIds.ID_SUN_META_STORE, "data"), + State(ElementIds.ID_SUN_SI_IP_UNIT_STORE, "data"), ], ) def update_heatmap(_, var, global_local, df, meta, si_ip): diff --git a/pages/wind.py b/pages/wind.py index fe0d3da8..d123a0b3 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -1,6 +1,7 @@ import dash from dash import dcc, html from dash_extensions.enrich import Output, Input, State, callback +from pages.lib.global_column_names import ElementIds from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import month_lst, container_row_center_full @@ -28,14 +29,14 @@ def sliders(): """Returns 2 sliders for the hour""" return html.Div( className="container-col justify-center", - id="slider-container", + id=ElementIds.SLIDER_CONTAINER, children=[ html.Div( className="container-row each-slider", children=[ html.P("Month Range"), dcc.RangeSlider( - id="month-slider", + id=ElementIds.MONTH_SLIDER, min=1, max=12, step=1, @@ -51,7 +52,7 @@ def sliders(): children=[ html.P("Hour Range"), dcc.RangeSlider( - id="hour-slider", + id=ElementIds.HOUR_SLIDER, min=1, max=24, step=1, @@ -87,12 +88,12 @@ def seasonal_wind_rose(): dcc.Loading( type="circle", children=html.Div( - id="winter-wind-rose", + id=ElementIds.WINTER_WIND_ROSE, className="daily-wind-graph", ), ), html.P( - className="seasonal-text", id="winter-wind-rose-text" + className="seasonal-text", id=ElementIds.WINTER_WIND_ROSE_TEXT ), ], ), @@ -102,12 +103,12 @@ def seasonal_wind_rose(): dcc.Loading( type="circle", children=html.Div( - id="spring-wind-rose", + id=ElementIds.SPRING_WIND_ROSE, className="daily-wind-graph", ), ), html.P( - className="seasonal-text", id="spring-wind-rose-text" + className="seasonal-text", id=ElementIds.SPRING_WIND_ROSE_TEXT ), ], ), @@ -122,12 +123,12 @@ def seasonal_wind_rose(): dcc.Loading( type="circle", children=html.Div( - id="summer-wind-rose", + id= ElementIds.SUMMER_WIND_ROSE, className="daily-wind-graph", ), ), html.P( - className="seasonal-text", id="summer-wind-rose-text" + className="seasonal-text", id=ElementIds.SUMMER_WIND_ROSE_TEXT ), ], ), @@ -137,11 +138,11 @@ def seasonal_wind_rose(): dcc.Loading( type="circle", children=html.Div( - id="fall-wind-rose", + id=ElementIds.FALL_WIND_ROSE, className="daily-wind-graph", ), ), - html.P(className="seasonal-text", id="fall-wind-rose-text"), + html.P(className="seasonal-text", id=ElementIds.FALL_WIND_ROSE_TEXT), ], ), ], @@ -154,7 +155,7 @@ def daily_wind_rose(): """Return the section for the 3 daily wind rose graphs.""" return html.Div( className="container-col full-width", - id="tab5-daily-container", + id=ElementIds.TAB5_DAILY_CONTAINER, children=[ html.Div( children=title_with_link( @@ -164,7 +165,7 @@ def daily_wind_rose(): ), ), html.Div( - id="daily-wind-rose-outer-container", + id=ElementIds.DAILY_WIND_ROSE_OUTER_CONTAINER, className="container-row full-width", children=[ html.Div( @@ -175,11 +176,11 @@ def daily_wind_rose(): type="circle", children=html.Div( className="daily-wind-graph", - id="morning-wind-rose", + id=ElementIds.MORNING_WIND_ROSE, ), ), ), - html.P(className="daily-text", id="morning-wind-rose-text"), + html.P(className="daily-text", id=ElementIds.MORNING_WIND_ROSE_TEXT), ], ), html.Div( @@ -190,11 +191,11 @@ def daily_wind_rose(): type="circle", children=html.Div( className="daily-wind-graph", - id="noon-wind-rose", + id=ElementIds.NOON_WIND_ROSE, ), ), ), - html.P(className="daily-text", id="noon-wind-rose-text"), + html.P(className="daily-text", id=ElementIds.NOON_WIND_ROSE_TEXT), ], ), html.Div( @@ -205,11 +206,11 @@ def daily_wind_rose(): type="circle", children=html.Div( className="daily-wind-graph", - id="night-wind-rose", + id=ElementIds.NIGHT_WIND_ROSE, ), ), ), - html.P(className="daily-text", id="night-wind-rose-text"), + html.P(className="daily-text", id=ElementIds.NIGHT_WIND_ROSE_TEXT), ], ), ], @@ -231,7 +232,7 @@ def custom_wind_rose(): ), html.Div( className="container-row full-width justify-center", - id="tab5-custom-dropdown-container", + id=ElementIds.TAB5_CUSTOM_DROPDOWN_CONTAINER, children=[ html.Div( className="container-col justify-center p-2 mr-2", @@ -244,7 +245,7 @@ def custom_wind_rose(): children=["Start Month:"], ), dropdown( - id="tab5-custom-start-month", + id=ElementIds.TAB5_CUSTOM_START_MONTH, options={ j: i + 1 for i, j in enumerate(month_lst) }, @@ -261,7 +262,7 @@ def custom_wind_rose(): children=["Start Hour:"], ), dropdown( - id="tab5-custom-start-hour", + id=ElementIds.TAB5_CUSTOM_START_HOUR, options={ str(i) + ":00": i for i in range(0, 24) }, @@ -283,7 +284,7 @@ def custom_wind_rose(): children=["End Month:"], ), dropdown( - id="tab5-custom-end-month", + id=ElementIds.TAB5_CUSTOM_END_MONTH, options={ j: i + 1 for i, j in enumerate(month_lst) }, @@ -300,7 +301,7 @@ def custom_wind_rose(): children=["End Hour:"], ), dropdown( - id="tab5-custom-end-hour", + id=ElementIds.TAB5_CUSTOM_END_HOUR, options={ str(i) + ":00": i for i in range(1, 25) }, @@ -315,7 +316,7 @@ def custom_wind_rose(): ), dcc.Loading( type="circle", - children=html.Div(id="custom-wind-rose"), + children=html.Div(id=ElementIds.CUSTOM_WIND_ROSE), ), ], ) @@ -336,16 +337,16 @@ def layout(): dcc.Loading( type="circle", children=html.Div( - id="wind-rose", + id=ElementIds.WIND_ROSE, ), ), dcc.Loading( type="circle", - children=html.Div(id="wind-speed"), + children=html.Div(id=ElementIds.WIND_SPEED), ), dcc.Loading( type="circle", - children=html.Div(id="wind-direction"), + children=html.Div(id=ElementIds.WIND_DIRECTION), ), seasonal_wind_rose(), daily_wind_rose(), @@ -356,12 +357,12 @@ def layout(): # wind rose @callback( - Output("wind-rose", "children"), - Input("df-store", "modified_timestamp"), + Output(ElementIds.WIND_ROSE, "children"), + Input(ElementIds.ID_WIND_DF_STORE, "modified_timestamp"), [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_WIND_DF_STORE, "data"), + State(ElementIds.ID_WIND_META_STORE, "data"), + State(ElementIds.ID_WIND_SI_IP_UNIT_STORE, "data"), ], ) def update_annual_wind_rose(_, df, meta, si_ip): @@ -380,13 +381,13 @@ def update_annual_wind_rose(_, df, meta, si_ip): Output(ColNames.WIND_SPEED, "children"), # General [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_WIND_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_WINDGLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_WIND_DF_STORE, "data"), + State(ElementIds.ID_WIND_META_STORE, "data"), + State(ElementIds.ID_WIND_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_wind_speed(_, global_local, df, meta, si_ip): @@ -402,15 +403,15 @@ def update_tab_wind_speed(_, global_local, df, meta, si_ip): # wind direction @callback( - Output("wind-direction", "children"), + Output(ElementIds.WIND_DIRECTION, "children"), # General [ - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_WINDGLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_WIND_DF_STORE, "data"), + State(ElementIds.ID_WIND_META_STORE, "data"), + State(ElementIds.ID_WIND_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_wind_direction(global_local, df, meta, si_ip): @@ -426,19 +427,19 @@ def update_tab_wind_direction(global_local, df, meta, si_ip): # Custom Wind rose @callback( - Output("custom-wind-rose", "children"), + Output(ElementIds.CUSTOM_WIND_ROSE, "children"), # Custom Graph Input [ - Input("df-store", "modified_timestamp"), - Input("tab5-custom-start-month", "value"), - Input("tab5-custom-start-hour", "value"), - Input("tab5-custom-end-month", "value"), - Input("tab5-custom-end-hour", "value"), + Input(ElementIds.ID_WIND_DF_STORE, "modified_timestamp"), + Input(ElementIds.TAB5_CUSTOM_START_MONTH, "value"), + Input(ElementIds.TAB5_CUSTOM_START_HOUR, "value"), + Input(ElementIds.TAB5_CUSTOM_END_MONTH, "value"), + Input(ElementIds.TAB5_CUSTOM_END_HOUR, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_WIND_DF_STORE, "data"), + State(ElementIds.ID_WIND_META_STORE, "data"), + State(ElementIds.ID_WIND_SI_IP_UNIT_STORE, "data"), ], ) def update_custom_wind_rose( @@ -475,22 +476,22 @@ def update_custom_wind_rose( @callback( [ - Output("winter-wind-rose", "children"), - Output("spring-wind-rose", "children"), - Output("summer-wind-rose", "children"), - Output("fall-wind-rose", "children"), - Output("winter-wind-rose-text", "children"), - Output("spring-wind-rose-text", "children"), - Output("summer-wind-rose-text", "children"), - Output("fall-wind-rose-text", "children"), + Output(ElementIds.WINTER_WIND_ROSE, "children"), + Output(ElementIds.SPRING_WIND_ROSE, "children"), + Output(ElementIds.SUMMER_WIND_ROSE, "children"), + Output(ElementIds.FALL_WIND_ROSE, "children"), + Output(ElementIds.WINTER_WIND_ROSE_TEXT, "children"), + Output(ElementIds.SPRING_WIND_ROSE_TEXT, "children"), + Output(ElementIds.SUMMER_WIND_ROSE_TEXT, "children"), + Output(ElementIds.FALL_WIND_ROSE_TEXT, "children"), ], [ - Input("df-store", "modified_timestamp"), + Input(ElementIds.ID_WIND_DF_STORE, "modified_timestamp"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_WIND_DF_STORE, "data"), + State(ElementIds.ID_WIND_META_STORE, "data"), + State(ElementIds.ID_WIND_SI_IP_UNIT_STORE, "data"), ], ) def update_seasonal_graphs(_, df, meta, si_ip): @@ -591,19 +592,19 @@ def seasonal_chart_caption(month_start, month_end, count, n_calm): @callback( # Daily Graphs [ - Output("morning-wind-rose", "children"), - Output("noon-wind-rose", "children"), - Output("night-wind-rose", "children"), - Output("morning-wind-rose-text", "children"), - Output("noon-wind-rose-text", "children"), - Output("night-wind-rose-text", "children"), + Output(ElementIds.MORNING_WIND_ROSE, "children"), + Output(ElementIds.NOON_WIND_ROSE, "children"), + Output(ElementIds.NIGHT_WIND_ROSE, "children"), + Output(ElementIds.MORNING_WIND_ROSE_TEXT, "children"), + Output(ElementIds.NOON_WIND_ROSE_TEXT, "children"), + Output(ElementIds.NIGHT_WIND_ROSE_TEXT, "children"), ], # General - Input("df-store", "modified_timestamp"), + Input(ElementIds.ID_WIND_DF_STORE, "modified_timestamp"), [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_WIND_DF_STORE, "data"), + State(ElementIds.ID_WIND_META_STORE, "data"), + State(ElementIds.ID_WIND_SI_IP_UNIT_STORE, "data"), ], ) def update_daily_graphs(_, df, meta, si_ip): From da93f7f349d5e409f75b1ea9a78651d586895658 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Sun, 24 Aug 2025 19:46:26 +1000 Subject: [PATCH 29/66] Fix: 1.add a new python file "global_elementids.py" and move the class ElementIds to this file 2.updated the ElementIDs of the explorer.py,global_column_names.py,natural_ventilation.py,outdoor.py,psy-chart.py,select.py,summary.py,sun.py,t_rh.py,wind.py --- pages/explorer.py | 8 +- pages/lib/global_column_names.py | 164 ------------------------- pages/lib/global_elementids.py | 200 +++++++++++++++++++++++++++++++ pages/natural_ventilation.py | 23 ++-- pages/outdoor.py | 4 +- pages/psy-chart.py | 2 +- pages/select.py | 77 ++++++------ pages/summary.py | 143 +++++++++++----------- pages/sun.py | 2 +- pages/t_rh.py | 61 +++++----- pages/wind.py | 6 +- 11 files changed, 365 insertions(+), 325 deletions(-) create mode 100644 pages/lib/global_elementids.py diff --git a/pages/explorer.py b/pages/explorer.py index 5a5ed6e0..92377385 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -12,7 +12,7 @@ two_var_graph, three_var_graph, ) -from pages.lib.global_column_names import ElementIds +from pages.lib.global_elementids import ElementIds from pages.lib.global_scheme import ( fig_config, dropdown_names, @@ -626,9 +626,9 @@ def layout(): Output(ElementIds.YEARLY_EXPLORE, "children"), # Section One [ - Input("df-store", "modified_timestamp"), + Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ State(ElementIds.ID_EXPLORER_DF_STORE, "data"), @@ -685,7 +685,7 @@ def update_tab_daily(_, var, global_local, df, meta, si_ip): [ Input(ElementIds.ID_EXPLORER_DF_STORE, "modified_timestamp"), Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), - Input(ElementIds.ID_NATURAL_GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ State(ElementIds.ID_EXPLORER_DF_STORE, "data"), diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index c959918e..6babc9a7 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -49,172 +49,8 @@ class ColNames(str, Enum): DOY = "DOY" # Day of Year -class ElementIds: - # ==================== Defines the unique ID constant for each element in the front-end page ==================== - OUTDOOR_COMFORT_OUTPUT = "outdoor-comfort-output" - DAILY = "daily" - DF_STORE = "df-store" - ENABLE_CONDENSATION = "enable-condensation" - ID_EXPLORER_DF_STORE = "df-store" - ID_EXPLORER_META_STORE = "meta-store" - ID_NATURAL_DF_STORE = "df-store" - ID_NATURAL_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - DROPDOWN = "dropdown" - CUSTOM_SUMMARY = "custom-summary" - CUSTOM_HEATMAP = "custom-heatmap" - GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - HEATMAP = "heatmap" - MAIN_NV_SECTION = "main-nv-section" - ID_NATURAL_META_STORE = "meta-store" - NORMALIZE = "normalize" - NV_BAR_CHART = "nv-bar-chart" - NV_DBT_FILTER = "nv-dbt-filter" - NV_DPT_FILTER = "nv-dpt-filter" - NV_DPT_MAX_VAL = "nv-dpt-max-val" - NV_TDB_MIN_VAL = "nv-tdb-min-val" - NV_TDB_MAX_VAL = "nv-tdb-max-val" - NV_HEATMAP_CHART = "nv-heatmap-chart" - OUTDOOR_COMFORT_HOUR_SLIDER = "outdoor-comfort-hour-slider" - NV_MONTH_HOUR_FILTER = "nv-month-hour-filter" - NV_MONTH_SLIDER = "nv-month-slider" - IMAGE_SELECTION = "image-selection" - INVERT_HOUR_OUTDOOR_COMFORT = "invert-hour-outdoor-comfort" - INVERT_MONTH_OUTDOOR_COMFORT = "invert-month-outdoor-comfort" - INVERT_MONTH_NV = "invert-month-nv" - ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - INVERT_HOUR_EXPLORE_DESCRIPTIVE = "invert-hour-explore-descriptive" - INVERT_MONTH_EXPLORE_DESCRIPTIVE = "invert-month-explore-descriptive" - INVERT_MONTH_EXPLORE_HEATMAP = "invert-month-explore-heatmap" - INVERT_HOUR_EXPLORE_HEATMAP = "invert-hour-explore-heatmap" - INVERT_MONTH_EXPLORE_MORE_CHARTS = "invert-month-explore-more-charts" - INVERT_HOUR_EXPLORE_MORE_CHARTS = "invert-hour-explore-more-charts" - INVERT_HOUR_NV = "invert-hour-nv" - ID_EXPLORER_SI_IP_UNIT_STORE = "si-ip-unit-store" - ID_NATURAL_SI_IP_UNIT_STORE = "si-ip-unit-store" - SI_IP_RADIO_INPUT = "si-ip-radio-input" - META_STORE = "meta-store" - OUTDOOR_COMFORT_MONTH_SLIDER = "outdoor-comfort-month-slider" - OUTDOOR_COMFORT_SWITCHES_INPUT = "outdoor-comfort-switches-input" - NV_HOUR_SLIDER = "nv-hour-slider" - PSYCH_CHART = "psych-chart" - PSY_CHART_BTN = "psy-chart-btn" - PSY_COLOR_BY_DROPDOWN = "psy-color-by-dropdown" - DATA_FILTER = "data-filter" - PSY_VAR_DROPDOWN = "psy-var-dropdown" - PSY_HOUR_SLIDER = "psy-hour-slider" - INVERT_HOUR_PSY = "invert-hour-psy" - INVERT_MONTH_PSY = "invert-month-psy" - PSY_MAX_VAL = "psy-max-val" - PSY_MIN_VAL = "psy-min-val" - PSY_MONTH_SLIDER = "psy-month-slider" - ID_OUTDOOR_MONTH_HOUR_FILTER = "month-hour-filter" - MONTH_HOUR_FILTER = "month-hour-filter" - SEC1_HOUR_SLIDER = "sec1-hour-slider" - SEC1_MONTH_SLIDER = "sec1-month-slider" - SEC1_TIME_FILTER_INPUT = "sec1-time-filter-input" - SEC1_VAR_DROPDOWN = "sec1-var-dropdown" - SDATA_FILTER = "data-filter" - SEC2_DATA_FILTER_INPUT = "sec2-data-filter-input" - SEC2_DATA_FILTER_VAR = "sec2-data-filter-var" - SEC2_VAR_DROPDOWN = "sec2-var-dropdown" - SEC2_HOUR_SLIDER = "sec2-hour-slider" - SEC2_TIME_FILTER_INPUT = "sec2-time-filter-input" - SEC2_MONTH_SLIDER = "sec2-month-slider" - SEC2_MIN_VAL = "sec2-min-val" - SEC2_MAX_VAL = "sec2-max-val" - TAB7_DROPDOWN = "tab7-dropdown" - SI_IP_UNIT_STORE = "si-ip-unit-store" - SWITCHES_INPUT = "switches-input" - TABLE_TMP_HUM = "table-tmp-hum" - TABLE_DATA_EXPLORER = "table-data-explorer" - TAB6_SEC2_CONTAINER = "tab6-sec2-container" - TAB6_SEC3_DATA_FILTER_INPUT = "tab6-sec3-data-filter-input" - TAB6_SEC3_FILTER_VAR_DROPDOWN = "tab6-sec3-filter-var-dropdown" - TAB6_SEC3_MIN_VAL = "tab6-sec3-min-val" - TAB6_SEC3_MAX_VAL = "tab6-sec3-max-val" - TAB6_SEC3_TIME_FILTER_INPUT = "tab6-sec3-time-filter-input" - TAB6_SEC3_QUERY_HOUR_SLIDER = "tab6-sec3-query-hour-slider" - TAB6_SEC3_QUERY_MONTH_SLIDER = "tab6-sec3-query-month-slider" - TAB6_SEC3_VAR_X_DROPDOWN = "tab6-sec3-var-x-dropdown" - TAB6_SEC3_VAR_Y_DROPDOWN = "tab6-sec3-var-y-dropdown" - TAB6_SEC3_COLORBY_DROPDOWN = "tab6-sec3-colorby-dropdown" - MONTH_HOUR_FILTER_OUTDOOR_COMFORT = "month-hour-filter-outdoor-comfort" - TWO_VAR = "two-var" - THREE_VAR = "three-var" - QUERY_DAILY = "query-daily" - QUERY_HEATMAP = "query-heatmap" - UTCI_CATEGORY_HEATMAP = "utci-category-heatmap" - UTCI_HEATMAP = "utci-heatmap" - UTCI_SUMMARY_CHART = "utci-summary-chart" - YEARLY_CHART = "yearly-chart" - YEARLY_EXPLORE = "yearly-explore" - ID_OUTDOOR_DF_STORE = "df-store" - ID_OUTDOOR_META_STORE = "meta-store" - ID_OUTDOOR_SI_IP_UNIT_STORE = "si-ip-unit-store" - ID_PSY_CHART_SI_IP_UNIT_STORE = "si-ip-unit-store" - ID_OUTDOOR_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - ID_PSY_CHART_DF_STORE = "df-store" - ID_PSY_CHART_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - ID_PSY_CHART_META_STORE = "meta-store" - CUSTOM_SUN_VIEW_DROPDOWN = "custom-sun-view-dropdown" - CUSTOM_SUN_VAR_DROPDOWN = "custom-sun-var-dropdown" - CUSTOM_SUNPATH = "custom-sunpath" - TAB_EXPLORE_DROPDOWN = "tab4-explore-dropdown" - TAB4_DAILY = "tab4-daily" - TAB4_HEATMAP = "tab4-heatmap" - STATIC_SECTION = "static-section" - TAB_FOUR_CONTAINER = "tab-four-container" - MONTHLY_SOLAR = "monthly-solar" - CLOUD_COVER = "cloud-cover" - ID_SUN_SI_IP_RADIO_INPUT = "si-ip-radio-input" - ID_SUN_DF_STORE = "df-store" - ID_SUN_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" - ID_SUN_SI_IP_UNIT_STORE = "si-ip-unit-store" - ID_SUN_META_STORE = "meta-store" - SLIDER_CONTAINER = "slider-container" - MONTH_SLIDER = "month-slider" - HOUR_SLIDER = "hour-slider" - WINTER_WIND_ROSE = "winter-wind-rose" - WINTER_WIND_ROSE_TEXT = "winter-wind-rose-text" - SPRING_WIND_ROSE = "spring-wind-rose" - SPRING_WIND_ROSE_TEXT = "spring-wind-rose-text" - SUMMER_WIND_ROSE = "summer-wind-rose" - SUMMER_WIND_ROSE_TEXT = "summer-wind-rose-text" - FALL_WIND_ROSE = "fall-wind-rose" - FALL_WIND_ROSE_TEXT = "fall-wind-rose-text" - TAB5_DAILY_CONTAINER = "tab5-daily-container" - DAILY_WIND_ROSE_OUTER_CONTAINER = "daily-wind-rose-outer-container" - MORNING_WIND_ROSE = "morning-wind-rose" - MORNING_WIND_ROSE_TEXT = "morning-wind-rose-text" - NOON_WIND_ROSE = "noon-wind-rose" - NOON_WIND_ROSE_TEXT = "noon-wind-rose-text" - NIGHT_WIND_ROSE = "night-wind-rose" - NIGHT_WIND_ROSE_TEXT = "night-wind-rose-text" - TAB5_CUSTOM_DROPDOWN_CONTAINER = "tab5-custom-dropdown-container" - TAB5_CUSTOM_START_MONTH = "tab5-custom-start-month" - TAB5_CUSTOM_START_HOUR = "tab5-custom-start-hour" - TAB5_CUSTOM_END_MONTH = "tab5-custom-end-month" - TAB5_CUSTOM_END_HOUR = "tab5-custom-end-hour" - CUSTOM_WIND_ROSE = "custom-wind-rose" - WIND_ROSE = "wind-rose" - WIND_SPEED = "wind-speed" - WIND_DIRECTION = "wind-direction" - ID_WIND_DF_STORE = "df-store" - ID_WIND_META_STORE = "meta-store" - ID_WIND_SI_IP_UNIT_STORE = "si-ip-unit-store" - ID_WINDGLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" -class ComponentProperty: - # ==================== Define common attribute name constants for components ==================== - CHILDREN = "children" - DATA = "data" - MODIFIED_TIMESTAMP = "modified_timestamp" - VALUE = "value" - -class Type: - # ==================== Defines type constants available for UI components ==================== - CIRCLE = "circle" diff --git a/pages/lib/global_elementids.py b/pages/lib/global_elementids.py new file mode 100644 index 00000000..6fcce955 --- /dev/null +++ b/pages/lib/global_elementids.py @@ -0,0 +1,200 @@ +from enum import Enum + + +class ElementIds(str, Enum): + # ==================== Defines the unique ID constant for each element in the front-end page ==================== + OUTDOOR_COMFORT_OUTPUT = "outdoor-comfort-output" + DAILY = "daily" + ID_T_RH_DF_STORE = "df-store" + ENABLE_CONDENSATION = "enable-condensation" + ID_EXPLORER_DF_STORE = "df-store" + ID_EXPLORER_META_STORE = "meta-store" + ID_NATURAL_VENTILATION_DF_STORE = "df-store" + ID_NATURAL_VENTILATION_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_T_RH_DROPDOWN = "dropdown" + CUSTOM_SUMMARY = "custom-summary" + CUSTOM_HEATMAP = "custom-heatmap" + ID_T_RH_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + HEATMAP = "heatmap" + MAIN_NV_SECTION = "main-nv-section" + ID_NATURAL_VENTILATION_META_STORE = "meta-store" + NORMALIZE = "normalize" + NV_BAR_CHART = "nv-bar-chart" + NV_DBT_FILTER = "nv-dbt-filter" + NV_DPT_FILTER = "nv-dpt-filter" + NV_DPT_MAX_VAL = "nv-dpt-max-val" + NV_TDB_MIN_VAL = "nv-tdb-min-val" + NV_TDB_MAX_VAL = "nv-tdb-max-val" + NV_HEATMAP_CHART = "nv-heatmap-chart" + OUTDOOR_COMFORT_HOUR_SLIDER = "outdoor-comfort-hour-slider" + NV_MONTH_HOUR_FILTER = "nv-month-hour-filter" + NV_MONTH_SLIDER = "nv-month-slider" + IMAGE_SELECTION = "image-selection" + INVERT_HOUR_OUTDOOR_COMFORT = "invert-hour-outdoor-comfort" + INVERT_MONTH_OUTDOOR_COMFORT = "invert-month-outdoor-comfort" + INVERT_MONTH_NV = "invert-month-nv" + ID_EXPLORER_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + INVERT_HOUR_EXPLORE_DESCRIPTIVE = "invert-hour-explore-descriptive" + INVERT_MONTH_EXPLORE_DESCRIPTIVE = "invert-month-explore-descriptive" + INVERT_MONTH_EXPLORE_HEATMAP = "invert-month-explore-heatmap" + INVERT_HOUR_EXPLORE_HEATMAP = "invert-hour-explore-heatmap" + INVERT_MONTH_EXPLORE_MORE_CHARTS = "invert-month-explore-more-charts" + INVERT_HOUR_EXPLORE_MORE_CHARTS = "invert-hour-explore-more-charts" + INVERT_HOUR_NV = "invert-hour-nv" + ID_EXPLORER_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_NATURAL_VENTILATION_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_NATURAL_VENTILATION_SI_IP_RADIO_INPUT = "si-ip-radio-input" + ID_T_RH_META_STORE = "meta-store" + OUTDOOR_COMFORT_MONTH_SLIDER = "outdoor-comfort-month-slider" + OUTDOOR_COMFORT_SWITCHES_INPUT = "outdoor-comfort-switches-input" + NV_HOUR_SLIDER = "nv-hour-slider" + PSYCH_CHART = "psych-chart" + PSY_CHART_BTN = "psy-chart-btn" + PSY_COLOR_BY_DROPDOWN = "psy-color-by-dropdown" + DATA_FILTER = "data-filter" + PSY_VAR_DROPDOWN = "psy-var-dropdown" + PSY_HOUR_SLIDER = "psy-hour-slider" + INVERT_HOUR_PSY = "invert-hour-psy" + INVERT_MONTH_PSY = "invert-month-psy" + PSY_MAX_VAL = "psy-max-val" + PSY_MIN_VAL = "psy-min-val" + PSY_MONTH_SLIDER = "psy-month-slider" + ID_OUTDOOR_MONTH_HOUR_FILTER = "month-hour-filter" + MONTH_HOUR_FILTER = "month-hour-filter" + SEC1_HOUR_SLIDER = "sec1-hour-slider" + SEC1_MONTH_SLIDER = "sec1-month-slider" + SEC1_TIME_FILTER_INPUT = "sec1-time-filter-input" + SEC1_VAR_DROPDOWN = "sec1-var-dropdown" + SDATA_FILTER = "data-filter" + SEC2_DATA_FILTER_INPUT = "sec2-data-filter-input" + SEC2_DATA_FILTER_VAR = "sec2-data-filter-var" + SEC2_VAR_DROPDOWN = "sec2-var-dropdown" + SEC2_HOUR_SLIDER = "sec2-hour-slider" + SEC2_TIME_FILTER_INPUT = "sec2-time-filter-input" + SEC2_MONTH_SLIDER = "sec2-month-slider" + SEC2_MIN_VAL = "sec2-min-val" + SEC2_MAX_VAL = "sec2-max-val" + TAB7_DROPDOWN = "tab7-dropdown" + ID_T_RH_SI_IP_UNIT_STORE = "si-ip-unit-store" + SWITCHES_INPUT = "switches-input" + TABLE_TMP_HUM = "table-tmp-hum" + TABLE_DATA_EXPLORER = "table-data-explorer" + TAB6_SEC2_CONTAINER = "tab6-sec2-container" + TAB6_SEC3_DATA_FILTER_INPUT = "tab6-sec3-data-filter-input" + TAB6_SEC3_FILTER_VAR_DROPDOWN = "tab6-sec3-filter-var-dropdown" + TAB6_SEC3_MIN_VAL = "tab6-sec3-min-val" + TAB6_SEC3_MAX_VAL = "tab6-sec3-max-val" + TAB6_SEC3_TIME_FILTER_INPUT = "tab6-sec3-time-filter-input" + TAB6_SEC3_QUERY_HOUR_SLIDER = "tab6-sec3-query-hour-slider" + TAB6_SEC3_QUERY_MONTH_SLIDER = "tab6-sec3-query-month-slider" + TAB6_SEC3_VAR_X_DROPDOWN = "tab6-sec3-var-x-dropdown" + TAB6_SEC3_VAR_Y_DROPDOWN = "tab6-sec3-var-y-dropdown" + TAB6_SEC3_COLORBY_DROPDOWN = "tab6-sec3-colorby-dropdown" + MONTH_HOUR_FILTER_OUTDOOR_COMFORT = "month-hour-filter-outdoor-comfort" + TWO_VAR = "two-var" + THREE_VAR = "three-var" + QUERY_DAILY = "query-daily" + QUERY_HEATMAP = "query-heatmap" + UTCI_CATEGORY_HEATMAP = "utci-category-heatmap" + UTCI_HEATMAP = "utci-heatmap" + UTCI_SUMMARY_CHART = "utci-summary-chart" + YEARLY_CHART = "yearly-chart" + YEARLY_EXPLORE = "yearly-explore" + ID_OUTDOOR_DF_STORE = "df-store" + ID_OUTDOOR_META_STORE = "meta-store" + ID_OUTDOOR_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_PSY_CHART_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_OUTDOOR_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_PSY_CHART_DF_STORE = "df-store" + ID_PSY_CHART_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_PSY_CHART_META_STORE = "meta-store" + CUSTOM_SUN_VIEW_DROPDOWN = "custom-sun-view-dropdown" + CUSTOM_SUN_VAR_DROPDOWN = "custom-sun-var-dropdown" + CUSTOM_SUNPATH = "custom-sunpath" + TAB_EXPLORE_DROPDOWN = "tab4-explore-dropdown" + TAB4_DAILY = "tab4-daily" + TAB4_HEATMAP = "tab4-heatmap" + STATIC_SECTION = "static-section" + TAB_FOUR_CONTAINER = "tab-four-container" + MONTHLY_SOLAR = "monthly-solar" + CLOUD_COVER = "cloud-cover" + ID_SUN_SI_IP_RADIO_INPUT = "si-ip-radio-input" + ID_SUN_DF_STORE = "df-store" + ID_SUN_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_SUN_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_SUN_META_STORE = "meta-store" + SLIDER_CONTAINER = "slider-container" + MONTH_SLIDER = "month-slider" + HOUR_SLIDER = "hour-slider" + WINTER_WIND_ROSE = "winter-wind-rose" + WINTER_WIND_ROSE_TEXT = "winter-wind-rose-text" + SPRING_WIND_ROSE = "spring-wind-rose" + SPRING_WIND_ROSE_TEXT = "spring-wind-rose-text" + SUMMER_WIND_ROSE = "summer-wind-rose" + SUMMER_WIND_ROSE_TEXT = "summer-wind-rose-text" + FALL_WIND_ROSE = "fall-wind-rose" + FALL_WIND_ROSE_TEXT = "fall-wind-rose-text" + TAB5_DAILY_CONTAINER = "tab5-daily-container" + DAILY_WIND_ROSE_OUTER_CONTAINER = "daily-wind-rose-outer-container" + MORNING_WIND_ROSE = "morning-wind-rose" + MORNING_WIND_ROSE_TEXT = "morning-wind-rose-text" + NOON_WIND_ROSE = "noon-wind-rose" + NOON_WIND_ROSE_TEXT = "noon-wind-rose-text" + NIGHT_WIND_ROSE = "night-wind-rose" + NIGHT_WIND_ROSE_TEXT = "night-wind-rose-text" + TAB5_CUSTOM_DROPDOWN_CONTAINER = "tab5-custom-dropdown-container" + TAB5_CUSTOM_START_MONTH = "tab5-custom-start-month" + TAB5_CUSTOM_START_HOUR = "tab5-custom-start-hour" + TAB5_CUSTOM_END_MONTH = "tab5-custom-end-month" + TAB5_CUSTOM_END_HOUR = "tab5-custom-end-hour" + CUSTOM_WIND_ROSE = "custom-wind-rose" + WIND_ROSE = "wind-rose" + WIND_SPEED = "wind-speed" + WIND_DIRECTION = "wind-direction" + ID_WIND_DF_STORE = "df-store" + ID_WIND_META_STORE = "meta-store" + ID_WIND_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_WIND_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + LOADING_ONE = "loading-1" + UPLOAD_DATA = "upload-data" + UPLOAD_DATA_BUTTON = "upload-data-button" + TAB_ONE_MAP = "tab-one-map" + SKELETON_GRAPH_CONTAINER = "skeleton-graph-container" + MODAL_HEADER = "modal-header" + MODAL_CLOSE_BUTTON = "modal-close-button" + MODAL_YES_BUTTON = "modal-yes-button" + MODAL = "modal" + ALERT = "alert" + ID_SELECT_META_STORE = "meta-store" + LINES_STORE = "lines-store" + ID_SELECT_URL_STORE = "url-store" + ID_SELECT_DF_STORE = "df-store" + ID_SELECT_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_SELECT_SI_IP_RADIO_INPUT = "si-ip-radio-input" + BANNER_SUBTITLE = "banner-subtitle" + TABLE_TWO_CONTAINER = "tab-two-container" + ID_SUMMARY_SI_IP_RADIO_INPUT = "si-ip-radio-input" + TAB2_SCE1_CONTAINER = "tab2-sec1-container" + LOCATION_INFO = "location-info" + WORLD_MAP = "world-map" + DOWN_EPW_BUTTON = "download-epw-button" + DOWNLOAD_BUTTON = "download-button" + DOWNLOAD_DATAFRAME_CSV = "download-dataframe-csv" + DOWNLOAD_EPW = "download-epw" + WARNING_CDD_HIGHER_HDD = "warning-cdd-higher-hdd" + INPUT_HDD_SET_POINT = "input-hdd-set-point" + INPUT_CDD_SET_POINT = "input-cdd-set-point" + SUBMIT_SET_POINTS = "submit-set-points" + GRAPH_CONTAINER = "graph-container" + DEGREE_DAYS_CHART_WRAPPER = "degree-days-chart-wrapper" + TEMP_PROFILE_GRAPH = "temp-profile-graph" + HUMIDITY_PROFILE_GRAPH = "humidity-profile-graph" + SOLAR_RADIATION_GRAPH = "solar-radiation-graph" + WIND_SPEED_GRAPH = "wind-speed-graph" + ID_SUMMARY_META_STORE = "meta-store" + GH_RAD_PROFILE_GRAPH = "gh_rad-profile-graph" + ID_SUMMARY_DF_STORE = "df-store" + ID_SUMMARY_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_SUMMARY_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + TDB_PROFILE_GRAPH = "tdb-profile-graph" + RH_PROFILE_GRAPH = "rh-profile-graph" diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 3c16861c..c772c0b4 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -19,7 +19,8 @@ container_col_center_one_of_three, ) from pages.lib.template_graphs import filter_df_by_month_and_hour -from pages.lib.global_column_names import ColNames, ElementIds +from pages.lib.global_column_names import ColNames +from pages.lib.global_elementids import ElementIds from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -51,7 +52,7 @@ def layout(): @callback( Output(ElementIds.MAIN_NV_SECTION, "children"), - [Input(ElementIds.SI_IP_RADIO_INPUT, "value")], + [Input(ElementIds.ID_NATURAL_VENTILATION_SI_IP_RADIO_INPUT, "value")], ) def update_layout(si_ip): if si_ip == UnitSystem.IP: @@ -286,24 +287,24 @@ def inputs_tab(t_min, t_max, d_set): @callback( Output(ElementIds.NV_HEATMAP_CHART, "children"), [ - Input(ElementIds.ID_NATURAL_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_NATURAL_VENTILATION_DF_STORE, "modified_timestamp"), Input(ElementIds.NV_MONTH_HOUR_FILTER, "n_clicks"), Input(ElementIds.NV_DBT_FILTER, "n_clicks"), Input(ElementIds.NV_DPT_FILTER, "n_clicks"), - Input(ElementIds.ID_NATURAL_GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_NATURAL_VENTILATION_GLOBAL_LOCAL_RADIO_INPUT, "value"), Input(ElementIds.ENABLE_CONDENSATION, "value"), ], [ - State(ElementIds.ID_NATURAL_DF_STORE, "data"), + State(ElementIds.ID_NATURAL_VENTILATION_DF_STORE, "data"), State(ElementIds.NV_MONTH_SLIDER, "value"), State(ElementIds.NV_HOUR_SLIDER, "value"), State(ElementIds.NV_TDB_MIN_VAL, "value"), State(ElementIds.NV_TDB_MAX_VAL, "value"), State(ElementIds.NV_DPT_MAX_VAL, "value"), - State(ElementIds.ID_NATURAL_META_STORE, "data"), + State(ElementIds.ID_NATURAL_VENTILATION_META_STORE, "data"), State(ElementIds.INVERT_MONTH_NV, "value"), State(ElementIds.INVERT_HOUR_NV, "value"), - State(ElementIds.ID_NATURAL_SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_NATURAL_VENTILATION_SI_IP_UNIT_STORE, "data"), ], ) def nv_heatmap( @@ -453,7 +454,7 @@ def nv_heatmap( @callback( Output(ElementIds.NV_BAR_CHART, "children"), [ - Input(ElementIds.ID_NATURAL_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_NATURAL_VENTILATION_DF_STORE, "modified_timestamp"), Input(ElementIds.NV_MONTH_HOUR_FILTER, "n_clicks"), Input(ElementIds.NV_DBT_FILTER, "n_clicks"), Input(ElementIds.NV_DPT_FILTER, "n_clicks"), @@ -461,16 +462,16 @@ def nv_heatmap( Input(ElementIds.ENABLE_CONDENSATION, "value"), ], [ - State(ElementIds.ID_NATURAL_DF_STORE, "data"), + State(ElementIds.ID_NATURAL_VENTILATION_DF_STORE, "data"), State(ElementIds.NV_MONTH_SLIDER, "value"), State(ElementIds.NV_HOUR_SLIDER, "value"), State(ElementIds.NV_TDB_MIN_VAL, "value"), State(ElementIds.NV_TDB_MAX_VAL, "value"), State(ElementIds.NV_DPT_MAX_VAL, "value"), - State(ElementIds.ID_NATURAL_META_STORE, "data"), + State(ElementIds.ID_NATURAL_VENTILATION_META_STORE, "data"), State(ElementIds.INVERT_MONTH_NV, "value"), State(ElementIds.INVERT_HOUR_NV, "value"), - State(ElementIds.ID_NATURAL_SI_IP_UNIT_STORE, "data"), + State(ElementIds.ID_NATURAL_VENTILATION_SI_IP_UNIT_STORE, "data"), ], ) def nv_bar_chart( diff --git a/pages/outdoor.py b/pages/outdoor.py index d6a805be..eb2350f8 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -6,7 +6,7 @@ import numpy as np from config import PageUrls, DocLinks, PageInfo -from pages.lib.global_column_names import ElementIds, ComponentProperty, Type +from pages.lib.global_elementids import ElementIds from pages.lib.global_scheme import ( outdoor_dropdown_names, ) @@ -316,7 +316,7 @@ def change_image_based_on_selection(value): [ Input(ElementIds.ID_OUTDOOR_DF_STORE, "modified_timestamp"), Input(ElementIds.TAB7_DROPDOWN, "value"), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_OUTDOOR_GLOBAL_LOCAL_RADIO_INPUT, "value"), Input(ElementIds.MONTH_HOUR_FILTER_OUTDOOR_COMFORT, "n_clicks"), ], [ diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 83ba076f..51bac4cc 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -12,7 +12,7 @@ from pythermalcomfort import psychrometrics as psy from config import PageUrls, DocLinks, PageInfo, UnitSystem -from pages.lib.global_column_names import ElementIds, ComponentProperty, Type +from pages.lib.global_elementids import ElementIds from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, diff --git a/pages/select.py b/pages/select.py index cfc770d7..d0e27df9 100644 --- a/pages/select.py +++ b/pages/select.py @@ -13,6 +13,7 @@ from pages.lib.extract_df import convert_data from pages.lib.extract_df import create_df, get_data, get_location_info +from pages.lib.global_elementids import ElementIds from pages.lib.global_scheme import mapping_dictionary from config import PageUrls, PageInfo, UnitSystem from pages.lib.utils import generate_chart_name @@ -40,19 +41,19 @@ def layout(): className="container-col tab-container", children=[ dcc.Loading( - id="loading-1", + id=ElementIds.LOADING_ONE, type="circle", fullscreen=True, children=alert(), ), dcc.Upload( - id="upload-data", + id=ElementIds.UPLOAD_DATA, children=dbc.Button( [ "Drag and Drop or ", html.A("Select an EPW file from your computer"), ], - id="upload-data-button", + id=ElementIds.UPLOAD_DATA_BUTTON, outline=True, color="secondary", className="mt-2", @@ -64,31 +65,31 @@ def layout(): ), dmc.Skeleton( visible=False, - id="skeleton-graph-container", + id=ElementIds.SKELETON_GRAPH_CONTAINER, height=500, - children=html.Div(id="tab-one-map"), + children=html.Div(id=ElementIds.TAB_ONE_MAP), ), dbc.Modal( [ - dbc.ModalHeader(id="modal-header"), + dbc.ModalHeader(id=ElementIds.MODAL_HEADER), dbc.ModalFooter( children=[ dbc.Button( "Close", - id="modal-close-button", + id=ElementIds.MODAL_CLOSE_BUTTON, className="ml-2", color="light", ), dbc.Button( "Yes", - id="modal-yes-button", + id=ElementIds.MODAL_YES_BUTTON, className="ml-2", color="primary", ), ] ), ], - id="modal", + id=ElementIds.MODAL, is_open=False, ), ], @@ -100,7 +101,7 @@ def alert(): return dbc.Alert( messages_alert["start"], color="primary", - id="alert", + id=ElementIds.ALERT, dismissable=False, is_open=True, style={"maxHeight": "66px"}, @@ -110,20 +111,20 @@ def alert(): # add si-ip and map dictionary in the output @callback( [ - Output("meta-store", "data"), - Output("lines-store", "data"), - Output("alert", "is_open"), - Output("alert", "children"), - Output("alert", "color"), + Output(ElementIds.ID_SELECT_META_STORE, "data"), + Output(ElementIds.LINES_STORE, "data"), + Output(ElementIds.ALERT, "is_open"), + Output(ElementIds.ALERT, "children"), + Output(ElementIds.ALERT, "color"), ], [ - Input("modal-yes-button", "n_clicks"), - Input("upload-data-button", "n_clicks"), - Input("upload-data", "contents"), + Input(ElementIds.MODAL_YES_BUTTON, "n_clicks"), + Input(ElementIds.UPLOAD_DATA_BUTTON, "n_clicks"), + Input(ElementIds.UPLOAD_DATA, "contents"), ], [ - State("upload-data", "filename"), - State("url-store", "data"), + State(ElementIds.UPLOAD_DATA, "filename"), + State(ElementIds.ID_SELECT_URL_STORE, "data"), ], prevent_initial_call=True, ) @@ -205,14 +206,14 @@ def submitted_data( # add switch_si_ip function and convert the data-store @callback( [ - Output("df-store", "data"), - Output("si-ip-unit-store", "data"), + Output(ElementIds.ID_SELECT_DF_STORE, "data"), + Output(ElementIds.ID_SELECT_SI_IP_UNIT_STORE, "data"), ], [ - Input("lines-store", "modified_timestamp"), - Input("si-ip-radio-input", "value"), + Input(ElementIds.LINES_STORE, "modified_timestamp"), + Input(ElementIds.ID_SELECT_SI_IP_RADIO_INPUT, "value"), ], - [State("url-store", "data"), State("lines-store", "data")], + [State(ElementIds.ID_SELECT_URL_STORE, "data"), State("lines-store", "data")], ) def switch_si_ip(_, si_ip_input, url_store, lines): if lines is not None: @@ -239,11 +240,11 @@ def switch_si_ip(_, si_ip_input, url_store, lines): Output("/explorer", "disabled"), Output("/outdoor", "disabled"), Output("/natural-ventilation", "disabled"), - Output("banner-subtitle", "children"), + Output(ElementIds.BANNER_SUBTITLE, "children"), ], [ - Input("meta-store", "data"), - Input("df-store", "data"), + Input(ElementIds.ID_SELECT_META_STORE, "data"), + Input(ElementIds.ID_SELECT_DF_STORE, "data"), ], ) def enable_tabs_when_data_is_loaded(meta, data): @@ -279,15 +280,15 @@ def enable_tabs_when_data_is_loaded(meta, data): @callback( [ - Output("modal", "is_open"), - Output("url-store", "data"), + Output(ElementIds.MODAL, "is_open"), + Output(ElementIds.ID_SELECT_URL_STORE, "data"), ], [ - Input("modal-yes-button", "n_clicks"), - Input("tab-one-map", "clickData"), - Input("modal-close-button", "n_clicks"), + Input(ElementIds.MODAL_YES_BUTTON, "n_clicks"), + Input(ElementIds.TAB_ONE_MAP, "clickData"), + Input(ElementIds.MODAL_CLOSE_BUTTON, "n_clicks"), ], - [State("modal", "is_open")], + [State(ElementIds.MODAL, "is_open")], prevent_initial_call=True, ) def display_modal_when_data_clicked(_, click_map, __, is_open): @@ -302,10 +303,10 @@ def display_modal_when_data_clicked(_, click_map, __, is_open): @callback( [ - Output("modal-header", "children"), + Output(ElementIds.MODAL_HEADER, "children"), ], [ - Input("tab-one-map", "clickData"), + Input(ElementIds.TAB_ONE_MAP, "clickData"), ], prevent_initial_call=True, ) @@ -317,7 +318,7 @@ def change_text_modal(click_map): @callback( - Output("skeleton-graph-container", "children"), + Output(ElementIds.SKELETON_GRAPH_CONTAINER, "children"), Input("url", "pathname"), ) def plot_location_epw_files(pathname): @@ -369,7 +370,7 @@ def plot_location_epw_files(pathname): return ( dcc.Graph( - id="tab-one-map", + id=ElementIds.TAB_ONE_MAP, figure=fig, config=generate_chart_name("epw_location_select"), ), diff --git a/pages/summary.py b/pages/summary.py index 20547b8d..d06dceb6 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -12,6 +12,7 @@ from pages.lib.global_scheme import template, tight_margins, mapping_dictionary from pages.lib.template_graphs import violin from pages.lib.global_column_names import ColNames +from pages.lib.global_elementids import ElementIds from pages.lib.utils import ( generate_chart_name, generate_units, @@ -42,7 +43,7 @@ def layout(): @callback( - Output("tab-two-container", "children"), [Input("si-ip-radio-input", "value")] + Output(ElementIds.TABLE_TWO_CONTAINER, "children"), [Input(ElementIds.ID_SUMMARY_SI_IP_RADIO_INPUT, "value")] ) def update_layout(si_ip): if si_ip == UnitSystem.SI: @@ -54,19 +55,19 @@ def update_layout(si_ip): return html.Div( className="container-col", - id="tab2-sec1-container", + id=ElementIds.TAB2_SCE1_CONTAINER, children=[ dcc.Loading( type="circle", children=html.Div( className="container-col", - id="location-info", + id=ElementIds.LOCATION_INFO, style={"padding": "12px"}, ), ), dcc.Loading( type="circle", - children=html.Div(className="tab-two-section", id="world-map"), + children=html.Div(className="tab-two-section", id=ElementIds.WORLD_MAP), ), html.Div( children=title_with_tooltip( @@ -83,7 +84,7 @@ def update_layout(si_ip): dbc.Button( "Download EPW", color="primary", - id="download-epw-button", + id=ElementIds.DOWN_EPW_BUTTON, ), width="auto", ), @@ -91,14 +92,14 @@ def update_layout(si_ip): dbc.Button( "Download Clima dataframe", color="primary", - id="download-button", + id=ElementIds.DOWNLOAD_BUTTON, ), width="auto", ), dbc.Col( [ - dcc.Download(id="download-dataframe-csv"), - dcc.Download(id="download-epw"), + dcc.Download(id=ElementIds.DOWNLOAD_DATAFRAME_CSV), + dcc.Download(id=ElementIds.DOWNLOAD_EPW), ], width=1, ), @@ -116,7 +117,7 @@ def update_layout(si_ip): "WARNING: Invalid Results! The CDD setpoint should be higher than the HDD setpoint!", color="warning", is_open=False, - id="warning-cdd-higher-hdd", + id=ElementIds.WARNING_CDD_HIGHER_HDD, ), dbc.Row( [ @@ -128,7 +129,7 @@ def update_layout(si_ip): ), dbc.Col( dbc.Input( - id="input-hdd-set-point", + id=ElementIds.INPUT_HDD_SET_POINT, type="number", value=heating_setpoint, style={"width": "4rem"}, @@ -143,7 +144,7 @@ def update_layout(si_ip): ), dbc.Col( dbc.Input( - id="input-cdd-set-point", + id=ElementIds.INPUT_CDD_SET_POINT, type="number", value=cooling_setpoint, style={"width": "4rem"}, @@ -152,7 +153,7 @@ def update_layout(si_ip): ), dbc.Col( dbc.Button( - id="submit-set-points", + id=ElementIds.SUBMIT_SET_POINTS, children="Submit", color="primary", ), @@ -164,7 +165,7 @@ def update_layout(si_ip): ), dcc.Loading( type="circle", - children=html.Div(id="degree-days-chart-wrapper"), + children=html.Div(id=ElementIds.DEGREE_DAYS_CHART_WRAPPER), ), html.Div( children=title_with_link( @@ -174,12 +175,12 @@ def update_layout(si_ip): ), ), dbc.Row( - id="graph-container", + id=ElementIds.GRAPH_CONTAINER, children=[ - dbc.Col(id="temp-profile-graph", width=12, md=6, lg=3), - dbc.Col(id="humidity-profile-graph", width=12, md=6, lg=3), - dbc.Col(id="solar-radiation-graph", width=12, md=6, lg=3), - dbc.Col(id="wind-speed-graph", width=12, md=6, lg=3), + dbc.Col(id=ElementIds.TEMP_PROFILE_GRAPH, width=12, md=6, lg=3), + dbc.Col(id=ElementIds.HUMIDITY_PROFILE_GRAPH, width=12, md=6, lg=3), + dbc.Col(id=ElementIds.SOLAR_RADIATION_GRAPH, width=12, md=6, lg=3), + dbc.Col(id=ElementIds.WIND_SPEED_GRAPH, width=12, md=6, lg=3), ], ), ], @@ -198,13 +199,13 @@ def update_layout(si_ip): @callback( - Output("world-map", "children"), - Input("meta-store", "data"), + Output(ElementIds.WORLD_MAP, "children"), + Input(ElementIds.ID_SUMMARY_META_STORE, "data"), ) def update_map(meta): """Update the contents of tab two. Passing in the general info (df, meta).""" map_world = dcc.Graph( - id="gh_rad-profile-graph", + id=ElementIds.GH_RAD_PROFILE_GRAPH, config=generate_chart_name("map", meta), figure=world_map(meta), ) @@ -213,12 +214,12 @@ def update_map(meta): @callback( - Output("location-info", "children"), - Input("df-store", "modified_timestamp"), + Output(ElementIds.LOCATION_INFO, "children"), + Input(ElementIds.ID_SUMMARY_DF_STORE, "modified_timestamp"), [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUMMARY_DF_STORE, "data"), + State(ElementIds.ID_SUMMARY_META_STORE, "data"), + State(ElementIds.ID_SUMMARY_SI_IP_UNIT_STORE, "data"), ], ) def update_location_info(ts, df, meta, si_ip): @@ -303,20 +304,20 @@ def update_location_info(ts, df, meta, si_ip): @callback( [ - Output("degree-days-chart-wrapper", "children"), - Output("warning-cdd-higher-hdd", "is_open"), + Output(ElementIds.DEGREE_DAYS_CHART_WRAPPER, "children"), + Output(ElementIds.WARNING_CDD_HIGHER_HDD, "is_open"), ], [ - Input("df-store", "modified_timestamp"), - Input("submit-set-points", "n_clicks_timestamp"), + Input(ElementIds.ID_SUMMARY_DF_STORE, "modified_timestamp"), + Input(ElementIds.SUBMIT_SET_POINTS, "n_clicks_timestamp"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("input-hdd-set-point", "value"), - State("input-cdd-set-point", "value"), - State("submit-set-points", "n_clicks"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUMMARY_DF_STORE, "data"), + State(ElementIds.ID_SUMMARY_META_STORE, "data"), + State(ElementIds.INPUT_HDD_SET_POINT, "value"), + State(ElementIds.INPUT_CDD_SET_POINT, "value"), + State(ElementIds.SUBMIT_SET_POINTS, "n_clicks"), + State(ElementIds.ID_SUMMARY_SI_IP_UNIT_STORE, "data"), ], ) def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ip): @@ -413,21 +414,21 @@ def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ @callback( - Output("temp-profile-graph", "children"), + Output(ElementIds.TEMP_PROFILE_GRAPH, "children"), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_SUMMARY_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_SUMMARY_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUMMARY_DF_STORE, "data"), + State(ElementIds.ID_SUMMARY_META_STORE, "data"), + State(ElementIds.ID_SUMMARY_SI_IP_UNIT_STORE, "data"), ], ) def update_violin_tdb(ts, global_local, df, meta, si_ip): units = generate_units_degree(si_ip) return dcc.Graph( - id="tdb-profile-graph", + id=ElementIds.TDB_PROFILE_GRAPH, className="violin-container", config=generate_chart_name("DryBulbTemperature", meta, units), figure=violin(df, ColNames.DBT, global_local, si_ip), @@ -435,15 +436,15 @@ def update_violin_tdb(ts, global_local, df, meta, si_ip): @callback( - Output("wind-speed-graph", "children"), + Output(ElementIds.WIND_SPEED_GRAPH, "children"), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_SUMMARY_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_SUMMARY_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUMMARY_DF_STORE, "data"), + State(ElementIds.ID_SUMMARY_META_STORE, "data"), + State(ElementIds.ID_SUMMARY_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_wind(ts, global_local, df, meta, si_ip): @@ -458,22 +459,22 @@ def update_tab_wind(ts, global_local, df, meta, si_ip): @callback( - Output("humidity-profile-graph", "children"), + Output(ElementIds.HUMIDITY_PROFILE_GRAPH, "children"), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_SUMMARY_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_SUMMARY_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUMMARY_DF_STORE, "data"), + State(ElementIds.ID_SUMMARY_META_STORE, "data"), + State(ElementIds.ID_SUMMARY_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_rh(ts, global_local, df, meta, si_ip): """Update the contents of tab two. Passing in the general info (df, meta).""" units = generate_units(si_ip) return dcc.Graph( - id="rh-profile-graph", + id=ElementIds.RH_PROFILE_GRAPH, className="violin-container", config=generate_chart_name("RelativeHumidity", meta, units), figure=violin(df, ColNames.RH, global_local, si_ip), @@ -481,22 +482,22 @@ def update_tab_rh(ts, global_local, df, meta, si_ip): @callback( - Output("solar-radiation-graph", "children"), + Output(ElementIds.SOLAR_RADIATION_GRAPH, "children"), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_SUMMARY_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_SUMMARY_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUMMARY_DF_STORE, "data"), + State(ElementIds.ID_SUMMARY_META_STORE, "data"), + State(ElementIds.ID_SUMMARY_SI_IP_UNIT_STORE, "data"), ], ) def update_tab_gh_rad(ts, global_local, df, meta, si_ip): """Update the contents of tab two. Passing in the general info (df, meta).""" units = generate_units(si_ip) return dcc.Graph( - id="gh_rad-profile-graph", + id=ElementIds.GH_RAD_PROFILE_GRAPH, className="violin-container", config=generate_chart_name("GlobalHorizontalRadiation", meta, units), figure=violin(df, ColNames.GLOB_HOR_RAD, global_local, si_ip), @@ -504,12 +505,12 @@ def update_tab_gh_rad(ts, global_local, df, meta, si_ip): @callback( - Output("download-dataframe-csv", "data"), - [Input("download-button", "n_clicks")], + Output(ElementIds.DOWNLOAD_DATAFRAME_CSV, "data"), + [Input(ElementIds.DOWNLOAD_BUTTON, "n_clicks")], [ - State("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "data"), + State(ElementIds.ID_SUMMARY_DF_STORE, "data"), + State(ElementIds.ID_SUMMARY_META_STORE, "data"), + State(ElementIds.ID_SUMMARY_SI_IP_UNIT_STORE, "data"), ], prevent_initial_call=True, ) @@ -530,9 +531,9 @@ def download_clima_dataframe(n_clicks, df, meta, si_ip): @callback( - Output("download-epw", "data"), - [Input("download-epw-button", "n_clicks")], - [State("meta-store", "data")], + Output(ElementIds.DOWNLOAD_EPW, "data"), + [Input(ElementIds.DOWN_EPW_BUTTON, "n_clicks")], + [State(ElementIds.ID_SUMMARY_META_STORE, "data")], prevent_initial_call=True, ) def download_epw(n_clicks, meta): diff --git a/pages/sun.py b/pages/sun.py index 7d35fae8..74e50e8f 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -1,5 +1,5 @@ from copy import deepcopy -from pages.lib.global_column_names import ElementIds +from pages.lib.global_elementids import ElementIds import dash import dash_bootstrap_components as dbc diff --git a/pages/t_rh.py b/pages/t_rh.py index 400c99f4..47341cba 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -3,7 +3,8 @@ from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import dropdown_names from pages.lib.template_graphs import heatmap, yearly_profile, daily_profile -from pages.lib.global_column_names import ElementIds, Type, ComponentProperty, ColNames +from pages.lib.global_column_names import ColNames +from pages.lib.global_elementids import ElementIds from pages.lib.utils import ( generate_chart_name, generate_units, @@ -37,7 +38,7 @@ def layout(): className="text-next-to-input", children=["Select a variable: "] ), dropdown( - id=ElementIds.DROPDOWN, + id=ElementIds.ID_T_RH_DROPDOWN, className="dropdown-t-rh", options={var: dropdown_names[var] for var in var_to_plot}, value=dropdown_names[var_to_plot[0]], @@ -55,7 +56,7 @@ def layout(): ), ), dcc.Loading( - type=Type.CIRCLE, + type="circle", children=html.Div(id=ElementIds.YEARLY_CHART), ), html.Div( @@ -66,7 +67,7 @@ def layout(): ), ), dcc.Loading( - type=Type.CIRCLE, + type="circle", children=html.Div(id=ElementIds.DAILY), ), html.Div( @@ -77,7 +78,7 @@ def layout(): ), ), dcc.Loading( - type=Type.CIRCLE, + type="circle", children=html.Div(id=ElementIds.HEATMAP), ), html.Div( @@ -97,16 +98,16 @@ def layout(): @callback( - Output(ElementIds.YEARLY_CHART, ComponentProperty.CHILDREN), + Output(ElementIds.YEARLY_CHART, "children"), [ - Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), - Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), + Input(ElementIds.ID_T_RH_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_T_RH_GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_T_RH_DROPDOWN, "value"), ], [ - State(ElementIds.DF_STORE, ComponentProperty.DATA), - State(ElementIds.META_STORE, ComponentProperty.DATA), - State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), + State(ElementIds.ID_T_RH_DF_STORE, "data"), + State(ElementIds.ID_T_RH_META_STORE, "data"), + State(ElementIds.ID_T_RH_SI_IP_UNIT_STORE, "data"), ], ) def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): @@ -129,16 +130,16 @@ def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): @callback( - Output(ElementIds.DAILY, ComponentProperty.CHILDREN), + Output(ElementIds.DAILY, "children"), [ - Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), - Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), + Input(ElementIds.ID_T_RH_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_T_RH_GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_T_RH_DROPDOWN, "value"), ], [ - State(ElementIds.DF_STORE, ComponentProperty.DATA), - State(ElementIds.META_STORE, ComponentProperty.DATA), - State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), + State(ElementIds.ID_T_RH_DF_STORE, "data"), + State(ElementIds.ID_T_RH_META_STORE, "data"), + State(ElementIds.ID_T_RH_SI_IP_UNIT_STORE, "data"), ], ) def update_daily(_, global_local, dd_value, df, meta, si_ip): @@ -167,16 +168,16 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): @callback( - [Output(ElementIds.HEATMAP, ComponentProperty.CHILDREN)], + [Output(ElementIds.HEATMAP, "children")], [ - Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), - Input(ElementIds.GLOBAL_LOCAL_RADIO_INPUT, ComponentProperty.VALUE), - Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), + Input(ElementIds.ID_T_RH_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_T_RH_GLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_T_RH_DROPDOWN, "value"), ], [ - State(ElementIds.DF_STORE, ComponentProperty.DATA), - State(ElementIds.META_STORE, ComponentProperty.DATA), - State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA), + State(ElementIds.ID_T_RH_DF_STORE, "data"), + State(ElementIds.ID_T_RH_META_STORE, "data"), + State(ElementIds.ID_T_RH_SI_IP_UNIT_STORE, "data"), ], ) def update_heatmap(_, global_local, dd_value, df, meta, si_ip): @@ -206,12 +207,12 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): @callback( - Output(ElementIds.TABLE_TMP_HUM, ComponentProperty.CHILDREN), + Output(ElementIds.TABLE_TMP_HUM, "children"), [ - Input(ElementIds.DF_STORE, ComponentProperty.MODIFIED_TIMESTAMP), - Input(ElementIds.DROPDOWN, ComponentProperty.VALUE), + Input(ElementIds.ID_T_RH_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_T_RH_DROPDOWN, "value"), ], - [State(ElementIds.DF_STORE, ComponentProperty.DATA), State(ElementIds.SI_IP_UNIT_STORE, ComponentProperty.DATA)], + [State(ElementIds.ID_T_RH_DF_STORE, "data"), State(ElementIds.ID_T_RH_SI_IP_UNIT_STORE, "data")], ) def update_table(_, dd_value, df, si_ip): """Update the contents of tab three. Passing in general info (df, meta).""" diff --git a/pages/wind.py b/pages/wind.py index d123a0b3..6b8cda4a 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -1,7 +1,7 @@ import dash from dash import dcc, html from dash_extensions.enrich import Output, Input, State, callback -from pages.lib.global_column_names import ElementIds +from pages.lib.global_elementids import ElementIds from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import month_lst, container_row_center_full @@ -382,7 +382,7 @@ def update_annual_wind_rose(_, df, meta, si_ip): # General [ Input(ElementIds.ID_WIND_DF_STORE, "modified_timestamp"), - Input(ElementIds.ID_WINDGLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_WIND_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ State(ElementIds.ID_WIND_DF_STORE, "data"), @@ -406,7 +406,7 @@ def update_tab_wind_speed(_, global_local, df, meta, si_ip): Output(ElementIds.WIND_DIRECTION, "children"), # General [ - Input(ElementIds.ID_WINDGLOBAL_LOCAL_RADIO_INPUT, "value"), + Input(ElementIds.ID_WIND_GLOBAL_LOCAL_RADIO_INPUT, "value"), ], [ State(ElementIds.ID_WIND_DF_STORE, "data"), From 01f40457712f80df5223a8119762bc2c33d1cada Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Sun, 24 Aug 2025 20:34:13 +1000 Subject: [PATCH 30/66] Fix:Align development branch versions to avoid conflicts --- .bumpversion.cfg | 2 +- .github/workflows/cypress.yml | 10 +- .github/workflows/deploy.yml | 41 ++-- .github/workflows/python.yml | 25 ++- .pre-commit-config.yaml | 10 + Pipfile | 1 + Pipfile.lock | 359 ++++++++++++++++++++---------- assets/manifest.json | 2 +- docs/contributing/contributing.md | 11 + pages/lib/extract_df.py | 4 +- pages/lib/layout.py | 2 +- pages/outdoor.py | 12 + pages/wind.py | 2 +- tests/python/test_utils.py | 9 +- 14 files changed, 323 insertions(+), 167 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a862796a..c6fbd8fa 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.8.18 +current_version = 0.9.0 commit = True tag = True diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 0a8d7a21..36aa2465 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -1,5 +1,11 @@ -name: Cypress 🌲 -on: [push, pull_request] +name: Cypress Testing 🌲 +on: + push: + branches: + - development + pull_request: + branches: + - development jobs: cypress: runs-on: ubuntu-latest diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e550f2d6..b33b1daa 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,46 +1,31 @@ # .github/workflows/deploy.yml name: Deploy 🚀 Clima to Google Cloud Run (☁🏃) + on: push: branches: - main + jobs: deploy: name: Deploying 🚀 Clima runs-on: ubuntu-latest - if: "contains(github.event.head_commit.message, 'bump version')" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Setup python - uses: actions/setup-python@v4 + - id: auth + uses: google-github-actions/auth@v1 with: - python-version: '3.11' - - - name: Export gcloud related env variable - run: export CLOUDSDK_PYTHON="/usr/bin/python3" + credentials_json: ${{ secrets.GCP_SA_KEY_JSON }} - # Build and push image to Google Container Registry - - name: Setting up - uses: google-github-actions/setup-gcloud@v0 + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 with: - version: '318.0.0' - service_account_key: ${{ secrets.GCP_SA_KEY_JSON }} - service_account_email: "federico.tartarini@bears-berkeley.sg" - project_id: clima-316917 + project_id: heat-stress-scale - - name: Building (🏗️) - run: |- + - name: Building (🏗️) and Deploying (🚀) + run: | gcloud builds submit \ - --tag us-docker.pkg.dev/clima-316917/gcr.io/clima - - # Setup gcloud CLI - - name: Deploy (🚀) - uses: google-github-actions/deploy-cloudrun@v1 - with: - service: clima - image: us-docker.pkg.dev/clima-316917/gcr.io/clima - region: us-central1 - credentials: ${{ secrets.GCP_SA_KEY_JSON }} - project_id: clima-316917 \ No newline at end of file + --project=clima-316917 \ + --substitutions=_REPO_NAME="clima",_PROJ_NAME="clima-316917",_IMG_NAME="main",_GCR="us.gcr.io",_REGION="us-central1",_MEMORY="4Gi",_CPU="2" \ No newline at end of file diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 919693a6..3f57c5d9 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -1,5 +1,11 @@ -name: Python 🐍 -on: [push, pull_request] +name: Python Testing 🐍 +on: + push: + branches: + - development + pull_request: + branches: + - development jobs: pytest: runs-on: ubuntu-latest @@ -17,13 +23,14 @@ jobs: pip install pipenv pipenv install --dev - - name: Test Clima + - name: Ruff Check run: |- - pipenv run python -m pytest + pipenv run ruff check . + + - name: Ruff Format + run: |- + pipenv run ruff format - - name: Run Black - # TODO: Add to dev dependencies: Adding it right now - # bumps other dependencies and the application won't run. + - name: Test Clima run: |- - pip install black - black . --check + pipenv run python -m pytest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..8a2b92b7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.12.9 + hooks: + # Run the linter. + - id: ruff-check + args: [ --fix ] + # Run the formatter. + - id: ruff-format \ No newline at end of file diff --git a/Pipfile b/Pipfile index b1667a53..91288833 100644 --- a/Pipfile +++ b/Pipfile @@ -23,6 +23,7 @@ pytest = "*" bump2version = "*" black = "*" ruff = "*" +pre-commit = "*" [requires] python_version = "3.11" diff --git a/Pipfile.lock b/Pipfile.lock index beb16723..89f3c053 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f96acae67c176ca7533141c154725a6a5563f14bf40da515952c2ee9b02a74c8" + "sha256": "7214a1158f64483648ecff36a57b61b67020408e0f16770b77d7165a9d6f47e0" }, "pipfile-spec": 6, "requires": { @@ -34,109 +34,96 @@ }, "certifi": { "hashes": [ - "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", - "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995" + "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", + "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5" ], "markers": "python_version >= '3.7'", - "version": "==2025.7.14" + "version": "==2025.8.3" }, "charset-normalizer": { "hashes": [ - "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", - "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45", - "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", - "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", - "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", - "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", - "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d", - "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", - "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184", - "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", - "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b", - "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64", - "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", - "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", - "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", - "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344", - "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58", - "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", - "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", - "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", - "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", - "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", - "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", - "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", - "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", - "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1", - "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01", - "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", - "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58", - "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", - "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", - "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2", - "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a", - "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", - "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", - "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5", - "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb", - "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f", - "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", - "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", - "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", - "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", - "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7", - "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", - "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455", - "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", - "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4", - "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", - "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", - "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", - "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", - "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", - "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", - "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", - "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", - "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", - "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", - "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa", - "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", - "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", - "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", - "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", - "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", - "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", - "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02", - "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", - "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", - "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", - "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", - "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", - "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", - "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", - "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681", - "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", - "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", - "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a", - "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", - "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", - "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", - "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", - "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027", - "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", - "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", - "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", - "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", - "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", - "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", - "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da", - "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", - "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f", - "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", - "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f" + "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", + "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", + "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", + "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", + "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", + "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", + "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c", + "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", + "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", + "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", + "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432", + "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", + "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", + "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", + "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", + "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19", + "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", + "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", + "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4", + "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7", + "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312", + "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", + "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", + "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", + "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", + "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", + "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b", + "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", + "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", + "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", + "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", + "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", + "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", + "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc", + "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", + "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", + "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a", + "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40", + "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", + "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", + "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", + "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", + "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", + "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", + "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", + "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", + "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", + "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34", + "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9", + "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", + "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", + "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", + "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b", + "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", + "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942", + "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", + "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", + "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b", + "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", + "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", + "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", + "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", + "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", + "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", + "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", + "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", + "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", + "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", + "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca", + "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", + "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", + "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb", + "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", + "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557", + "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", + "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7", + "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", + "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", + "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9" ], "markers": "python_version >= '3.7'", - "version": "==3.4.2" + "version": "==3.4.3" }, "click": { "hashes": [ @@ -146,6 +133,14 @@ "markers": "python_version >= '3.10'", "version": "==8.2.1" }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" + }, "dash": { "hashes": [ "sha256:d38891337fc855d5673f75e5346354daa063c4ff45a8a6a21f25e858fcae41c2", @@ -286,7 +281,7 @@ "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd" ], - "markers": "python_version >= '3.7'", + "markers": "python_version >= '3.9'", "version": "==8.7.0" }, "itsdangerous": { @@ -589,11 +584,11 @@ }, "retrying": { "hashes": [ - "sha256:4d206e0ed2aff5ef2f3cd867abb9511e9e8f31127c5aca20f1d5246e476903b0", - "sha256:d736050c1adfc0a71fa022d9198ee130b0e66be318678a3fdd8b1b8872dc0997" + "sha256:bbc004aeb542a74f3569aeddf42a2516efefcdaff90df0eb38fbfbf19f179f59", + "sha256:d102e75d53d8d30b88562d45361d6c6c934da06fab31bd81c0420acb97a8ba39" ], "markers": "python_version >= '3.6'", - "version": "==1.4.1" + "version": "==1.4.2" }, "scipy": { "hashes": [ @@ -731,6 +726,14 @@ "markers": "python_version >= '3.5'", "version": "==1.0.1" }, + "cfgv": { + "hashes": [ + "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", + "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" + ], + "markers": "python_version >= '3.8'", + "version": "==3.4.0" + }, "cleanpy": { "hashes": [ "sha256:9ddfa7ce80dd888b597a8b0bfeea3b69567839b6f41b775a4f76f46914d5170e", @@ -748,6 +751,37 @@ "markers": "python_version >= '3.10'", "version": "==8.2.1" }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" + }, + "distlib": { + "hashes": [ + "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", + "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d" + ], + "version": "==0.4.0" + }, + "filelock": { + "hashes": [ + "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", + "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d" + ], + "markers": "python_version >= '3.9'", + "version": "==3.19.1" + }, + "identify": { + "hashes": [ + "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", + "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32" + ], + "markers": "python_version >= '3.9'", + "version": "==2.6.13" + }, "iniconfig": { "hashes": [ "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", @@ -764,6 +798,14 @@ "markers": "python_version >= '3.8'", "version": "==1.1.0" }, + "nodeenv": { + "hashes": [ + "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", + "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==1.9.1" + }, "packaging": { "hashes": [ "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", @@ -796,6 +838,15 @@ "markers": "python_version >= '3.9'", "version": "==1.6.0" }, + "pre-commit": { + "hashes": [ + "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", + "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==4.3.0" + }, "pygments": { "hashes": [ "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", @@ -813,30 +864,98 @@ "markers": "python_version >= '3.9'", "version": "==8.4.1" }, + "pyyaml": { + "hashes": [ + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" + ], + "markers": "python_version >= '3.8'", + "version": "==6.0.2" + }, "ruff": { "hashes": [ - "sha256:06bfb01e1623bf7f59ea749a841da56f8f653d641bfd046edee32ede7ff6c606", - "sha256:1fc3193f238bc2d7968772c82831a4ff69252f673be371fb49663f0068b7ec71", - "sha256:2e1c2a3b8626339bb6369116e7030a4cf194ea48f49b64bb505732a7fce4f4e3", - "sha256:32dec41817623d388e645612ec70d5757a6d9c035f3744a52c7b195a57e03860", - "sha256:4000623300563c709458d0ce170c3d0d788c23a058912f28bbadc6f905d67afa", - "sha256:47ef751f722053a5df5fa48d412dbb54d41ab9b17875c6840a58ec63ff0c247c", - "sha256:5726f59b171111fa6a69d82aef48f00b56598b03a22f0f4170664ff4d8298efb", - "sha256:5d0bfe4e77fba61bf2ccadf8cf005d6133e3ce08793bbe870dd1c734f2699a3e", - "sha256:69ffe0e5f9b2cf2b8e289a3f8945b402a1b19eff24ec389f45f23c42a3dd6fb5", - "sha256:74e6f5c04c4dd4aba223f4fe6e7104f79e0eebf7d307e4f9b18c18362124bccd", - "sha256:76e4f31529899b8c434c3c1dede98c4483b89590e15fb49f2d46183801565303", - "sha256:789b7a03e72507c54fb3ba6209e4bb36517b90f1a3569ea17084e3fd295500fb", - "sha256:9c18f3d707ee9edf89da76131956aba1270c6348bfee8f6c647de841eac7194f", - "sha256:a07a5c8ffa2611a52732bdc67bf88e243abd84fe2d7f6daef3826b59abbfeda4", - "sha256:a828a5fc25a3efd3e1ff7b241fd392686c9386f20e5ac90aa9234a5faa12c423", - "sha256:c928f1b2ec59fb77dfdf70e0419408898b63998789cc98197e15f560b9e77f77", - "sha256:dfce05101dbd11833a0776716d5d1578641b7fddb537fe7fa956ab85d1769b69", - "sha256:e41df94a957d50083fd09b916d6e89e497246698c3f3d5c681c8b3e7b9bb4ac8" + "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", + "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", + "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", + "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", + "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", + "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", + "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", + "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", + "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", + "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", + "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", + "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", + "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", + "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", + "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", + "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", + "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", + "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", + "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.12.7" + "version": "==0.12.10" + }, + "virtualenv": { + "hashes": [ + "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", + "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a" + ], + "markers": "python_version >= '3.8'", + "version": "==20.34.0" } } } diff --git a/assets/manifest.json b/assets/manifest.json index 40b8618d..591f5946 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -467,7 +467,7 @@ "orientation": "portrait", "background_color": "#ffffff", "display": "standalone", - "id": "0.8.18", + "id": "0.9.0", "description": "CBE Clima Tool: a free and open-source web application for climate analysis tailored to sustainable building design", "start_url": "/", "scope": "/", diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 6fc210af..0da8696c 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -81,6 +81,17 @@ We use Black.exe to format the code. Install Black: +We use ruff to enforce the code style and code formatting. You can run it with: + +```bash +pipenv run ruff check . +pipenv run ruff format . +``` + +To ensure that your code is formatted correctly, we have a pre-commit hook that will run ruff before every commit. +Hence, you will need to make that the code is formatted correctly before committing your changes otherwise the commit will fail. +More information about pre-commit hooks can be found [here](https://pre-commit.com/). + ```bash pipenv install black ``` diff --git a/pages/lib/extract_df.py b/pages/lib/extract_df.py index c4c9a3b0..087c67cf 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -6,6 +6,7 @@ from datetime import timedelta from urllib.request import Request, urlopen +import logging import numpy as np import pandas as pd import requests @@ -43,7 +44,8 @@ def get_data(source_url): req = Request(source_url, headers=headers) epw = urlopen(req).read().decode() return epw.split("\n") - except: + except Exception as e: + logging.error(f"Failed to fetch EPW data: {e}") return None diff --git a/pages/lib/layout.py b/pages/lib/layout.py index 1edea71d..ea7488b7 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -75,7 +75,7 @@ def footer(): dmc.Group( [ dmc.Anchor( - "Version: 0.8.18", + "Version: 0.9.0", href="https://center-for-the-built-environment.gitbook.io/clima/version/changelog", underline=True, c="white", diff --git a/pages/outdoor.py b/pages/outdoor.py index eb2350f8..b9fbbe5a 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -222,6 +222,18 @@ def layout(): ], ) def update_outdoor_comfort_output(_, df): + """ + Find the column(s) with the highest number of zero values. + + Args: + _: Unused callback input. + df: DataFrame-like object containing UTCI category columns. + + Returns + ------- + str + Description of the best weather condition(s). + """ cols = [ "utci_noSun_Wind_categories", "utci_noSun_noWind_categories", diff --git a/pages/wind.py b/pages/wind.py index 6b8cda4a..9ef474ae 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -329,7 +329,7 @@ def layout(): children=[ html.Div( children=title_with_link( - text="Wind Rose", + text="Annual Wind Rose", id_button="wind-rose-label", doc_link=DocLinks.WIND_ROSE, ), diff --git a/tests/python/test_utils.py b/tests/python/test_utils.py index eb0c59bd..714d1068 100644 --- a/tests/python/test_utils.py +++ b/tests/python/test_utils.py @@ -5,14 +5,17 @@ from config import UnitSystem +import requests + +from pages.lib.utils import summary_table_tmp_rh_tab +from pages.lib.extract_df import get_data, create_df + + root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) if root_dir not in sys.path: sys.path.append(root_dir) -import requests -from pages.lib.utils import summary_table_tmp_rh_tab -from pages.lib.extract_df import get_data, create_df def save_epw_test(path_file): From 7828453b84a30b4d9932c6149383ce6237d550eb Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:10:28 +1000 Subject: [PATCH 31/66] fix: aligned development branch versions of 3 days ago to avoid conflicts. --- .github/workflows/deploy.yml | 2 +- .github/workflows/python.yml | 6 +++--- cloudbuild.yaml | 2 +- config.py | 16 ++++++++-------- docs/README.md | 4 +++- docs/contributing/contributing.md | 12 ++++++++++-- docs/contributing/run-project-locally.md | 2 +- docs/documentation/tabs-explained/README.md | 2 +- .../tabs-explained/natural-ventilation.md | 3 --- .../outdoor-comfort/utci-explained.md | 6 +++--- .../psychrometric-chart/README.md | 7 +++---- .../psychrometric-chart-explained.md | 6 +++--- .../tabs-explained/sun-and-cloud/README.md | 5 ++--- .../sun-and-cloud/cloud-coverage.md | 4 ++-- .../customizable-daily-and-hourly-maps.md | 5 ++--- .../README.md | 2 +- docs/documentation/tabs-explained/tab-home.md | 6 +++--- .../tabs-explained/tab-summary/README.md | 6 +++--- .../tab-summary/clima-dataframe.md | 18 +++++++++--------- .../tab-summary/climate-profiles-explained.md | 4 ++-- .../tab-summary/degree-days-explained.md | 4 ++-- .../wind/how-to-read-a-wind-rose.md | 2 +- pages/lib/layout.py | 2 +- pages/lib/template_graphs.py | 2 +- pages/not_found_404.py | 4 +--- pages/select.py | 7 +++---- pages/summary.py | 9 ++++++++- tests/python/test_utils.py | 8 -------- 28 files changed, 78 insertions(+), 78 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b33b1daa..bc24325c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v4 - id: auth - uses: google-github-actions/auth@v1 + uses: google-github-actions/auth@v2 with: credentials_json: ${{ secrets.GCP_SA_KEY_JSON }} diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 3f57c5d9..32f7e7c3 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -25,11 +25,11 @@ jobs: - name: Ruff Check run: |- - pipenv run ruff check . + pipenv run ruff check . --output-format=github - - name: Ruff Format + - name: Ruff Format (verify) run: |- - pipenv run ruff format + pipenv run ruff format --check . - name: Test Clima run: |- diff --git a/cloudbuild.yaml b/cloudbuild.yaml index a60de353..8eb6ac4a 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -12,4 +12,4 @@ steps: '--image', '$_GCR/$_PROJ_NAME/$_REPO_NAME/$_IMG_NAME', '--region', '$_REGION', '--memory', '$_MEMORY', - '--cpu', '$_CPU',] \ No newline at end of file + '--cpu', '$_CPU'] \ No newline at end of file diff --git a/config.py b/config.py index bc7c61e8..bc794545 100644 --- a/config.py +++ b/config.py @@ -61,21 +61,21 @@ class PageInfo: TEMP_RH_NAME = "Temperature and Humidity" TEMP_RH_ORDER = 2 SOLAR_RADIATION_NAME = "Solar Radiation" - SOLAR_RADIATION_ORDER = 2 + SOLAR_RADIATION_ORDER = 3 SUN_NAME = "Sun and Clouds" - SUN_ORDER = 3 + SUN_ORDER = 4 WIND_NAME = "Wind" - WIND_ORDER = 4 + WIND_ORDER = 5 PSYCHROMETRIC_NAME = "Psychrometric Chart" - PSYCHROMETRIC_ORDER = 5 + PSYCHROMETRIC_ORDER = 6 NATURAL_VENTILATION_NAME = "Natural Ventilation" - NATURAL_VENTILATION_ORDER = 6 + NATURAL_VENTILATION_ORDER = 7 UTCI_NAME = "Outdoor Comfort" - UTCI_ORDER = 7 + UTCI_ORDER = 8 EXPLORER_NAME = "Data Explorer" - EXPLORER_ORDER = 8 + EXPLORER_ORDER = 9 CHANGELOG_NAME = "Changelog" - CHANGELOG_ORDER = 9 + CHANGELOG_ORDER = 10 NOT_FOUND_NAME = "404" diff --git a/docs/README.md b/docs/README.md index 12986645..05ce6e22 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,7 +10,7 @@ The CBE Clima Tool is a web-based application built to support climate analysis The CBE Clima Tool is open source. We have released the source code on a [public repository](https://github.com/CenterForTheBuiltEnvironment/clima). We welcome contributions from the community ([more info here](contributing/contributing.md)). -### Video Tutorial +## Video Tutorial Learn more about the CBE Clima Tool by watching the following video. @@ -18,6 +18,8 @@ Learn more about the CBE Clima Tool by watching the following video. CBE Clima tool tutorial and overview {% endembed %} +[Watch on YouTube](https://www.youtube.com/watch?v=VJ_wOHadVdw) + ## Contributions This ongoing project results from the collaboration and contributions of the people listed below. diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 0da8696c..d005aa08 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -88,8 +88,16 @@ pipenv run ruff check . pipenv run ruff format . ``` -To ensure that your code is formatted correctly, we have a pre-commit hook that will run ruff before every commit. -Hence, you will need to make that the code is formatted correctly before committing your changes otherwise the commit will fail. +To ensure that the code is formatted correctly, we use a pre-commit hook that runs Ruff before every commit. +Run the following once to enable hooks in your local repo: + +```bash +pipenv run pre-commit install +# optional: run on all files +pipenv run pre-commit run --all-files +``` + +Hence, you will need to make sure that the code is formatted correctly before committing your changes; otherwise, the commit will fail. More information about pre-commit hooks can be found [here](https://pre-commit.com/). ```bash diff --git a/docs/contributing/run-project-locally.md b/docs/contributing/run-project-locally.md index fef988bd..e125cd23 100644 --- a/docs/contributing/run-project-locally.md +++ b/docs/contributing/run-project-locally.md @@ -27,7 +27,7 @@ This guide is for Mac OSX, Linux, or Windows. 2. **Create a virtual environment using pipenv and install dependencies:** ```text - pipenv install + pipenv install --dev ``` 3. **Run tool locally** diff --git a/docs/documentation/tabs-explained/README.md b/docs/documentation/tabs-explained/README.md index 12b13e7a..b10d8a35 100644 --- a/docs/documentation/tabs-explained/README.md +++ b/docs/documentation/tabs-explained/README.md @@ -8,7 +8,7 @@ description: >- The Clima app is organized in a series of tabs that allow the exploration of various topics. All the tabs other than "Select Weather File" are active after a weather file has been selected. -Although there is a logical sequence in the organization of the tabs, thy can be accessed in any order. +Although there is a logical sequence in the organization of the tabs, thy can be accessed in any order. The Followin section will explain the content and the usage of each tab. diff --git a/docs/documentation/tabs-explained/natural-ventilation.md b/docs/documentation/tabs-explained/natural-ventilation.md index f863de59..3d140408 100644 --- a/docs/documentation/tabs-explained/natural-ventilation.md +++ b/docs/documentation/tabs-explained/natural-ventilation.md @@ -32,6 +32,3 @@ Learn more about the Natural Ventilation tab by watching the following video. {% embed url="https://youtu.be/VJ_wOHadVdw?si=_cUoFQGyxJD7V85a&t=703" %} - - - diff --git a/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md b/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md index 08adc0d6..bb6df199 100644 --- a/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md +++ b/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md @@ -1,6 +1,6 @@ # UTCI explained -The UTCI tab allows users to analyze outdoor thermal comfort for a combination of different meteorological conditions based on the presence or absence of sun and wind. +The UTCI tab allows users to analyze outdoor thermal comfort for a combination of different meteorological conditions based on the presence or absence of sun and wind. ![Logos highlighting the different scenarios which can be displayed in Clima](<../../../.gitbook/assets/UTCI 01-01.jpg>) @@ -9,7 +9,7 @@ Clima leverages the several models implemented in [Pythermalcomfort](https://pyt * The "[Solar gain on people](https://pythermalcomfort.readthedocs.io/en/latest/reference/pythermalcomfort.html#solar-gain-on-people)" calculates the solar gain to the human body, so the mean radiant temperature. To simulate a sunless situation, Clima considers the person surrounded by surfaces that shade him, all of which tend toward dry bulb temperature; * Wind data is obtained directly from the weather file. The windless situation sets the value at 0.5 m/s, which is the minimum value allowed by the UTCI model. -The UTCI can then be visualized for the entire year for the scenario chosen. +The UTCI can then be visualized for the entire year for the scenario chosen.

UTCI perceived temperature annual heatmap in the four conditions for Rome, ITA

@@ -17,4 +17,4 @@ The values are then converted into a scale assessing thermal stress, either beca

UTCI heat stress index heatmap in the four conditions for Rome, ITA

-The UTCI is a useful tool to design the outdoor space, to maximize the number of comfortable hours. The designer can influence two factors out of the four driving outdoor comfort: radiant temperature (i.e. exposure to the sun) and wind speed (i.e. exposure to the wind). +The UTCI is a useful tool to design the outdoor space, to maximize the number of comfortable hours. The designer can influence two factors out of the four driving outdoor comfort: radiant temperature (i.e. exposure to the sun) and wind speed (i.e. exposure to the wind). \ No newline at end of file diff --git a/docs/documentation/tabs-explained/psychrometric-chart/README.md b/docs/documentation/tabs-explained/psychrometric-chart/README.md index 1ef00ac0..a0817de6 100644 --- a/docs/documentation/tabs-explained/psychrometric-chart/README.md +++ b/docs/documentation/tabs-explained/psychrometric-chart/README.md @@ -1,12 +1,12 @@ # Psychrometric Chart -**Clima** allows the user to visualize all annual weather conditions on a [psychometric diagram.](psychrometric-chart-explained.md) +**Clima** allows the user to visualize all annual weather conditions on a [psychrometric diagram.](psychrometric-chart-explained.md) The default diagram allows the users to overlay the frequency with which weather conditions recur throughout the year.

Example: Frequency of climatic condition in the Psychrometric chart of New York, USA

-With the first choice in the drop-down list, "None", it is possible to view temperature conditions in the psychometric diagram over the entire year. The visualized dots have the same gradient with a transparency rate, they are not colored according to a legend. Multiplying them when overlaid provides a visualization of their frequency, so the most common conditions. +With the first choice in the drop-down list, "None", it is possible to view temperature conditions in the psychrometric diagram over the entire year. The visualized dots have the same gradient with a transparency rate, they are not colored according to a legend. Multiplying them when overlaid provides a visualization of their frequency, so the most common conditions.

Example: Psychrometric chart with climatic conditions of New York, USA

@@ -22,5 +22,4 @@ Moreover, data can be filtered by date, time, or one of the [Clima dataframe](.. Learn more about the Psychrometric tab by watching the following video. -{% embed url="https://youtu.be/VJ_wOHadVdw?si=iAcBQpq3IgCNY-H6&t=582" %} - +{% embed url="https://youtu.be/VJ_wOHadVdw?si=iAcBQpq3IgCNY-H6&t=582" %} \ No newline at end of file diff --git a/docs/documentation/tabs-explained/psychrometric-chart/psychrometric-chart-explained.md b/docs/documentation/tabs-explained/psychrometric-chart/psychrometric-chart-explained.md index 9defd6e1..28a3b285 100644 --- a/docs/documentation/tabs-explained/psychrometric-chart/psychrometric-chart-explained.md +++ b/docs/documentation/tabs-explained/psychrometric-chart/psychrometric-chart-explained.md @@ -2,7 +2,7 @@ A [psychrometric diagram](https://en.wikipedia.org/wiki/Psychrometrics) is a psychrometry tool used to understand the relationship between humidity and air temperature conditions. Through the use of the psychrometric diagram and appropriate calculations, it is possible to know the amount of heat or cooling needed to achieve the desired temperature and humidity. -The **Clima** psychometric diagram shows dry bulb temperature on the abscissae, specific humidity on the ordinates, and relative humidity as parametric curves inside the graph. +The **Clima** psychrometric diagram shows dry bulb temperature on the abscissae, specific humidity on the ordinates, and relative humidity as parametric curves inside the graph.

Temperature line in the Psychrometric diagram

@@ -14,7 +14,7 @@ All air conditions cannot go beyond the 100% saturation curve, which means that

Relative humidity curves in the Psychrometric diagram

-The simplest transformation to be analyzed on the psychometric diagram is the heating and cooling processes. The transition from the starting condition (1) to the final one (2) occurs horizontally, at constant humidity ratio values. The final condition (2) can be inspected as a function of the starting one. +The simplest transformation to be analyzed on the psychrometric diagram is the heating and cooling processes. The transition from the starting condition (1) to the final one (2) occurs horizontally, at constant humidity ratio values. The final condition (2) can be inspected as a function of the starting one.

Cooling and heating process

@@ -28,4 +28,4 @@ The main application of the psychrometric diagram is in the design of large all- The diagram is applied whenever the humidity of a particular environment needs to be studied, for reasons of thermal comfort or for the preservation of valuable objects, such as in museums. -

Stradivari Violin, stored under precise temperature and humidity conditions to prevent the valuable wood from warping. Source: Frammentirivista

+

Stradivari Violin, stored under precise temperature and humidity conditions to prevent the valuable wood from warping. Source: Frammentirivista

\ No newline at end of file diff --git a/docs/documentation/tabs-explained/sun-and-cloud/README.md b/docs/documentation/tabs-explained/sun-and-cloud/README.md index ff1f17f2..c54f5571 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/README.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/README.md @@ -23,7 +23,6 @@ This allows the user to identify climatic patterns in relation to the apparent s ### Video Tutorial -Learn more about the Sun and Cloud tab by watching the following video. - -{% embed url="https://youtu.be/VJ_wOHadVdw?si=mB2xNH57MWW_4CRR&t=447" %} +Learn more about the Sun and Clouds tab by watching the following video. +{% embed url="https://youtu.be/VJ_wOHadVdw?si=mB2xNH57MWW_4CRR&t=447" %} \ No newline at end of file diff --git a/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md b/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md index 3228741a..bc250e1a 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md @@ -2,7 +2,7 @@ The cloud coverage diagram reports, for every month of the year the frequency of "clear", "cloudy" or "intermediate" conditions. -As the Cloud cover is reported in tenths of coverage (i.e. 0 is 0/10 covered. 10 is total coverage) for the purpose of this graph we have simplified the scale as per the table below. +As the Cloud cover is reported in tenths of coverage (i.e. 0 is 0/10 covered. 10 is total coverage) for the purpose of this graph we have simplified the scale as per the table below. | Categorization | Color | Tenth of coverage | | ----------------------- | ------------------------------------------------------------------------------- | ----------------- | @@ -18,4 +18,4 @@ As the Cloud cover is reported in tenths of coverage (i.e. 0 is 0/10 covered. 10 | Cloudy (ABOVE range) | | 9 | | Cloudy (ABOVE range) | | 10 | -

Example cloud coverage graph for San Francisco, USA

+

Example cloud coverage graph for San Francisco, USA

\ No newline at end of file diff --git a/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md b/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md index bdc56435..c1dfc256 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md @@ -8,7 +8,6 @@ The chart above shows the [scatter plot](https://en.wikipedia.org/wiki/Scatter\_

Example: Heat map of the hourly Global Horizontal radiation on all days of the year for San Francisco, USA

-[Heat maps](https://en.wikipedia.org/wiki/Heat\_map) allow the intensity of values to be perceived through color palettes. These graphs are very helpful in seeing how magnitudes vary throughout the year. - -

Examples of daily graphs with different variables (from top left to bottom right): global horizontal radiation, global horizontal illuminance, zenith luminance, opaque sky cover

+[Heat maps](https://en.wikipedia.org/wiki/Heat\_map) allow the intensity of values to be perceived through color palettes. These graphs are very helpful in seeing how magnitudes vary throughout the year. +

Examples of daily graphs with different variables (from top left to bottom right): global horizontal radiation, global horizontal illuminance, zenith luminance, opaque sky cover

\ No newline at end of file diff --git a/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/README.md b/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/README.md index d9f65b5a..5013530d 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/README.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/README.md @@ -11,4 +11,4 @@ Typical daily graphs showing the amount of energy gained from the sun have many * manage the **indirect solar gain** transfer into the building with a time shift, exploiting the thermal mass, heating thick walls or concrete floors, or designing special rooms adjacent to the main spaces that rely on convection to transfer the heat, such as sunroom or [Trombe wall](https://en.wikipedia.org/wiki/Trombe\_wall); * evaluating sustainable **renewable energy solutions** such as solar thermal or photovoltaic panels. - The integral of the curves in the graphs is the total energy (in Wh/m²), supplied by the sun. Be careful in considering the [different types of solar radiation.](global-diffuse-and-normal-solar-radiation-explained.md) +The integral of the curves in the graphs is the total energy (in Wh/m²), supplied by the sun. Be careful in considering the [different types of solar radiation.](global-diffuse-and-normal-solar-radiation-explained.md) \ No newline at end of file diff --git a/docs/documentation/tabs-explained/tab-home.md b/docs/documentation/tabs-explained/tab-home.md index c0e73350..a7c1106a 100644 --- a/docs/documentation/tabs-explained/tab-home.md +++ b/docs/documentation/tabs-explained/tab-home.md @@ -4,13 +4,13 @@ description: This page explains how a user can load an EPW file in the Clima too # Select Weather File -Users can either choose to analyse the climate of the locations displayed on the map or upload a custom EPW file. After loading an EPW file the user can then access the other tabs to generate dynamic visualisations of the data. +Users can either choose to analyse the climate of the locations displayed on the map or upload a custom EPW file. After loading an EPW file the user can then access the other tabs to generate dynamic visualisations of the data. -### Video Tutorial +## Video Tutorial Learn more about how to analyse the climate of a specific location and uploading your custom EPW file by watching the following video. {% embed url="https://youtu.be/VJ_wOHadVdw?si=SxvUzaI9rCNIFFs0&t=136" %} -How to select or upload a EPW file +How to select or upload an EPW file {% endembed %} diff --git a/docs/documentation/tabs-explained/tab-summary/README.md b/docs/documentation/tabs-explained/tab-summary/README.md index 0f251189..4d474c10 100644 --- a/docs/documentation/tabs-explained/tab-summary/README.md +++ b/docs/documentation/tabs-explained/tab-summary/README.md @@ -8,9 +8,9 @@ The bottom section of the page comprises the heating and cooling degree day char ![Tab summary top](../../../.gitbook/assets/clima-summary-bottom.png) -### Video Tutorial +## Video Tutorial Learn more about the Climate Summary tab by watching the following video. -{% embed url="https://youtu.be/VJ_wOHadVdw?si=H-93XRhh5Neuby_b&t=220" %} - + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=H-93XRhh5Neuby_b&t=220" %} \ No newline at end of file diff --git a/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md b/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md index 11cc1d82..0806f435 100644 --- a/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md +++ b/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md @@ -1,6 +1,6 @@ # Clima Dataframe -**Clima** calculates new variables and creates a new **dataframe** containing the variables already inside the original EPW files and other we calculate. Users can overlay all the variables on the [sun path](../sun-and-cloud/), on the [psychometric chart](../psychrometric-chart/), and on the customizable graphs in the [data explorer](../data-explorer.md). +**Clima** calculates new variables and creates a new **dataframe** containing the variables already inside the original EPW files and other we calculate. Users can overlay all the variables on the [sun path](../sun-and-cloud/), on the [psychrometric chart](../psychrometric-chart/), and on the customizable graphs in the [data explorer](../data-explorer.md). All the variables in the new Clima dataframe are listed below. @@ -13,15 +13,15 @@ All the variables in the new Clima dataframe are listed below. * [Horizontal Infrared Radiation ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-horizontal-infrared-radiation-intensity) * [Global Horizontal Radiation ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-global-horizontal-radiation) * [Direct Normal Radiation ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-direct-normal-radiation) -* [Diffuse Horizontal Radiation](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-diffuse-horizontal-radiation) -* [Global Horizontal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-global-horizontal-illuminance) -* [Direct Normal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-direct-normal-illuminance) +* [Diffuse Horizontal Radiation](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-diffuse-horizontal-radiation) +* [Global Horizontal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-global-horizontal-illuminance) +* [Direct Normal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-direct-normal-illuminance) * [Diffuse Horizontal Illuminance ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-diffuse-horizontal-illuminance) * [Zenith Luminance ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-zenith-luminance) -* [Wind Direction](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-direction) -* [Wind Speed](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-speed) -* [Total Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-total-sky-cover) -* [Opaque Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-opaque-sky-cover) +* [Wind Direction](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-direction) +* [Wind Speed](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-speed) +* [Total Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-total-sky-cover) +* [Opaque Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-opaque-sky-cover) * [Visibility](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-visibility) * [UTCI, Universal Thermal Climate Index](../outdoor-comfort/utci-explained.md) * [Vapor partial pressure](https://en.wikipedia.org/wiki/Vapor\_pressure) @@ -29,4 +29,4 @@ All the variables in the new Clima dataframe are listed below. * [Wet-bulb temperature](https://en.wikipedia.org/wiki/Wet-bulb\_temperature) * [Elevation](https://en.wikipedia.org/wiki/Solar\_zenith\_angle) * [Azimuth](https://en.wikipedia.org/wiki/Solar\_azimuth\_angle) -* [Saturation pressure](https://en.wikipedia.org/wiki/Vapour\_pressure\_of\_water) +* [Saturation pressure](https://en.wikipedia.org/wiki/Vapour\_pressure\_of\_water) \ No newline at end of file diff --git a/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md b/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md index 6eb090b7..dc45d8e4 100644 --- a/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md +++ b/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md @@ -2,7 +2,7 @@ The Climate Profiles graph gives the user the opportunity to observe at a glance the distribution of the data in the EPW file for four key variables and their variation between day and night. -The Climate Profiles graph are [Violin Plots](https://en.wikipedia.org/wiki/Violin\_plot). They show the [probability density](https://en.wikipedia.org/wiki/Probability\_density\_function) of the data at different values, usually smoothed by a [kernel density estimator](https://en.wikipedia.org/wiki/Kernel\_density\_estimator). Wider sections of the violin plot represent a higher probability that members of the population will take on the given value; the skinnier sections represent a lower probability. +The Climate Profiles graph are [Violin Plots](https://en.wikipedia.org/wiki/Violin\_plot). They show the [probability density](https://en.wikipedia.org/wiki/Probability\_density\_function) of the data at different values, usually smoothed by a [kernel density estimator](https://en.wikipedia.org/wiki/Kernel\_density\_estimator). Wider sections of the violin plot represent a higher probability that members of the population will take on the given value; the skinnier sections represent a lower probability. On mouse hover, they display various statistical properties of the data: @@ -13,4 +13,4 @@ On mouse hover, they display various statistical properties of the data: * 1st quartile * 3rd quartile -![Climate Profiles for Jerusalem Center, ISRAEL](<../../../.gitbook/assets/image (2) (1) (1).png>) +![Climate Profiles for Jerusalem Center, ISRAEL](<../../../.gitbook/assets/image (2) (1) (1).png>) \ No newline at end of file diff --git a/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md b/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md index 21d70bc9..0d6d9d09 100644 --- a/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md +++ b/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md @@ -4,7 +4,7 @@ you might want to start understanding what degree days are here: {% embed url="https://en.wikipedia.org/wiki/Heating_degree_day" %} -**Degree days** are calculated as the _integral of the difference between the outside air temperature and a base temperature over time_. +**Degree days** are calculated as the _integral of the difference between the outside air temperature and a base temperature over time_. If you can define a **base temperature** (the outside temperature above which a building needs no heating or cooling) then you can use this to estimate degree days. The building requires heating if the outside air temperature falls below the heating base temperature, and the heating degree days accrue; if the outside air temperature rises above the cooling base temperature, the structure requires cooling, and the cooling degree days accumulate. @@ -12,4 +12,4 @@ The base temperature does not necessarily correspond to the desired building int ![example deegree days for New York, Downtown Manhattan, NY, USA](<../../../.gitbook/assets/image (3).png>) -![example deegree days for Palermo Boccadifalco Airport, ITALY](<../../../.gitbook/assets/image (1) (1).png>) +![example deegree days for Palermo Boccadifalco Airport, ITALY](<../../../.gitbook/assets/image (1) (1).png>) \ No newline at end of file diff --git a/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md b/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md index 617e30e0..e7838698 100644 --- a/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md +++ b/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md @@ -14,7 +14,7 @@ The length of each radius around the circle shows how often the wind blew from t

Frequency of wind intensity recurrence in the wind rose

-As most graphs in **Clima Tool**, the wind rose is strongly interactive. Clicking on the legend will hide or highlight the selected category. As such, it is easy to go from a wind rose showing all the wind directions and frequency to one that highlights only the selected speed range. This can be particularly useful to identify low-frequency, high-speed wind patterns. +As most graphs in **Clima Tool**, the wind rose is strongly interactive. Clicking on the legend will hide or highlight the selected category. As such, it is easy to go from a wind rose showing all the wind directions and frequency to one that highlights only the selected speed range. This can be particularly useful to identify low-frequency, high-speed wind patterns.

Left: wind rose showing all velocity ranges
Right: the same data can be easily filtered (by clicking on the legend) to show only direction and frequency of wind speeds above 10.7m/s

diff --git a/pages/lib/layout.py b/pages/lib/layout.py index ea7488b7..558e3745 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -14,7 +14,7 @@ def alert(): children=[ dbc.Toast( [ - "If you have a moment, help us improving Clima and take a ", + "If you have a moment, help us improve Clima and take a ", html.A( "quick user survey", href="https://forms.gle/k289zP3R92jdu14M7", diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index e5acbe95..a5b214fa 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -850,5 +850,5 @@ def catch(func, handle=lambda e: e, *args, **kwargs): # Handle category not in dictionary try: return func(*args, **kwargs) - except Exception: + except (KeyError, IndexError, TypeError): return 0 diff --git a/pages/not_found_404.py b/pages/not_found_404.py index d28a630e..ff3b389c 100644 --- a/pages/not_found_404.py +++ b/pages/not_found_404.py @@ -13,9 +13,7 @@ layout = [ dmc.Title("I could not find the page you are currently looking for", order=4), - dmc.Text( - "Please navigate the the home page by using the button below", className="mb-2" - ), + dmc.Text("Use the button below to return to the home page.", className="mb-2"), Lottie( options=dict( loop=True, diff --git a/pages/select.py b/pages/select.py index d0e27df9..ad1a3a32 100644 --- a/pages/select.py +++ b/pages/select.py @@ -191,8 +191,8 @@ def submitted_data( messages_alert["invalid_format"], "warning", ) - except Exception: - # print(e) + except (ValueError, IndexError, KeyError) as e: + print(f"Error parsing EPW file: {e}") return ( None, None, @@ -331,8 +331,7 @@ def plot_location_epw_files(pathname): df = json_normalize(data["features"]) df[["lon", "lat"]] = pd.DataFrame(df["geometry.coordinates"].tolist()) - df["lat"] += 0.005 - df["lat"] += 0.005 + df["lat"] += 0.010 df = df.rename(columns={"properties.epw": "Source"}) fig = px.scatter_mapbox( diff --git a/pages/summary.py b/pages/summary.py index d06dceb6..6df0112a 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -263,7 +263,14 @@ def update_location_info(ts, df, meta, si_ip): total_solar_rad_unit = "k" + mapping_dictionary["glob_hor_rad"][si_ip]["unit"] total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" - total_diffuse_rad = f"Percentage of diffuse horizontal solar radiation: {round(df['dif_hor_rad'].sum() / df['glob_hor_rad'].sum() * 100, 1)} %" + glob_sum = df["glob_hor_rad"].sum() + if glob_sum > 0: + diffuse_percentage = round(df["dif_hor_rad"].sum() / glob_sum * 100, 1) + else: + diffuse_percentage = 0 + total_diffuse_rad = ( + f"Percentage of diffuse horizontal solar radiation: {diffuse_percentage} %" + ) tmp_unit = mapping_dictionary[ColNames.DBT][si_ip]["unit"] average_yearly_tmp = ( f"Average yearly temperature: {df['DBT'].mean().round(1)} " + tmp_unit diff --git a/tests/python/test_utils.py b/tests/python/test_utils.py index 714d1068..ab085c42 100644 --- a/tests/python/test_utils.py +++ b/tests/python/test_utils.py @@ -1,4 +1,3 @@ -import sys import os import pandas as pd @@ -11,13 +10,6 @@ from pages.lib.extract_df import get_data, create_df -root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) -if root_dir not in sys.path: - sys.path.append(root_dir) - - - - def save_epw_test(path_file): test_url = "http://climate.onebuilding.org/WMO_Region_6_Europe/ITA_Italy/ER_Emilia-Romagna/ITA_ER_Bologna-Marconi.AP.161400_TMYx.2004-2018.zip" From c7f11c4915742a5a33995d5ba305c67803132762 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sun, 24 Aug 2025 22:10:30 +1000 Subject: [PATCH 32/66] fix: updated new column names --- pages/lib/global_column_names.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 6babc9a7..c8c627d0 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -13,6 +13,9 @@ class ColNames(str, Enum): DBT = "DBT" # Dry Bulb Temperature DPT = "DPT" # Dew Point Temperature RH = "RH" # Relative Humidity + HI_RH = "hiRH" # High Relative Humidity + LO_RH = "loRH" # Low Relative Humidity + HR = "hr" P_ATM = "p_atm" # Atmospheric Pressure # ==================== Radiation-related column ==================== @@ -31,6 +34,8 @@ class ColNames(str, Enum): ZLUMI = "Zlumi" # Luminance WIND_DIR = "wind_dir" # Wind Direction WIND_SPEED = "wind_speed" # Wind Speed + WIND_SPEED_UTCI = "wind_speed_utci" # Wind Speed Utci + WIND_SPEED_UTCI_0 = "wind_speed_utci_0" # Wind Speed Utci 0 TOT_SKY_COVER = "tot_sky_cover" # Total Sky Cover OSKYCOVER = "Oskycover" # Opaque Sky Cover VIS = "Vis" # Visibility @@ -41,6 +46,22 @@ class ColNames(str, Enum): AsolOptD = "AsolOptD" # Aerosol Optical Depth SnowD = "SnowD" # Snow Depth DaySSnow = "DaySSnow" # Daily Snow + ELEVATION = "elevation" # Elevation + APPARENT_ELEVATION = "apparent_elevation" # Apparent Elevation + AZIMUTH = "azimuth" # Azimuth + MRT = "MRT" + DELTA_MRT = "delta_mrt" + UTCI_SUN_WIND = "utci_Sun_Wind" # Utci Sun Wind + UTCI_SUN_NO_WIND = "utci_Sun_noWind" # Utci Sun no Wind + UTCI_NO_SUN_WIND = "utci_noSun_Wind" # Utci no Sun Wind + UTCI_NO_SUN_NO_WIND = "utci_noSun_noWind" # Utci no Sun no Wind + ADAPTIVE_COMFORT = "adaptive_comfort" # Adaptive comfort + ADAPTIVE_CMF_80_LOW = "adaptive_cmf_80_low" # Adaptive comfort 80 low + ADAPTIVE_CMF_80_UP = "adaptive_cmf_80_up" # Adaptive comfort 80 up + ADAPTIVE_CMF_90_LOW = "adaptive_cmf_90_low" # Adaptive comfort 90 low + ADAPTIVE_CMF_90_UP = "adaptive_cmf_90_up" # Adaptive comfort 90 up + ADAPTIVE_CMF_RMT = "adaptive_cmf_rmt" # Adaptive comfort rmt + NV_ALLOWED = "nv_allowed" # ==================== Calculation column ==================== FAKE_YEAR = "fake_year" # Fake Year From a6acb0bb51ba04f41c79d4e6863fd0160f1b19e2 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sun, 24 Aug 2025 22:11:12 +1000 Subject: [PATCH 33/66] fix: replaced the string with new column names --- pages/explorer.py | 19 +++++---- pages/lib/charts_sun.py | 40 +++++++++--------- pages/lib/extract_df.py | 80 ++++++++++++++++++------------------ pages/lib/template_graphs.py | 8 ++-- pages/natural_ventilation.py | 12 +++--- pages/psy-chart.py | 43 +++++++++---------- pages/summary.py | 8 ++-- 7 files changed, 106 insertions(+), 104 deletions(-) diff --git a/pages/explorer.py b/pages/explorer.py index 92377385..3e079ebc 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -13,6 +13,7 @@ three_var_graph, ) from pages.lib.global_elementids import ElementIds +from pages.lib.global_column_names import ColNames from pages.lib.global_scheme import ( fig_config, dropdown_names, @@ -233,7 +234,7 @@ def section_two_inputs(): dropdown( id=ElementIds.SEC2_VAR_DROPDOWN, options=explore_dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -334,7 +335,7 @@ def section_two_inputs(): dropdown( id=ElementIds.SEC2_DATA_FILTER_VAR, options=explore_dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -438,7 +439,7 @@ def section_three_inputs(): dropdown( id=ElementIds.TAB6_SEC3_VAR_Y_DROPDOWN, options=explore_dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -548,7 +549,7 @@ def section_three_inputs(): dropdown( id=ElementIds.TAB6_SEC3_FILTER_VAR_DROPDOWN, options=explore_dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -946,11 +947,11 @@ def update_table( ) filtered_df = df[ - (df["month"] >= start_month) - & (df["month"] <= end_month) - & (df["hour"] >= start_hour) - & (df["hour"] <= end_hour) + (df[ColNames.MONTH] >= start_month) + & (df[ColNames.MONTH] <= end_month) + & (df[ColNames.HOUR] >= start_hour) + & (df[ColNames.HOUR] <= end_hour) ] return summary_table_tmp_rh_tab( - filtered_df[["month", "hour", dd_value, "month_names"]], dd_value, si_ip + filtered_df[[ColNames.MONTH, ColNames.HOUR, dd_value, ColNames.MONTH_NAMES]], dd_value, si_ip ) diff --git a/pages/lib/charts_sun.py b/pages/lib/charts_sun.py index 103e11b4..805fd717 100644 --- a/pages/lib/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -113,7 +113,7 @@ def polar_graph(df, meta, global_local, var, si_ip): latitude = float(meta["lat"]) longitude = float(meta["lon"]) time_zone = float(meta["time_zone"]) - solpos = df.loc[df["apparent_elevation"] > 0, :] + solpos = df.loc[df[ColNames.APPARENT_ELEVATION] > 0, :] if var != "None": var_unit = mapping_dictionary[var][si_ip]["unit"] @@ -135,7 +135,7 @@ def polar_graph(df, meta, global_local, var, si_ip): ) delta = timedelta(days=0, hours=time_zone - 1, minutes=0) times = times - delta - solpos = df.loc[df["apparent_elevation"] > 0, :] + solpos = df.loc[df[ColNames.APPARENT_ELEVATION] > 0, :] if var == "None": var_color = "orange" @@ -167,7 +167,7 @@ def polar_graph(df, meta, global_local, var, si_ip): fig.add_trace( go.Scatterpolar( r=90 * np.cos(np.radians(90 - solpos["apparent_zenith"])), - theta=solpos["azimuth"], + theta=solpos[ColNames.AZIMUTH], mode="markers", marker_color="orange", marker_size=marker_size, @@ -177,8 +177,8 @@ def polar_graph(df, meta, global_local, var, si_ip): solpos[ColNames.DAY], solpos[ColNames.MONTH_NAMES], solpos[ColNames.HOUR], - solpos["elevation"], - solpos["azimuth"], + solpos[ColNames.ELEVATION], + solpos[ColNames.AZIMUTH], ), axis=-1, ), @@ -197,7 +197,7 @@ def polar_graph(df, meta, global_local, var, si_ip): fig.add_trace( go.Scatterpolar( r=90 * np.cos(np.radians(90 - solpos["apparent_zenith"])), - theta=solpos["azimuth"], + theta=solpos[ColNames.AZIMUTH], mode="markers", marker=dict( color=solpos[var], @@ -213,8 +213,8 @@ def polar_graph(df, meta, global_local, var, si_ip): solpos[ColNames.DAY], solpos[ColNames.MONTH_NAMES], solpos[ColNames.HOUR], - solpos["elevation"], - solpos["azimuth"], + solpos[ColNames.ELEVATION], + solpos[ColNames.AZIMUTH], solpos[var], ), axis=-1, @@ -241,7 +241,7 @@ def polar_graph(df, meta, global_local, var, si_ip): times = pd.date_range(date, date + pd.Timedelta("24h"), freq="5min", tz=tz) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) - solpos = solpos.loc[solpos["apparent_elevation"] > 0, :] + solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] fig.add_trace( go.Scatterpolar( @@ -265,7 +265,7 @@ def polar_graph(df, meta, global_local, var, si_ip): times = pd.date_range(date, date + pd.Timedelta("24h"), freq="5min", tz=tz) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) - solpos = solpos.loc[solpos["apparent_elevation"] > 0, :] + solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] fig.add_trace( go.Scatterpolar( @@ -345,8 +345,8 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): if var == "None": fig.add_trace( go.Scatter( - y=df["elevation"], - x=df["azimuth"], + y=df[ColNames.ELEVATION], + x=df[ColNames.AZIMUTH], mode="markers", marker_color="orange", marker_size=marker_size, @@ -356,8 +356,8 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): df[ColNames.DAY], df[ColNames.MONTH_NAMES], df[ColNames.HOUR], - df["elevation"], - df["azimuth"], + df[ColNames.ELEVATION], + df[ColNames.AZIMUTH], ), axis=-1, ), @@ -375,8 +375,8 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): else: fig.add_trace( go.Scatter( - y=df["elevation"], - x=df["azimuth"], + y=df[ColNames.ELEVATION], + x=df[ColNames.AZIMUTH], mode="markers", marker=dict( color=df[var], @@ -392,8 +392,8 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): df[ColNames.DAY], df[ColNames.MONTH_NAMES], df[ColNames.HOUR], - df["elevation"], - df["azimuth"], + df[ColNames.ELEVATION], + df[ColNames.AZIMUTH], df[var], ), axis=-1, @@ -421,7 +421,7 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): delta = timedelta(days=0, hours=time_zone - 1, minutes=0) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) - solpos = solpos.loc[solpos["apparent_elevation"] > 0, :] + solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] fig.add_trace( go.Scatter( @@ -445,7 +445,7 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): delta = timedelta(days=0, hours=time_zone - 1, minutes=0) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) - solpos = solpos.loc[solpos["apparent_elevation"] > 0, :] + solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] fig.add_trace( go.Scatter( diff --git a/pages/lib/extract_df.py b/pages/lib/extract_df.py index 087c67cf..ce2df0fa 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -227,9 +227,9 @@ def create_df(lst, file_name): epw_df = pd.concat([epw_df, solar_position], axis=1) # Add in UTCI - sol_altitude = epw_df["elevation"].mask(epw_df["elevation"] <= 0, 0) + sol_altitude = epw_df[ColNames.ELEVATION].mask(epw_df[ColNames.ELEVATION] <= 0, 0) sharp = [45] * 8760 - sol_radiation_dir = epw_df["dir_nor_rad"] + sol_radiation_dir = epw_df[ColNames.DIR_NOR_RAD] sol_transmittance = [1] * 8760 # CHECK VALUE f_svv = [1] * 8760 # CHECK VALUE f_bes = [1] * 8760 # CHECK VALUE @@ -249,48 +249,48 @@ def create_df(lst, file_name): floor_reflectance, ) mrt_df = pd.DataFrame.from_records(mrt) - mrt_df["delta_mrt"] = mrt_df["delta_mrt"].mask(mrt_df["delta_mrt"] >= 70, 70) + mrt_df[ColNames.DELTA_MRT] = mrt_df[ColNames.DELTA_MRT].mask(mrt_df[ColNames.DELTA_MRT] >= 70, 70) mrt_df = mrt_df.set_index(epw_df.times) epw_df = epw_df.join(mrt_df) - epw_df["MRT"] = epw_df["delta_mrt"] + epw_df[ColNames.DBT] - epw_df["wind_speed_utci"] = epw_df[ColNames.WIND_SPEED] - epw_df["wind_speed_utci"] = epw_df["wind_speed_utci"].mask( - epw_df["wind_speed_utci"] >= 17, 16.9 + epw_df[ColNames.MRT] = epw_df[ColNames.DELTA_MRT] + epw_df[ColNames.DBT] + epw_df[ColNames.WIND_SPEED_UTCI] = epw_df[ColNames.WIND_SPEED] + epw_df[ColNames.WIND_SPEED_UTCI] = epw_df[ColNames.WIND_SPEED_UTCI].mask( + epw_df[ColNames.WIND_SPEED_UTCI] >= 17, 16.9 ) - epw_df["wind_speed_utci"] = epw_df["wind_speed_utci"].mask( - epw_df["wind_speed_utci"] <= 0.5, 0.6 + epw_df[ColNames.WIND_SPEED_UTCI] = epw_df[ColNames.WIND_SPEED_UTCI].mask( + epw_df[ColNames.WIND_SPEED_UTCI] <= 0.5, 0.6 ) - epw_df["wind_speed_utci_0"] = epw_df["wind_speed_utci"].mask( - epw_df["wind_speed_utci"] >= 0, 0.5 + epw_df[ColNames.WIND_SPEED_UTCI_0] = epw_df[ColNames.WIND_SPEED_UTCI].mask( + epw_df[ColNames.WIND_SPEED_UTCI] >= 0, 0.5 ) - epw_df["utci_noSun_Wind"] = utci( - epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df["wind_speed_utci"], epw_df[ColNames.RH] + epw_df[ColNames.UTCI_NO_SUN_WIND] = utci( + epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df[ColNames.WIND_SPEED_UTCI], epw_df[ColNames.RH] ) - epw_df["utci_noSun_noWind"] = utci( - epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df["wind_speed_utci_0"], epw_df[ColNames.RH] + epw_df[ColNames.UTCI_NO_SUN_NO_WIND] = utci( + epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df[ColNames.WIND_SPEED_UTCI_0], epw_df[ColNames.RH] ) - epw_df["utci_Sun_Wind"] = utci( - epw_df[ColNames.DBT], epw_df["MRT"], epw_df["wind_speed_utci"], epw_df[ColNames.RH] + epw_df[ColNames.UTCI_SUN_WIND] = utci( + epw_df[ColNames.DBT], epw_df[ColNames.MRT], epw_df[ColNames.WIND_SPEED_UTCI], epw_df[ColNames.RH] ) - epw_df["utci_Sun_noWind"] = utci( - epw_df[ColNames.DBT], epw_df["MRT"], epw_df["wind_speed_utci_0"], epw_df[ColNames.RH] + epw_df[ColNames.UTCI_SUN_NO_WIND] = utci( + epw_df[ColNames.DBT], epw_df[ColNames.MRT], epw_df[ColNames.WIND_SPEED_UTCI_0], epw_df[ColNames.RH] ) utci_bins = [-999, -40, -27, -13, 0, 9, 26, 32, 38, 46, 999] utci_labels = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] epw_df["utci_noSun_Wind_categories"] = pd.cut( - x=epw_df["utci_noSun_Wind"], bins=utci_bins, labels=utci_labels + x=epw_df[ColNames.UTCI_NO_SUN_WIND], bins=utci_bins, labels=utci_labels ) epw_df["utci_noSun_noWind_categories"] = pd.cut( - x=epw_df["utci_noSun_noWind"], bins=utci_bins, labels=utci_labels + x=epw_df[ColNames.UTCI_NO_SUN_NO_WIND], bins=utci_bins, labels=utci_labels ) epw_df["utci_Sun_Wind_categories"] = pd.cut( - x=epw_df["utci_Sun_Wind"], bins=utci_bins, labels=utci_labels + x=epw_df[ColNames.UTCI_SUN_WIND], bins=utci_bins, labels=utci_labels ) epw_df["utci_Sun_noWind_categories"] = pd.cut( - x=epw_df["utci_Sun_noWind"], bins=utci_bins, labels=utci_labels + x=epw_df[ColNames.UTCI_SUN_NO_WIND], bins=utci_bins, labels=utci_labels ) # Add psy values @@ -302,12 +302,12 @@ def create_df(lst, file_name): # calculate adaptive data dbt_day_ave = epw_df.groupby([ColNames.DOY])[ColNames.DBT].mean().to_list() n = 7 - epw_df["adaptive_comfort"] = np.nan - epw_df["adaptive_cmf_80_low"] = np.nan - epw_df["adaptive_cmf_80_up"] = np.nan - epw_df["adaptive_cmf_90_low"] = np.nan - epw_df["adaptive_cmf_90_up"] = np.nan - epw_df["adaptive_cmf_rmt"] = np.nan + epw_df[ColNames.ADAPTIVE_COMFORT] = np.nan + epw_df[ColNames.ADAPTIVE_CMF_80_LOW] = np.nan + epw_df[ColNames.ADAPTIVE_CMF_80_UP] = np.nan + epw_df[ColNames.ADAPTIVE_CMF_90_LOW] = np.nan + epw_df[ColNames.ADAPTIVE_CMF_90_UP] = np.nan + epw_df[ColNames.ADAPTIVE_CMF_RMT] = np.nan for day in epw_df.DOY.unique(): i = day - 1 if i < n: @@ -328,12 +328,12 @@ def create_df(lst, file_name): v=0.5, limit_inputs=False, ) - epw_df.loc[epw_df.DOY == day, "adaptive_cmf_rmt"] = rmt - epw_df.loc[epw_df.DOY == day, "adaptive_comfort"] = r["tmp_cmf"] - epw_df.loc[epw_df.DOY == day, "adaptive_cmf_80_low"] = r["tmp_cmf_80_low"] - epw_df.loc[epw_df.DOY == day, "adaptive_cmf_80_up"] = r["tmp_cmf_80_up"] - epw_df.loc[epw_df.DOY == day, "adaptive_cmf_90_low"] = r["tmp_cmf_90_low"] - epw_df.loc[epw_df.DOY == day, "adaptive_cmf_90_up"] = r["tmp_cmf_90_up"] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_RMT] = rmt + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_COMFORT] = r["tmp_cmf"] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_LOW] = r["tmp_cmf_80_low"] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_UP] = r["tmp_cmf_80_up"] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_LOW] = r["tmp_cmf_90_low"] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_UP] = r["tmp_cmf_90_up"] return epw_df, location_info @@ -372,11 +372,11 @@ def enthalpy(df, name): def convert_data(df, mapping_json): - df["adaptive_comfort"] = df["adaptive_comfort"] * 1.8 + 32 - df["adaptive_cmf_80_low"] = df["adaptive_cmf_80_low"] * 1.8 + 32 - df["adaptive_cmf_80_up"] = df["adaptive_cmf_80_up"] * 1.8 + 32 - df["adaptive_cmf_90_low"] = df["adaptive_cmf_90_low"] * 1.8 + 32 - df["adaptive_cmf_90_up"] = df["adaptive_cmf_90_up"] * 1.8 + 32 + df[ColNames.ADAPTIVE_COMFORT] = df[ColNames.ADAPTIVE_COMFORT] * 1.8 + 32 + df[ColNames.ADAPTIVE_CMF_80_LOW] = df[ColNames.ADAPTIVE_CMF_80_LOW] * 1.8 + 32 + df[ColNames.ADAPTIVE_CMF_80_UP] = df[ColNames.ADAPTIVE_CMF_80_UP] * 1.8 + 32 + df[ColNames.ADAPTIVE_CMF_90_LOW] = df[ColNames.ADAPTIVE_CMF_90_LOW] * 1.8 + 32 + df[ColNames.ADAPTIVE_CMF_90_UP] = df[ColNames.ADAPTIVE_CMF_90_UP] * 1.8 + 32 mapping_dict = json.loads(mapping_json) for key in json.loads(mapping_json): diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index a5b214fa..ae840591 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -187,13 +187,13 @@ def yearly_profile(df, var, global_local, si_ip): # plot relative Humidity limits (30-70%) lo_rh = [30] * 365 hi_rh = [70] * 365 - lo_rh_df = pd.DataFrame({"loRH": lo_rh}) - hi_rh_df = pd.DataFrame({"hiRH": hi_rh}) + lo_rh_df = pd.DataFrame({ColNames.LO_RH: lo_rh}) + hi_rh_df = pd.DataFrame({ColNames.HI_RH: hi_rh}) trace3 = go.Bar( x=df[ColNames.UTC_TIME].dt.date.unique(), - y=hi_rh_df["hiRH"] - lo_rh_df["loRH"], - base=lo_rh_df["loRH"], + y=hi_rh_df[ColNames.HI_RH] - lo_rh_df[ColNames.LO_RH], + base=lo_rh_df[ColNames.LO_RH], name="humidity comfort band", marker_opacity=0.3, marker_color="silver", diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index c772c0b4..8ee2fcf4 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -511,7 +511,7 @@ def nv_bar_chart( color_in = "dodgerblue" - df["nv_allowed"] = 1 + df[ColNames.NV_ALLOWED] = 1 df = filter_df_by_month_and_hour( df, time_filter, month, hour, invert_month, invert_hour, "nv_allowed" @@ -519,18 +519,18 @@ def nv_bar_chart( # this should be the total after filtering by time tot_month_hours = ( - df.groupby(df[ColNames.UTC_TIME].dt.month)["nv_allowed"].sum().values + df.groupby(df[ColNames.UTC_TIME].dt.month)[ColNames.NV_ALLOWED].sum().values ) if dbt_data_filter and (min_dbt_val <= max_dbt_val): - df.loc[(df[var] < min_dbt_val) | (df[var] > max_dbt_val), "nv_allowed"] = 0 + df.loc[(df[var] < min_dbt_val) | (df[var] > max_dbt_val), ColNames.NV_ALLOWED] = 0 if dpt_data_filter: - df.loc[(df[filter_var] > max_dpt_val), "nv_allowed"] = 0 + df.loc[(df[filter_var] > max_dpt_val), ColNames.NV_ALLOWED] = 0 n_hours_nv_allowed = ( - df.dropna(subset="nv_allowed") - .groupby(df[ColNames.UTC_TIME].dt.month)["nv_allowed"] + df.dropna(subset=ColNames.NV_ALLOWED) + .groupby(df[ColNames.UTC_TIME].dt.month)[ColNames.NV_ALLOWED] .sum() .values ) diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 51bac4cc..8d22822a 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -13,6 +13,7 @@ from config import PageUrls, DocLinks, PageInfo, UnitSystem from pages.lib.global_elementids import ElementIds +from pages.lib.global_column_names import ColNames from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, @@ -174,7 +175,7 @@ def inputs(): dropdown( id=ElementIds.PSY_VAR_DROPDOWN , options=dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -313,17 +314,17 @@ def update_psych_chart( if global_local == "global": # Set Global values for Max and minimum - var_range_x = mapping_dictionary["DBT"][si_ip]["range"] - var_range_y = mapping_dictionary["hr"][si_ip]["range"] + var_range_x = mapping_dictionary[ColNames.DBT][si_ip]["range"] + var_range_y = mapping_dictionary[ColNames.HR][si_ip]["range"] else: # Set maximum and minimum according to data - data_max = 5 * ceil(df["DBT"].max() / 5) - data_min = 5 * floor(df["DBT"].min() / 5) + data_max = 5 * ceil(df[ColNames.DBT].max() / 5) + data_min = 5 * floor(df[ColNames.DBT].min() / 5) var_range_x = [data_min, data_max] - data_max = round(df["hr"].max(), 4) - data_min = round(df["hr"].min(), 4) + data_max = round(df[ColNames.HR].max(), 4) + data_min = round(df[ColNames.HR].min(), 4) var_range_y = [data_min * 1000, data_max * 1000] title = "Psychrometric Chart" @@ -339,7 +340,7 @@ def update_psych_chart( hr_list = np.vectorize(psy.psy_ta_rh)(dbt_list, rh) hr_df = pd.DataFrame.from_records(hr_list) name = "rh" + str(rh) - rh_df[name] = hr_df["hr"] + rh_df[name] = hr_df[ColNames.HR] fig = go.Figure() @@ -369,13 +370,13 @@ def update_psych_chart( ) ) - df_hr_multiply = list(df["hr"]) + df_hr_multiply = list(df[ColNames.HR]) for k in range(len(df_hr_multiply)): df_hr_multiply[k] = df_hr_multiply[k] * 1000 if var == "None": fig.add_trace( go.Scatter( - x=df["DBT"], + x=df[ColNames.DBT], y=df_hr_multiply, showlegend=False, mode="markers", @@ -385,16 +386,16 @@ def update_psych_chart( showscale=False, opacity=0.2, ), - hovertemplate=mapping_dictionary["DBT"]["name"] + hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + ": %{x:.2f}" - + mapping_dictionary["DBT"]["name"], + + mapping_dictionary[ColNames.DBT]["name"], name="", ) ) elif var == "Frequency": fig.add_trace( go.Histogram2d( - x=df["DBT"], + x=df[ColNames.DBT], y=df_hr_multiply, name="", colorscale=var_color, @@ -438,7 +439,7 @@ def update_psych_chart( fig.add_trace( go.Scatter( - x=df["DBT"], + x=df[ColNames.DBT], y=df_hr_multiply, showlegend=False, mode="markers", @@ -450,14 +451,14 @@ def update_psych_chart( colorscale=var_color, colorbar=var_colorbar, ), - customdata=np.stack((df["RH"], df["h"], df[var], df["t_dp"]), axis=-1), - hovertemplate=mapping_dictionary["DBT"]["name"] + customdata=np.stack((df[ColNames.RH], df["h"], df[var], df["t_dp"]), axis=-1), + hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + ": %{x:.2f}" - + mapping_dictionary["DBT"][si_ip]["unit"] + + mapping_dictionary[ColNames.DBT][si_ip]["unit"] + "
" - + mapping_dictionary["RH"]["name"] + + mapping_dictionary[ColNames.RH]["name"] + ": %{customdata[0]:.2f}" - + mapping_dictionary["RH"][si_ip]["unit"] + + mapping_dictionary[ColNames.RH][si_ip]["unit"] + "
" + mapping_dictionary["h"]["name"] + ": %{customdata[1]:.2f}" @@ -475,8 +476,8 @@ def update_psych_chart( ) ) - xtitle_name = "Temperature" + " " + mapping_dictionary["DBT"][si_ip]["unit"] - ytitle_name = "Humidity Ratio" + " " + mapping_dictionary["hr"][si_ip]["unit"] + xtitle_name = "Temperature" + " " + mapping_dictionary[ColNames.DBT][si_ip]["unit"] + ytitle_name = "Humidity Ratio" + " " + mapping_dictionary[ColNames.HR][si_ip]["unit"] fig.update_layout(template=template, margin=tight_margins) fig.update_xaxes( title_text=xtitle_name, diff --git a/pages/summary.py b/pages/summary.py index 6df0112a..e5509eee 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -259,13 +259,13 @@ def update_location_info(ts, df, meta, si_ip): # global horizontal irradiance # Note that the value is divided by 1000, so a corresponding change is made in the unit: - total_solar_rad_value = round(df["glob_hor_rad"].sum() / 1000, 2) - total_solar_rad_unit = "k" + mapping_dictionary["glob_hor_rad"][si_ip]["unit"] + total_solar_rad_value = round(df[ColNames.GLOB_HOR_RAD].sum() / 1000, 2) + total_solar_rad_unit = "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip]["unit"] total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" - glob_sum = df["glob_hor_rad"].sum() + glob_sum = df[ColNames.GLOB_HOR_RAD].sum() if glob_sum > 0: - diffuse_percentage = round(df["dif_hor_rad"].sum() / glob_sum * 100, 1) + diffuse_percentage = round(df[ColNames.DIF_HOR_RAD].sum() / glob_sum * 100, 1) else: diffuse_percentage = 0 total_diffuse_rad = ( From e85bfe1cdcdbd5cb33321c918b348c872305f9ea Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sun, 24 Aug 2025 22:12:05 +1000 Subject: [PATCH 34/66] fix: renamed the python file of global_element_ids.py --- pages/explorer.py | 2 +- pages/lib/{global_elementids.py => global_element_ids.py} | 0 pages/natural_ventilation.py | 2 +- pages/outdoor.py | 2 +- pages/psy-chart.py | 2 +- pages/select.py | 2 +- pages/summary.py | 2 +- pages/sun.py | 2 +- pages/t_rh.py | 2 +- pages/wind.py | 2 +- 10 files changed, 9 insertions(+), 9 deletions(-) rename pages/lib/{global_elementids.py => global_element_ids.py} (100%) diff --git a/pages/explorer.py b/pages/explorer.py index 3e079ebc..32ae86db 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -12,7 +12,7 @@ two_var_graph, three_var_graph, ) -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames from pages.lib.global_scheme import ( fig_config, diff --git a/pages/lib/global_elementids.py b/pages/lib/global_element_ids.py similarity index 100% rename from pages/lib/global_elementids.py rename to pages/lib/global_element_ids.py diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 8ee2fcf4..091d8fa4 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -20,7 +20,7 @@ ) from pages.lib.template_graphs import filter_df_by_month_and_hour from pages.lib.global_column_names import ColNames -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from pages.lib.utils import ( title_with_tooltip, generate_chart_name, diff --git a/pages/outdoor.py b/pages/outdoor.py index b9fbbe5a..33d1fd5c 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -6,7 +6,7 @@ import numpy as np from config import PageUrls, DocLinks, PageInfo -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from pages.lib.global_scheme import ( outdoor_dropdown_names, ) diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 8d22822a..7fbd802d 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -12,7 +12,7 @@ from pythermalcomfort import psychrometrics as psy from config import PageUrls, DocLinks, PageInfo, UnitSystem -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames from pages.lib.global_scheme import ( container_row_center_full, diff --git a/pages/select.py b/pages/select.py index ad1a3a32..89fd67f0 100644 --- a/pages/select.py +++ b/pages/select.py @@ -13,7 +13,7 @@ from pages.lib.extract_df import convert_data from pages.lib.extract_df import create_df, get_data, get_location_info -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from pages.lib.global_scheme import mapping_dictionary from config import PageUrls, PageInfo, UnitSystem from pages.lib.utils import generate_chart_name diff --git a/pages/summary.py b/pages/summary.py index e5509eee..cce46c88 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -12,7 +12,7 @@ from pages.lib.global_scheme import template, tight_margins, mapping_dictionary from pages.lib.template_graphs import violin from pages.lib.global_column_names import ColNames -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from pages.lib.utils import ( generate_chart_name, generate_units, diff --git a/pages/sun.py b/pages/sun.py index 74e50e8f..b6663e76 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -1,5 +1,5 @@ from copy import deepcopy -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds import dash import dash_bootstrap_components as dbc diff --git a/pages/t_rh.py b/pages/t_rh.py index 47341cba..12d89fba 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -4,7 +4,7 @@ from pages.lib.global_scheme import dropdown_names from pages.lib.template_graphs import heatmap, yearly_profile, daily_profile from pages.lib.global_column_names import ColNames -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from pages.lib.utils import ( generate_chart_name, generate_units, diff --git a/pages/wind.py b/pages/wind.py index 9ef474ae..8517f510 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -1,7 +1,7 @@ import dash from dash import dcc, html from dash_extensions.enrich import Output, Input, State, callback -from pages.lib.global_elementids import ElementIds +from pages.lib.global_element_ids import ElementIds from config import PageUrls, DocLinks, PageInfo from pages.lib.global_scheme import month_lst, container_row_center_full From 2dd992c646ad99cad9bdeda9c9aba82609a7c260 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sun, 24 Aug 2025 23:50:58 +1000 Subject: [PATCH 35/66] fix: resolved the format to avoid conflict --- .github/workflows/deploy.yml | 4 ++-- Pipfile.lock | 2 +- pages/outdoor.py | 18 +++++++++--------- pages/summary.py | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bc24325c..5763701d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,5 +27,5 @@ jobs: - name: Building (🏗️) and Deploying (🚀) run: | gcloud builds submit \ - --project=clima-316917 \ - --substitutions=_REPO_NAME="clima",_PROJ_NAME="clima-316917",_IMG_NAME="main",_GCR="us.gcr.io",_REGION="us-central1",_MEMORY="4Gi",_CPU="2" \ No newline at end of file + --project=clima-316917 \ + --substitutions=_REPO_NAME="clima",_PROJ_NAME="clima-316917",_IMG_NAME="main",_GCR="us.gcr.io",_REGION="us-central1",_MEMORY="4Gi",_CPU="2" \ No newline at end of file diff --git a/Pipfile.lock b/Pipfile.lock index 89f3c053..ef31819c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -947,7 +947,7 @@ ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.12.10" + "version": "==0.12.9" }, "virtualenv": { "hashes": [ diff --git a/pages/outdoor.py b/pages/outdoor.py index 33d1fd5c..0e8d4cef 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -223,17 +223,17 @@ def layout(): ) def update_outdoor_comfort_output(_, df): """ - Find the column(s) with the highest number of zero values. + Find the column(s) with the highest number of zero values. - Args: - _: Unused callback input. - df: DataFrame-like object containing UTCI category columns. + Args: + _: Unused callback input. + df: DataFrame-like object containing UTCI category columns. - Returns - ------- - str - Description of the best weather condition(s). - """ + Returns + ------- + str + Description of the best weather condition(s). + """ cols = [ "utci_noSun_Wind_categories", "utci_noSun_noWind_categories", diff --git a/pages/summary.py b/pages/summary.py index cce46c88..396f926d 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -273,14 +273,14 @@ def update_location_info(ts, df, meta, si_ip): ) tmp_unit = mapping_dictionary[ColNames.DBT][si_ip]["unit"] average_yearly_tmp = ( - f"Average yearly temperature: {df['DBT'].mean().round(1)} " + tmp_unit + f"Average yearly temperature: {df[ColNames.DBT].mean().round(1)} " + tmp_unit ) hottest_yearly_tmp = ( - f"Hottest yearly temperature (99%): {df['DBT'].quantile(0.99).round(1)} " + f"Hottest yearly temperature (99%): {df[ColNames.DBT].quantile(0.99).round(1)} " + tmp_unit ) coldest_yearly_tmp = ( - f"Coldest yearly temperature (1%): {df['DBT'].quantile(0.01).round(1)} " + f"Coldest yearly temperature (1%): {df[ColNames.DBT].quantile(0.01).round(1)} " + tmp_unit ) From 2381e85b1e3a7f4fe9317dc1f4aa7a4d26f466ce Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Mon, 25 Aug 2025 00:21:33 +1000 Subject: [PATCH 36/66] Fix: updated the file global_element_ids.py and summary.py --- pages/lib/global_element_ids.py | 4 +++- pages/summary.py | 27 ++++++++++----------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/pages/lib/global_element_ids.py b/pages/lib/global_element_ids.py index 6fcce955..d36ff4f7 100644 --- a/pages/lib/global_element_ids.py +++ b/pages/lib/global_element_ids.py @@ -172,7 +172,7 @@ class ElementIds(str, Enum): ID_SELECT_SI_IP_UNIT_STORE = "si-ip-unit-store" ID_SELECT_SI_IP_RADIO_INPUT = "si-ip-radio-input" BANNER_SUBTITLE = "banner-subtitle" - TABLE_TWO_CONTAINER = "tab-two-container" + TAB_TWO_CONTAINER = "tab-two-container" ID_SUMMARY_SI_IP_RADIO_INPUT = "si-ip-radio-input" TAB2_SCE1_CONTAINER = "tab2-sec1-container" LOCATION_INFO = "location-info" @@ -187,6 +187,7 @@ class ElementIds(str, Enum): SUBMIT_SET_POINTS = "submit-set-points" GRAPH_CONTAINER = "graph-container" DEGREE_DAYS_CHART_WRAPPER = "degree-days-chart-wrapper" + DEGREE_DAYS_CHART = "degree-days-chart" TEMP_PROFILE_GRAPH = "temp-profile-graph" HUMIDITY_PROFILE_GRAPH = "humidity-profile-graph" SOLAR_RADIATION_GRAPH = "solar-radiation-graph" @@ -196,5 +197,6 @@ class ElementIds(str, Enum): ID_SUMMARY_DF_STORE = "df-store" ID_SUMMARY_SI_IP_UNIT_STORE = "si-ip-unit-store" ID_SUMMARY_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + WIND_PROFILE_GRAPH = "wind-profile-graph" TDB_PROFILE_GRAPH = "tdb-profile-graph" RH_PROFILE_GRAPH = "rh-profile-graph" diff --git a/pages/summary.py b/pages/summary.py index 396f926d..07235bad 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -35,7 +35,7 @@ def layout(): return html.Div( className="container-col", - id="tab-two-container", + id=ElementIds.TAB_TWO_CONTAINER, children=[ # ], @@ -43,7 +43,7 @@ def layout(): @callback( - Output(ElementIds.TABLE_TWO_CONTAINER, "children"), [Input(ElementIds.ID_SUMMARY_SI_IP_RADIO_INPUT, "value")] + Output(ElementIds.TAB_TWO_CONTAINER, "children"), [Input(ElementIds.ID_SUMMARY_SI_IP_RADIO_INPUT, "value")] ) def update_layout(si_ip): if si_ip == UnitSystem.SI: @@ -259,28 +259,21 @@ def update_location_info(ts, df, meta, si_ip): # global horizontal irradiance # Note that the value is divided by 1000, so a corresponding change is made in the unit: - total_solar_rad_value = round(df[ColNames.GLOB_HOR_RAD].sum() / 1000, 2) - total_solar_rad_unit = "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip]["unit"] + total_solar_rad_value = round(df["glob_hor_rad"].sum() / 1000, 2) + total_solar_rad_unit = "k" + mapping_dictionary["glob_hor_rad"][si_ip]["unit"] total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" - glob_sum = df[ColNames.GLOB_HOR_RAD].sum() - if glob_sum > 0: - diffuse_percentage = round(df[ColNames.DIF_HOR_RAD].sum() / glob_sum * 100, 1) - else: - diffuse_percentage = 0 - total_diffuse_rad = ( - f"Percentage of diffuse horizontal solar radiation: {diffuse_percentage} %" - ) + total_diffuse_rad = f"Percentage of diffuse horizontal solar radiation: {round(df['dif_hor_rad'].sum() / df['glob_hor_rad'].sum() * 100, 1)} %" tmp_unit = mapping_dictionary[ColNames.DBT][si_ip]["unit"] average_yearly_tmp = ( - f"Average yearly temperature: {df[ColNames.DBT].mean().round(1)} " + tmp_unit + f"Average yearly temperature: {df['DBT'].mean().round(1)} " + tmp_unit ) hottest_yearly_tmp = ( - f"Hottest yearly temperature (99%): {df[ColNames.DBT].quantile(0.99).round(1)} " + f"Hottest yearly temperature (99%): {df['DBT'].quantile(0.99).round(1)} " + tmp_unit ) coldest_yearly_tmp = ( - f"Coldest yearly temperature (1%): {df[ColNames.DBT].quantile(0.01).round(1)} " + f"Coldest yearly temperature (1%): {df['DBT'].quantile(0.01).round(1)} " + tmp_unit ) @@ -412,7 +405,7 @@ def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ units = generate_units_degree(si_ip) chart = dcc.Graph( - id="degree-days-chart", + id=ElementIds.DEGREE_DAYS_CHART, config=generate_chart_name("hdd_cdd", meta, custom_inputs, units), figure=fig, ) @@ -458,7 +451,7 @@ def update_tab_wind(ts, global_local, df, meta, si_ip): """Update the contents of tab two. Passing in the general info (df, meta).""" units = generate_units(si_ip) return dcc.Graph( - id="wind-profile-graph", + id=ElementIds.WIND_PROFILE_GRAPH, className="violin-container", config=generate_chart_name("WindSpeed", meta, units), figure=violin(df, ColNames.WIND_SPEED, global_local, si_ip), From f822ccacb5b92dd86c3f43d05cfffdb3a6d09b54 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Mon, 25 Aug 2025 10:46:08 +1000 Subject: [PATCH 37/66] fix: replaced the string with global constants --- pages/summary.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/summary.py b/pages/summary.py index 07235bad..2230d3a8 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -259,21 +259,21 @@ def update_location_info(ts, df, meta, si_ip): # global horizontal irradiance # Note that the value is divided by 1000, so a corresponding change is made in the unit: - total_solar_rad_value = round(df["glob_hor_rad"].sum() / 1000, 2) - total_solar_rad_unit = "k" + mapping_dictionary["glob_hor_rad"][si_ip]["unit"] + total_solar_rad_value = round(df[ColNames.GLOB_HOR_RAD].sum() / 1000, 2) + total_solar_rad_unit = "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip]["unit"] total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" - total_diffuse_rad = f"Percentage of diffuse horizontal solar radiation: {round(df['dif_hor_rad'].sum() / df['glob_hor_rad'].sum() * 100, 1)} %" + total_diffuse_rad = f"Percentage of diffuse horizontal solar radiation: {round(df[ColNames.DIF_HOR_RAD].sum() / df[ColNames.GLOB_HOR_RAD].sum() * 100, 1)} %" tmp_unit = mapping_dictionary[ColNames.DBT][si_ip]["unit"] average_yearly_tmp = ( - f"Average yearly temperature: {df['DBT'].mean().round(1)} " + tmp_unit + f"Average yearly temperature: {df[ColNames.DBT].mean().round(1)} " + tmp_unit ) hottest_yearly_tmp = ( - f"Hottest yearly temperature (99%): {df['DBT'].quantile(0.99).round(1)} " + f"Hottest yearly temperature (99%): {df[ColNames.DBT].quantile(0.99).round(1)} " + tmp_unit ) coldest_yearly_tmp = ( - f"Coldest yearly temperature (1%): {df['DBT'].quantile(0.01).round(1)} " + f"Coldest yearly temperature (1%): {df[ColNames.DBT].quantile(0.01).round(1)} " + tmp_unit ) From 0aeeb25c16897820c1b048bcdc4adab2b228fe8c Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Mon, 25 Aug 2025 16:25:05 +1000 Subject: [PATCH 38/66] fix: resolved the conflict issues --- Pipfile.lock | 46 ++++++++----------- .../tab-summary/climate-profiles-explained.md | 4 +- pages/summary.py | 9 +++- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index ef31819c..15c208f9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -751,14 +751,6 @@ "markers": "python_version >= '3.10'", "version": "==8.2.1" }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==0.4.6" - }, "distlib": { "hashes": [ "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", @@ -925,25 +917,25 @@ }, "ruff": { "hashes": [ - "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", - "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", - "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", - "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", - "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", - "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", - "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", - "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", - "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", - "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", - "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", - "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", - "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", - "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", - "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", - "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", - "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", - "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", - "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60" + "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", + "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", + "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", + "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", + "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", + "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", + "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", + "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", + "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", + "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", + "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", + "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", + "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", + "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", + "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", + "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", + "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", + "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", + "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e" ], "index": "pypi", "markers": "python_version >= '3.7'", diff --git a/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md b/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md index dc45d8e4..51756f30 100644 --- a/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md +++ b/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md @@ -2,7 +2,9 @@ The Climate Profiles graph gives the user the opportunity to observe at a glance the distribution of the data in the EPW file for four key variables and their variation between day and night. -The Climate Profiles graph are [Violin Plots](https://en.wikipedia.org/wiki/Violin\_plot). They show the [probability density](https://en.wikipedia.org/wiki/Probability\_density\_function) of the data at different values, usually smoothed by a [kernel density estimator](https://en.wikipedia.org/wiki/Kernel\_density\_estimator). Wider sections of the violin plot represent a higher probability that members of the population will take on the given value; the skinnier sections represent a lower probability. +The Climate Profiles graph are [Violin Plots](https://en.wikipedia.org/wiki/Violin\_plot). +They show the [probability density](https://en.wikipedia.org/wiki/Probability\_density\_function) of the data at different values, usually smoothed by a [kernel density estimator](https://en.wikipedia.org/wiki/Kernel\_density\_estimator). +Wider sections of the violin plot represent a higher probability that members of the population will take on the given value; the skinnier sections represent a lower probability. On mouse hover, they display various statistical properties of the data: diff --git a/pages/summary.py b/pages/summary.py index 2230d3a8..199191f1 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -263,7 +263,14 @@ def update_location_info(ts, df, meta, si_ip): total_solar_rad_unit = "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip]["unit"] total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" - total_diffuse_rad = f"Percentage of diffuse horizontal solar radiation: {round(df[ColNames.DIF_HOR_RAD].sum() / df[ColNames.GLOB_HOR_RAD].sum() * 100, 1)} %" + glob_sum = df[ColNames.GLOB_HOR_RAD].sum() + if glob_sum > 0: + diffuse_percentage = round(df[ColNames.DIF_HOR_RAD].sum() / glob_sum * 100, 1) + else: + diffuse_percentage = 0 + total_diffuse_rad = ( + f"Percentage of diffuse horizontal solar radiation: {diffuse_percentage} %" + ) tmp_unit = mapping_dictionary[ColNames.DBT][si_ip]["unit"] average_yearly_tmp = ( f"Average yearly temperature: {df[ColNames.DBT].mean().round(1)} " + tmp_unit From 9aee692a27314adb0a9015ae18c2114ff3194821 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:17:20 +1000 Subject: [PATCH 39/66] fix: resolved the conflict issues for three md documentations --- .../tabs-explained/sun-and-cloud/README.md | 28 ++++++------------- .../customizable-daily-and-hourly-maps.md | 2 +- .../tabs-explained/tab-summary/README.md | 2 +- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/docs/documentation/tabs-explained/sun-and-cloud/README.md b/docs/documentation/tabs-explained/sun-and-cloud/README.md index c54f5571..96aab5c8 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/README.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/README.md @@ -1,28 +1,18 @@ -# Sun and Clouds +# Sun and Cloud -The **Sun and Clouds** tab presents an overview of various climatic factors that relate to sun, solar position, intensity, and cloud cover, in particular: +### Sun and Cloud -* [Apparent sunpath for the location (spherical and cartesian projection)](broken-reference/) +The Sun and Clouds tab presents an overview of various climatic factors that relate to sun, solar position, intensity nad cloud cover, in particular: + +* [Apparent sunpath for the location \(spherical and cartesian projection\)](apparent-sunpath-for-the-location/) * [Global and Diffuse Horizontal Solar Radiation](global-and-diffuse-horizontal-solar-radiation/) * [Cloud coverage](cloud-coverage.md) * [Customizable daily and hourly maps](customizable-daily-and-hourly-maps.md) -### Apparent sun path for the location - -**Clima** allows the user to visualize the sun path for the chosen location in spherical and cartesian projection - -![Example: spherical sun path for Berlin, DEU](../../../.gitbook/assets/cbeclima\_berlin\_deu\_spherical\_sun\_path\_sun\_tab.svg) - -![Example: cartesian sun path for Berlin, DEU](../../../.gitbook/assets/cbeclima\_berlin\_deu\_cartesian\_sun\_path\_sun\_tab.svg) - -Clima optionally allows a variety of variables to be overlayed on either sun path type. - -This allows the user to identify climatic patterns in relation to the apparent solar position. Data are plotted on the analemma. - -![Spherical and carthesian sun paths for Berlin, DEU with various data overlays](../../../.gitbook/assets/sunpath+variables.png) +### Apparent sunpath for the location -### Video Tutorial +**Clima** allows the user to visualize the sunpath for the chosen location in spherical and cartesian projection -Learn more about the Sun and Clouds tab by watching the following video. +![Example: spherical sun path for Berlin, DEU ](../../.gitbook/assets/cbeclima_berlin_deu_spherical_sun_path_sun_tab.svg) -{% embed url="https://youtu.be/VJ_wOHadVdw?si=mB2xNH57MWW_4CRR&t=447" %} \ No newline at end of file +![Example: cartesiansun path for Berlin, DEU ](../../.gitbook/assets/cbeclima_berlin_deu_cartesian_sun_path_sun_tab.svg) \ No newline at end of file diff --git a/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md b/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md index c1dfc256..e226ac93 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md @@ -10,4 +10,4 @@ The chart above shows the [scatter plot](https://en.wikipedia.org/wiki/Scatter\_ [Heat maps](https://en.wikipedia.org/wiki/Heat\_map) allow the intensity of values to be perceived through color palettes. These graphs are very helpful in seeing how magnitudes vary throughout the year. -

Examples of daily graphs with different variables (from top left to bottom right): global horizontal radiation, global horizontal illuminance, zenith luminance, opaque sky cover

\ No newline at end of file +

Examples of daily graphs with different variables (from top left to bottom right): global horizontal radiation, global horizontal illuminance, zenith luminance, opaque sky cover

diff --git a/docs/documentation/tabs-explained/tab-summary/README.md b/docs/documentation/tabs-explained/tab-summary/README.md index 4d474c10..8024c92e 100644 --- a/docs/documentation/tabs-explained/tab-summary/README.md +++ b/docs/documentation/tabs-explained/tab-summary/README.md @@ -13,4 +13,4 @@ The bottom section of the page comprises the heating and cooling degree day char Learn more about the Climate Summary tab by watching the following video. -{% embed url="https://youtu.be/VJ_wOHadVdw?si=H-93XRhh5Neuby_b&t=220" %} \ No newline at end of file +{% embed url="https://youtu.be/VJ_wOHadVdw?si=H-93XRhh5Neuby_b&t=220" %} From 5cc9259edf128e0d39a011db67bd72e4420996f2 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:27:00 +1000 Subject: [PATCH 40/66] fix: resolved the conflict issues with global-diffuse-and-normal-solar-radiation-explained.md and the README.md which in sun-and-cloud folder --- .../tabs-explained/sun-and-cloud/README.md | 28 +++++++++++++------ ...se-and-normal-solar-radiation-explained.md | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/documentation/tabs-explained/sun-and-cloud/README.md b/docs/documentation/tabs-explained/sun-and-cloud/README.md index 96aab5c8..29cbb1da 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/README.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/README.md @@ -1,18 +1,28 @@ -# Sun and Cloud +# Sun and Clouds -### Sun and Cloud +The **Sun and Clouds** tab presents an overview of various climatic factors that relate to sun, solar position, intensity, and cloud cover, in particular: -The Sun and Clouds tab presents an overview of various climatic factors that relate to sun, solar position, intensity nad cloud cover, in particular: - -* [Apparent sunpath for the location \(spherical and cartesian projection\)](apparent-sunpath-for-the-location/) +* [Apparent sunpath for the location (spherical and cartesian projection)](broken-reference/) * [Global and Diffuse Horizontal Solar Radiation](global-and-diffuse-horizontal-solar-radiation/) * [Cloud coverage](cloud-coverage.md) * [Customizable daily and hourly maps](customizable-daily-and-hourly-maps.md) -### Apparent sunpath for the location +### Apparent sun path for the location + +**Clima** allows the user to visualize the sun path for the chosen location in spherical and cartesian projection + +![Example: spherical sun path for Berlin, DEU](../../../.gitbook/assets/cbeclima\_berlin\_deu\_spherical\_sun\_path\_sun\_tab.svg) + +![Example: cartesian sun path for Berlin, DEU](../../../.gitbook/assets/cbeclima\_berlin\_deu\_cartesian\_sun\_path\_sun\_tab.svg) + +Clima optionally allows a variety of variables to be overlayed on either sun path type. + +This allows the user to identify climatic patterns in relation to the apparent solar position. Data are plotted on the analemma. + +![Spherical and carthesian sun paths for Berlin, DEU with various data overlays](../../../.gitbook/assets/sunpath+variables.png) -**Clima** allows the user to visualize the sunpath for the chosen location in spherical and cartesian projection +### Video Tutorial -![Example: spherical sun path for Berlin, DEU ](../../.gitbook/assets/cbeclima_berlin_deu_spherical_sun_path_sun_tab.svg) +Learn more about the Sun and Clouds tab by watching the following video. -![Example: cartesiansun path for Berlin, DEU ](../../.gitbook/assets/cbeclima_berlin_deu_cartesian_sun_path_sun_tab.svg) \ No newline at end of file +{% embed url="https://youtu.be/VJ_wOHadVdw?si=mB2xNH57MWW_4CRR&t=447" %} diff --git a/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/global-diffuse-and-normal-solar-radiation-explained.md b/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/global-diffuse-and-normal-solar-radiation-explained.md index f93f94de..fd87f855 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/global-diffuse-and-normal-solar-radiation-explained.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/global-diffuse-and-normal-solar-radiation-explained.md @@ -13,4 +13,4 @@ $$ ![Conceptual representation of Global Horizontal, Diffuse Horizontal and Direct Normal Solar Radiation](../../../../.gitbook/assets/picture3.png) -![Measurement of Direct Irradiation on a horizontal and a normal plane](../../../../.gitbook/assets/picture4.png) +![Measurement of Direct Irradiation on a horizontal and a normal plane](../../../../.gitbook/assets/picture4.png) \ No newline at end of file From 54ce1d88c1d02910bb5a586adfd788d8e9cc5e6a Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Mon, 25 Aug 2025 18:21:11 +1000 Subject: [PATCH 41/66] fix: formatted the code style via using Black in global_column_names.py --- pages/lib/global_column_names.py | 39 +++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index c8c627d0..972ad76e 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -13,8 +13,8 @@ class ColNames(str, Enum): DBT = "DBT" # Dry Bulb Temperature DPT = "DPT" # Dew Point Temperature RH = "RH" # Relative Humidity - HI_RH = "hiRH" # High Relative Humidity - LO_RH = "loRH" # Low Relative Humidity + HI_RH = "hiRH" # High Relative Humidity + LO_RH = "loRH" # Low Relative Humidity HR = "hr" P_ATM = "p_atm" # Atmospheric Pressure @@ -34,8 +34,8 @@ class ColNames(str, Enum): ZLUMI = "Zlumi" # Luminance WIND_DIR = "wind_dir" # Wind Direction WIND_SPEED = "wind_speed" # Wind Speed - WIND_SPEED_UTCI = "wind_speed_utci" # Wind Speed Utci - WIND_SPEED_UTCI_0 = "wind_speed_utci_0" # Wind Speed Utci 0 + WIND_SPEED_UTCI = "wind_speed_utci" # Wind Speed Utci + WIND_SPEED_UTCI_0 = "wind_speed_utci_0" # Wind Speed Utci 0 TOT_SKY_COVER = "tot_sky_cover" # Total Sky Cover OSKYCOVER = "Oskycover" # Opaque Sky Cover VIS = "Vis" # Visibility @@ -46,21 +46,21 @@ class ColNames(str, Enum): AsolOptD = "AsolOptD" # Aerosol Optical Depth SnowD = "SnowD" # Snow Depth DaySSnow = "DaySSnow" # Daily Snow - ELEVATION = "elevation" # Elevation - APPARENT_ELEVATION = "apparent_elevation" # Apparent Elevation + ELEVATION = "elevation" # Elevation + APPARENT_ELEVATION = "apparent_elevation" # Apparent Elevation AZIMUTH = "azimuth" # Azimuth MRT = "MRT" DELTA_MRT = "delta_mrt" - UTCI_SUN_WIND = "utci_Sun_Wind" # Utci Sun Wind - UTCI_SUN_NO_WIND = "utci_Sun_noWind" # Utci Sun no Wind - UTCI_NO_SUN_WIND = "utci_noSun_Wind" # Utci no Sun Wind - UTCI_NO_SUN_NO_WIND = "utci_noSun_noWind" # Utci no Sun no Wind - ADAPTIVE_COMFORT = "adaptive_comfort" # Adaptive comfort - ADAPTIVE_CMF_80_LOW = "adaptive_cmf_80_low" # Adaptive comfort 80 low - ADAPTIVE_CMF_80_UP = "adaptive_cmf_80_up" # Adaptive comfort 80 up - ADAPTIVE_CMF_90_LOW = "adaptive_cmf_90_low" # Adaptive comfort 90 low - ADAPTIVE_CMF_90_UP = "adaptive_cmf_90_up" # Adaptive comfort 90 up - ADAPTIVE_CMF_RMT = "adaptive_cmf_rmt" # Adaptive comfort rmt + UTCI_SUN_WIND = "utci_Sun_Wind" # Utci Sun Wind + UTCI_SUN_NO_WIND = "utci_Sun_noWind" # Utci Sun no Wind + UTCI_NO_SUN_WIND = "utci_noSun_Wind" # Utci no Sun Wind + UTCI_NO_SUN_NO_WIND = "utci_noSun_noWind" # Utci no Sun no Wind + ADAPTIVE_COMFORT = "adaptive_comfort" # Adaptive comfort + ADAPTIVE_CMF_80_LOW = "adaptive_cmf_80_low" # Adaptive comfort 80 low + ADAPTIVE_CMF_80_UP = "adaptive_cmf_80_up" # Adaptive comfort 80 up + ADAPTIVE_CMF_90_LOW = "adaptive_cmf_90_low" # Adaptive comfort 90 low + ADAPTIVE_CMF_90_UP = "adaptive_cmf_90_up" # Adaptive comfort 90 up + ADAPTIVE_CMF_RMT = "adaptive_cmf_rmt" # Adaptive comfort rmt NV_ALLOWED = "nv_allowed" # ==================== Calculation column ==================== @@ -68,10 +68,3 @@ class ColNames(str, Enum): MONTH_NAMES = "month_names" # Month names UTC_TIME = "UTC_time" # UTC Time DOY = "DOY" # Day of Year - - - - - - - From a8c6d1d8affb7ef7ba17e40ced8d4c46df089929 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Tue, 26 Aug 2025 18:38:45 +1000 Subject: [PATCH 42/66] Fix: updated the ElementIds in file wind --- pages/wind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/wind.py b/pages/wind.py index 8517f510..92cc1284 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -378,7 +378,7 @@ def update_annual_wind_rose(_, df, meta, si_ip): # wind speed @callback( - Output(ColNames.WIND_SPEED, "children"), + Output(ElementIds.WIND_SPEED, "children"), # General [ Input(ElementIds.ID_WIND_DF_STORE, "modified_timestamp"), From cf63851a356a9e284fb7c1a98945adf3db49b645 Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Tue, 26 Aug 2025 18:51:20 +1000 Subject: [PATCH 43/66] fix: replaced the string that using Class ColNames in charts_sun.py and charts_summary.py --- pages/lib/charts_summary.py | 12 ++++++------ pages/lib/charts_sun.py | 16 ++++++++-------- pages/lib/global_column_names.py | 20 ++++++++++++++------ 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/pages/lib/charts_summary.py b/pages/lib/charts_summary.py index ede7b264..6973564d 100644 --- a/pages/lib/charts_summary.py +++ b/pages/lib/charts_summary.py @@ -1,14 +1,14 @@ import pandas as pd import plotly.express as px - +from pages.lib.global_column_names import ColNames def world_map(meta): """Return the world map showing the current location.""" - latitude = float(meta["lat"]) - longitude = float(meta["lon"]) - city = meta["city"] - country = meta["country"] - time_zone = float(meta["time_zone"]) + latitude = float(meta[ColNames.LAT]) + longitude = float(meta[ColNames.LON]) + city = meta[ColNames.CITY] + country = meta[ColNames.COUNTRY] + time_zone = float(meta[ColNames.TIME_ZONE]) lat_long_df = pd.DataFrame( data={ "Lat": [latitude], diff --git a/pages/lib/charts_sun.py b/pages/lib/charts_sun.py index 805fd717..a107475c 100644 --- a/pages/lib/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -110,9 +110,9 @@ def monthly_solar(epw_df, si_ip): def polar_graph(df, meta, global_local, var, si_ip): """Return the figure for the custom sun path.""" - latitude = float(meta["lat"]) - longitude = float(meta["lon"]) - time_zone = float(meta["time_zone"]) + latitude = float(meta[ColNames.LAT]) + longitude = float(meta[ColNames.LON]) + time_zone = float(meta[ColNames.TIME_ZONE]) solpos = df.loc[df[ColNames.APPARENT_ELEVATION] > 0, :] if var != "None": @@ -166,7 +166,7 @@ def polar_graph(df, meta, global_local, var, si_ip): if var == "None": fig.add_trace( go.Scatterpolar( - r=90 * np.cos(np.radians(90 - solpos["apparent_zenith"])), + r=90 * np.cos(np.radians(90 - solpos[ColNames.APPARENT_ZENITH])), theta=solpos[ColNames.AZIMUTH], mode="markers", marker_color="orange", @@ -196,7 +196,7 @@ def polar_graph(df, meta, global_local, var, si_ip): else: fig.add_trace( go.Scatterpolar( - r=90 * np.cos(np.radians(90 - solpos["apparent_zenith"])), + r=90 * np.cos(np.radians(90 - solpos[ColNames.APPARENT_ZENITH])), theta=solpos[ColNames.AZIMUTH], mode="markers", marker=dict( @@ -313,9 +313,9 @@ def polar_graph(df, meta, global_local, var, si_ip): def custom_cartesian_solar(df, meta, global_local, var, si_ip): """Return a graph of a latitude and longitude solar diagram.""" - latitude = float(meta["lat"]) - longitude = float(meta["lon"]) - time_zone = float(meta["time_zone"]) + latitude = float(meta[ColNames.LAT]) + longitude = float(meta[ColNames.LON]) + time_zone = float(meta[ColNames.TIME_ZONE]) tz = "UTC" if var != "None": diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 972ad76e..a4471baf 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -9,6 +9,13 @@ class ColNames(str, Enum): HOUR = "hour" # hour MINUTE = "minute" # minute + # ==================== Location related column ==================== + LAT = "lat" # Latitude + LON = "lon" # Longitude + CITY = "city" # City + COUNTRY = "country" # Country + TIME_ZONE = "time_zone" # Time Zone + # ==================== Meteorological data column ==================== DBT = "DBT" # Dry Bulb Temperature DPT = "DPT" # Dew Point Temperature @@ -40,14 +47,15 @@ class ColNames(str, Enum): OSKYCOVER = "Oskycover" # Opaque Sky Cover VIS = "Vis" # Visibility CHEIGHT = "Cheight" # Cloud Height - PWobs = "PWobs" # Precipitation Observation - PWcodes = "PWcodes" # Precipitation Codes - Pwater = "Pwater" # Precipitation Water - AsolOptD = "AsolOptD" # Aerosol Optical Depth - SnowD = "SnowD" # Snow Depth - DaySSnow = "DaySSnow" # Daily Snow +# PWobs = "PWobs" # Precipitation Observation +# PWcodes = "PWcodes" # Precipitation Codes +# Pwater = "Pwater" # Precipitation Water +# AsolOptD = "AsolOptD" # Aerosol Optical Depth +# SnowD = "SnowD" # Snow Depth +# DaySSnow = "DaySSnow" # Daily Snow ELEVATION = "elevation" # Elevation APPARENT_ELEVATION = "apparent_elevation" # Apparent Elevation + APPARENT_ZENITH = "apparent_zenith" # Apparent Zenith AZIMUTH = "azimuth" # Azimuth MRT = "MRT" DELTA_MRT = "delta_mrt" From 373b44cedc435b7b3059cafe649c2a8f6de2f65c Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Tue, 26 Aug 2025 19:15:27 +1000 Subject: [PATCH 44/66] Fix:Update the elements of global_column_names.py and charts_sun.py. --- pages/lib/charts_sun.py | 40 ++++++++++++++++++++------------ pages/lib/global_column_names.py | 28 +++++++++++++--------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/pages/lib/charts_sun.py b/pages/lib/charts_sun.py index a107475c..17028014 100644 --- a/pages/lib/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -20,10 +20,14 @@ def monthly_solar(epw_df, si_ip): g_h_rad_month_ave = ( - epw_df.groupby([ColNames.MONTH, ColNames.HOUR])[ColNames.GLOB_HOR_RAD].median().reset_index() + epw_df.groupby([ColNames.MONTH, ColNames.HOUR])[ColNames.GLOB_HOR_RAD] + .median() + .reset_index() ) dif_h_rad_month_ave = ( - epw_df.groupby([ColNames.MONTH, ColNames.HOUR])[ColNames.DIF_HOR_RAD].median().reset_index() + epw_df.groupby([ColNames.MONTH, ColNames.HOUR])[ColNames.DIF_HOR_RAD] + .median() + .reset_index() ) fig = make_subplots( rows=1, @@ -38,7 +42,9 @@ def monthly_solar(epw_df, si_ip): fig.add_trace( go.Scatter( - x=g_h_rad_month_ave.loc[g_h_rad_month_ave[ColNames.MONTH] == i + 1, ColNames.HOUR], + x=g_h_rad_month_ave.loc[ + g_h_rad_month_ave[ColNames.MONTH] == i + 1, ColNames.HOUR + ], y=g_h_rad_month_ave.loc[ g_h_rad_month_ave[ColNames.MONTH] == i + 1, ColNames.GLOB_HOR_RAD ], @@ -48,12 +54,14 @@ def monthly_solar(epw_df, si_ip): line_width=2, name="Global", showlegend=is_first, - customdata=epw_df.loc[epw_df[ColNames.MONTH] == i + 1, ColNames.MONTH_NAMES], + customdata=epw_df.loc[ + epw_df[ColNames.MONTH] == i + 1, ColNames.MONTH_NAMES + ], hovertemplate=( "" + "Global Horizontal Solar Radiation" + ": %{y:.2f} " - + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip]["unit"] + + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip][ColNames.UNIT] + "
" + "Month: %{customdata}
" + "Hour: %{x}:00
" @@ -78,12 +86,14 @@ def monthly_solar(epw_df, si_ip): line_width=2, name="Diffuse", showlegend=is_first, - customdata=epw_df.loc[epw_df[ColNames.MONTH] == i + 1, ColNames.MONTH_NAMES], + customdata=epw_df.loc[ + epw_df[ColNames.MONTH] == i + 1, ColNames.MONTH_NAMES + ], hovertemplate=( "" + "Diffuse Horizontal Solar Radiation" + ": %{y:.2f} " - + mapping_dictionary[ColNames.DIF_HOR_RAD][si_ip]["unit"] + + mapping_dictionary[ColNames.DIF_HOR_RAD][si_ip][ColNames.UNIT] + "
" + "Month: %{customdata}
" + "Hour: %{x}:00
" @@ -116,10 +126,10 @@ def polar_graph(df, meta, global_local, var, si_ip): solpos = df.loc[df[ColNames.APPARENT_ELEVATION] > 0, :] if var != "None": - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_name = mapping_dictionary[var]["name"] - var_color = mapping_dictionary[var]["color"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_name = mapping_dictionary[var][ColNames.NAME] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local == "global": # Set Global values for Max and minimum range_z = var_range @@ -319,10 +329,10 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): tz = "UTC" if var != "None": - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_name = mapping_dictionary[var]["name"] - var_color = mapping_dictionary[var]["color"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_name = mapping_dictionary[var][ColNames.NAME] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local == "global": # Set Global values for Max and minimum range_z = var_range diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index a4471baf..28317967 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -10,11 +10,11 @@ class ColNames(str, Enum): MINUTE = "minute" # minute # ==================== Location related column ==================== - LAT = "lat" # Latitude - LON = "lon" # Longitude - CITY = "city" # City - COUNTRY = "country" # Country - TIME_ZONE = "time_zone" # Time Zone + LAT = "lat" # Latitude + LON = "lon" # Longitude + CITY = "city" # City + COUNTRY = "country" # Country + TIME_ZONE = "time_zone" # Time Zone # ==================== Meteorological data column ==================== DBT = "DBT" # Dry Bulb Temperature @@ -47,12 +47,12 @@ class ColNames(str, Enum): OSKYCOVER = "Oskycover" # Opaque Sky Cover VIS = "Vis" # Visibility CHEIGHT = "Cheight" # Cloud Height -# PWobs = "PWobs" # Precipitation Observation -# PWcodes = "PWcodes" # Precipitation Codes -# Pwater = "Pwater" # Precipitation Water -# AsolOptD = "AsolOptD" # Aerosol Optical Depth -# SnowD = "SnowD" # Snow Depth -# DaySSnow = "DaySSnow" # Daily Snow + # PWobs = "PWobs" # Precipitation Observation + # PWcodes = "PWcodes" # Precipitation Codes + # Pwater = "Pwater" # Precipitation Water + # AsolOptD = "AsolOptD" # Aerosol Optical Depth + # SnowD = "SnowD" # Snow Depth + # DaySSnow = "DaySSnow" # Daily Snow ELEVATION = "elevation" # Elevation APPARENT_ELEVATION = "apparent_elevation" # Apparent Elevation APPARENT_ZENITH = "apparent_zenith" # Apparent Zenith @@ -76,3 +76,9 @@ class ColNames(str, Enum): MONTH_NAMES = "month_names" # Month names UTC_TIME = "UTC_time" # UTC Time DOY = "DOY" # Day of Year + + COLOR = "color" + NAME = "name" + RANGE = "range" + UNIT = "unit" + TWENTY_FOUR_HOUR = "24h" From f03a6ede15ee9890bbb9cf404c0e1a5cecbcd941 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Tue, 26 Aug 2025 19:31:18 +1000 Subject: [PATCH 45/66] Fix: Update the column name in file charts_data_explorer.py --- pages/lib/charts_data_explorer.py | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/pages/lib/charts_data_explorer.py b/pages/lib/charts_data_explorer.py index d1534057..c747763f 100644 --- a/pages/lib/charts_data_explorer.py +++ b/pages/lib/charts_data_explorer.py @@ -31,12 +31,12 @@ def custom_heatmap(df, global_local, var, time_filter_info, data_filter_info, si if df.dropna(subset=[var]).shape[0] == 0: return None - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_name = mapping_dictionary[var]["name"] - var_color = mapping_dictionary[var]["color"] - filter_name = mapping_dictionary[filter_var]["name"] - filter_unit = mapping_dictionary[filter_var][si_ip]["unit"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_name = mapping_dictionary[var][ColNames.NAME] + var_color = mapping_dictionary[var][ColNames.COLOR] + filter_name = mapping_dictionary[filter_var][ColNames.NAME] + filter_unit = mapping_dictionary[filter_var][si_ip][ColNames.UNIT] if global_local == "global": # Set Global values for Max and minimum @@ -112,12 +112,12 @@ def three_var_graph( min_val = data_filter_info3[2] max_val = data_filter_info3[3] - var_unit_x = mapping_dictionary[var_x][si_ip]["unit"] - var_unit_y = mapping_dictionary[var_y][si_ip]["unit"] + var_unit_x = mapping_dictionary[var_x][si_ip][ColNames.UNIT] + var_unit_y = mapping_dictionary[var_y][si_ip][ColNames.UNIT] var = color_by - var_range = mapping_dictionary[var][si_ip]["range"] - var_color = mapping_dictionary[var]["color"] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local != "global": # Set maximum and minimum according to data @@ -137,11 +137,11 @@ def three_var_graph( return None title = ( - mapping_dictionary[var_x]["name"] + mapping_dictionary[var_x][ColNames.NAME] + " vs " - + mapping_dictionary[var_y]["name"] + + mapping_dictionary[var_y][ColNames.NAME] + " colored by " - + mapping_dictionary[color_by]["name"] + + mapping_dictionary[color_by][ColNames.NAME] ) fig = px.scatter( @@ -168,13 +168,13 @@ def three_var_graph( def two_var_graph(df, var_x, var_y, si_ip): title = ( "Simultaneous frequency of " - + mapping_dictionary[var_x]["name"] + + mapping_dictionary[var_x][ColNames.NAME] + " and " - + mapping_dictionary[var_y]["name"] + + mapping_dictionary[var_y][ColNames.NAME] ) - var_unit_x = mapping_dictionary[var_x][si_ip]["unit"] - var_unit_y = mapping_dictionary[var_y][si_ip]["unit"] + var_unit_x = mapping_dictionary[var_x][si_ip][ColNames.UNIT] + var_unit_y = mapping_dictionary[var_y][si_ip][ColNames.UNIT] fig = px.density_heatmap( df, From 722caca33513731de14e1cf97ec5533eb59dfa12 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Tue, 26 Aug 2025 19:41:53 +1000 Subject: [PATCH 46/66] Fix: Update the column name in file extract_df.py anad global_column_names.py --- pages/lib/extract_df.py | 10 +++++----- pages/lib/global_column_names.py | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pages/lib/extract_df.py b/pages/lib/extract_df.py index ce2df0fa..55b76e76 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -329,11 +329,11 @@ def create_df(lst, file_name): limit_inputs=False, ) epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_RMT] = rmt - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_COMFORT] = r["tmp_cmf"] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_LOW] = r["tmp_cmf_80_low"] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_UP] = r["tmp_cmf_80_up"] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_LOW] = r["tmp_cmf_90_low"] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_UP] = r["tmp_cmf_90_up"] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_COMFORT] = r[ColNames.TMP_CMF] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_LOW] = r[ColNames.TMP_CMF_80_LOW] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_UP] = r[ColNames.TMP_CMF_80_UP] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_LOW] = r[ColNames.TMP_CMF_90_LOW] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_UP] = r[ColNames.TMP_CMF_90_UP] return epw_df, location_info diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 28317967..ece9a146 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -70,6 +70,12 @@ class ColNames(str, Enum): ADAPTIVE_CMF_90_UP = "adaptive_cmf_90_up" # Adaptive comfort 90 up ADAPTIVE_CMF_RMT = "adaptive_cmf_rmt" # Adaptive comfort rmt NV_ALLOWED = "nv_allowed" + TMP_CMF = "tmp_cmf" + TMP_CMF_80_LOW = "tmp_cmf_80_low" + TMP_CMF_80_UP = "tmp_cmf_80_up" + TMP_CMF_90_LOW = "tmp_cmf_90_low" + TMP_CMF_90_UP = "tmp_cmf_90_up" + # ==================== Calculation column ==================== FAKE_YEAR = "fake_year" # Fake Year From 73b2f8a4b28140f247d25e905bc3f286794e551b Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Tue, 26 Aug 2025 19:59:19 +1000 Subject: [PATCH 47/66] Fix:Update the elements of template_graphs.py. --- pages/lib/template_graphs.py | 85 ++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index ae840591..b0f20f1f 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -17,9 +17,9 @@ def violin(df, var, global_local, si_ip): """Return day night violin based on the 'var' col""" mask_day = (df[ColNames.HOUR] >= 8) & (df[ColNames.HOUR] < 20) mask_night = (df[ColNames.HOUR] < 8) | (df[ColNames.HOUR] >= 20) - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_name = mapping_dictionary[var]["name"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_name = mapping_dictionary[var][ColNames.NAME] data_day = df.loc[mask_day, var] data_night = df.loc[mask_night, var] @@ -85,10 +85,10 @@ def violin(df, var, global_local, si_ip): @code_timer def yearly_profile(df, var, global_local, si_ip): """Return yearly profile figure based on the 'var' col.""" - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_name = mapping_dictionary[var]["name"] - var_color = mapping_dictionary[var]["color"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_name = mapping_dictionary[var][ColNames.NAME] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local == "global": # Set Global values for Max and minimum @@ -114,7 +114,11 @@ def yearly_profile(df, var, global_local, si_ip): marker_opacity=0.3, name=var_name + " Range", customdata=np.stack( - (dbt_day["mean"], df.iloc[::24, :][ColNames.MONTH_NAMES], df.iloc[::24, :][ColNames.DAY]), + ( + dbt_day["mean"], + df.iloc[::24, :][ColNames.MONTH_NAMES], + df.iloc[::24, :][ColNames.DAY], + ), axis=-1, ), hovertemplate=( @@ -136,7 +140,11 @@ def yearly_profile(df, var, global_local, si_ip): marker_color=var_single_color, marker_opacity=1, customdata=np.stack( - (dbt_day["mean"], df.iloc[::24, :][ColNames.MONTH_NAMES], df.iloc[::24, :][ColNames.DAY]), + ( + dbt_day["mean"], + df.iloc[::24, :][ColNames.MONTH_NAMES], + df.iloc[::24, :][ColNames.DAY], + ), axis=-1, ), hovertemplate=( @@ -238,10 +246,10 @@ def yearly_profile(df, var, global_local, si_ip): # @code_timer def daily_profile(df, var, global_local, si_ip): """Return the daily profile based on the 'var' col.""" - var_name = mapping_dictionary[var]["name"] - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_color = mapping_dictionary[var]["color"] + var_name = mapping_dictionary[var][ColNames.NAME] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local == "global": # Set Global values for Max and minimum range_y = var_range @@ -252,7 +260,9 @@ def daily_profile(df, var, global_local, si_ip): range_y = [data_min, data_max] var_single_color = var_color[len(var_color) // 2] - var_month_ave = df.groupby([ColNames.MONTH, ColNames.HOUR])[var].median().reset_index() + var_month_ave = ( + df.groupby([ColNames.MONTH, ColNames.HOUR])[var].median().reset_index() + ) fig = make_subplots( rows=1, cols=12, @@ -286,7 +296,9 @@ def daily_profile(df, var, global_local, si_ip): fig.add_trace( go.Scatter( - x=var_month_ave.loc[var_month_ave[ColNames.MONTH] == i + 1, ColNames.HOUR], + x=var_month_ave.loc[ + var_month_ave[ColNames.MONTH] == i + 1, ColNames.HOUR + ], y=var_month_ave.loc[var_month_ave[ColNames.MONTH] == i + 1, var], mode="lines", line_color=var_single_color, @@ -331,9 +343,9 @@ def heatmap_with_filter( title, ): """General function that returns a heatmap.""" - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_color = mapping_dictionary[var]["color"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_color = mapping_dictionary[var][ColNames.COLOR] df = filter_df_by_month_and_hour( df, time_filter, month, hour, invert_month, invert_hour, var @@ -409,9 +421,9 @@ def heatmap_with_filter( def heatmap(df, var, global_local, si_ip): """General function that returns a heatmap.""" - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_color = mapping_dictionary[var]["color"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local == "global": # Set Global values for Max and minimum @@ -478,16 +490,20 @@ def wind_rose(df, title, month, hour, labels, si_ip): start_hour = hour[0] end_hour = hour[1] if start_month <= end_month: - df = df.loc[(df[ColNames.MONTH] >= start_month) & (df[ColNames.MONTH] <= end_month)] + df = df.loc[ + (df[ColNames.MONTH] >= start_month) & (df[ColNames.MONTH] <= end_month) + ] else: - df = df.loc[(df[ColNames.MONTH] <= end_month) | (df[ColNames.MONTH] >= start_month)] + df = df.loc[ + (df[ColNames.MONTH] <= end_month) | (df[ColNames.MONTH] >= start_month) + ] if start_hour <= end_hour: df = df.loc[(df[ColNames.HOUR] > start_hour) & (df[ColNames.HOUR] <= end_hour)] else: df = df.loc[(df[ColNames.HOUR] <= end_hour) | (df[ColNames.HOUR] >= start_hour)] - spd_colors = mapping_dictionary[ColNames.WIND_SPEED]["color"] - spd_unit = mapping_dictionary[ColNames.WIND_SPEED][si_ip]["unit"] + spd_colors = mapping_dictionary[ColNames.WIND_SPEED][ColNames.COLOR] + spd_unit = mapping_dictionary[ColNames.WIND_SPEED][si_ip][ColNames.UNIT] spd_bins = [-1, 0.5, 1.5, 3.3, 5.5, 7.9, 10.7, 13.8, 17.1, 20.7, np.inf] if si_ip == UnitSystem.IP: spd_bins = convert_bins(spd_bins) @@ -631,7 +647,10 @@ def thermal_stress_stacked_barchart( isNormalized = True if len(normalize) != 0 else False if isNormalized: new_df = ( - df.groupby(ColNames.MONTH)[var].value_counts(normalize=True).unstack(var).fillna(0) + df.groupby(ColNames.MONTH)[var] + .value_counts(normalize=True) + .unstack(var) + .fillna(0) ) new_df = new_df.set_axis(categories, axis=1) new_df.reset_index(inplace=True) @@ -714,12 +733,12 @@ def barchart(df, var, time_filter_info, data_filter_info, normalize, si_ip): end_hour = time_filter_info[2][1] filter_var = str(data_filter_info[1]) - filter_name = mapping_dictionary[filter_var]["name"] - filter_unit = mapping_dictionary[filter_var][si_ip]["unit"] + filter_name = mapping_dictionary[filter_var][ColNames.NAME] + filter_unit = mapping_dictionary[filter_var][si_ip][ColNames.UNIT] - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_name = mapping_dictionary[var]["name"] - var_color = mapping_dictionary[var]["color"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + var_name = mapping_dictionary[var][ColNames.NAME] + var_color = mapping_dictionary[var][ColNames.COLOR] color_below = var_color[0] color_above = var_color[-1] @@ -833,7 +852,9 @@ def filter_df_by_month_and_hour( mask = (df[ColNames.MONTH] < start_month) | (df[ColNames.MONTH] > end_month) df.loc[mask, var] = None else: - mask = (df[ColNames.MONTH] >= end_month) & (df[ColNames.MONTH] <= start_month) + mask = (df[ColNames.MONTH] >= end_month) & ( + df[ColNames.MONTH] <= start_month + ) df.loc[mask, var] = None if start_hour <= end_hour: From b8568e3a8b774e61debc0bdd052d7918bee2505f Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Tue, 26 Aug 2025 20:01:32 +1000 Subject: [PATCH 48/66] Fix: Update the column name in file global_scheme.py and psy-chart.py --- pages/lib/global_scheme.py | 11 ++++++----- pages/psy-chart.py | 34 +++++++++++++++++----------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/pages/lib/global_scheme.py b/pages/lib/global_scheme.py index b71d008c..b0ff2e6e 100644 --- a/pages/lib/global_scheme.py +++ b/pages/lib/global_scheme.py @@ -1,4 +1,5 @@ import plotly.io as pio +from global_column_names import ColNames from config import UnitSystem @@ -900,20 +901,20 @@ ] sun_cloud_tab_dropdown_names = { - mapping_dictionary[key]["name"]: key for key in variables_sun_cloud_tab_dropdown + mapping_dictionary[key][ColNames.NAME]: key for key in variables_sun_cloud_tab_dropdown } -dropdown_names = {mapping_dictionary[key]["name"]: key for key in variables_dropdown} +dropdown_names = {mapping_dictionary[key][ColNames.NAME]: key for key in variables_dropdown} more_variables_dropdown = { - mapping_dictionary[key]["name"]: key for key in variables_more_variables_dropdown + mapping_dictionary[key][ColNames.NAME]: key for key in variables_more_variables_dropdown } sun_cloud_tab_explore_dropdown_names = { - mapping_dictionary[key]["name"]: key + mapping_dictionary[key][ColNames.NAME]: key for key in variables_sun_cloud_tab_explore_dropdown } outdoor_dropdown_names = { - mapping_dictionary[key]["name"]: key for key in variables_outdoor_dropdown + mapping_dictionary[key][ColNames.NAME]: key for key in variables_outdoor_dropdown } diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 7fbd802d..9f934723 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -306,16 +306,16 @@ def update_psych_chart( elif var == "Frequency": var_color = ["rgba(255,255,255,0)", "rgb(0,150,255)", "rgb(0,0,150)"] else: - var_unit = mapping_dictionary[var][si_ip]["unit"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] - var_name = mapping_dictionary[var]["name"] + var_name = mapping_dictionary[var][ColNames.NAME] - var_color = mapping_dictionary[var]["color"] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local == "global": # Set Global values for Max and minimum - var_range_x = mapping_dictionary[ColNames.DBT][si_ip]["range"] - var_range_y = mapping_dictionary[ColNames.HR][si_ip]["range"] + var_range_x = mapping_dictionary[ColNames.DBT][si_ip][ColNames.RANGE] + var_range_y = mapping_dictionary[ColNames.HR][si_ip][ColNames.RANGE] else: # Set maximum and minimum according to data @@ -386,9 +386,9 @@ def update_psych_chart( showscale=False, opacity=0.2, ), - hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + hovertemplate=mapping_dictionary[ColNames.DBT][ColNames.NAME] + ": %{x:.2f}" - + mapping_dictionary[ColNames.DBT]["name"], + + mapping_dictionary[ColNames.DBT][ColNames.NAME], name="", ) ) @@ -452,21 +452,21 @@ def update_psych_chart( colorbar=var_colorbar, ), customdata=np.stack((df[ColNames.RH], df["h"], df[var], df["t_dp"]), axis=-1), - hovertemplate=mapping_dictionary[ColNames.DBT]["name"] + hovertemplate=mapping_dictionary[ColNames.DBT][ColNames.NAME] + ": %{x:.2f}" - + mapping_dictionary[ColNames.DBT][si_ip]["unit"] + + mapping_dictionary[ColNames.DBT][si_ip][ColNames.UNIT] + "
" - + mapping_dictionary[ColNames.RH]["name"] + + mapping_dictionary[ColNames.RH][ColNames.NAME] + ": %{customdata[0]:.2f}" - + mapping_dictionary[ColNames.RH][si_ip]["unit"] + + mapping_dictionary[ColNames.RH][si_ip][ColNames.UNIT] + "
" - + mapping_dictionary["h"]["name"] + + mapping_dictionary["h"][ColNames.NAME] + ": %{customdata[1]:.2f}" - + mapping_dictionary["h"][si_ip]["unit"] + + mapping_dictionary["h"][si_ip][ColNames.UNIT] + "
" - + mapping_dictionary["t_dp"]["name"] + + mapping_dictionary["t_dp"][ColNames.NAME] + ": %{customdata[3]:.2f}" - + mapping_dictionary["t_dp"][si_ip]["unit"] + + mapping_dictionary["t_dp"][si_ip][ColNames.UNIT] + "
" + "
" + var_name @@ -476,8 +476,8 @@ def update_psych_chart( ) ) - xtitle_name = "Temperature" + " " + mapping_dictionary[ColNames.DBT][si_ip]["unit"] - ytitle_name = "Humidity Ratio" + " " + mapping_dictionary[ColNames.HR][si_ip]["unit"] + xtitle_name = "Temperature" + " " + mapping_dictionary[ColNames.DBT][si_ip][ColNames.UNIT] + ytitle_name = "Humidity Ratio" + " " + mapping_dictionary[ColNames.HR][si_ip][ColNames.UNIT] fig.update_layout(template=template, margin=tight_margins) fig.update_xaxes( title_text=xtitle_name, From 6adf2fce1391c31001468f28485f55fb83a391b2 Mon Sep 17 00:00:00 2001 From: Ziqi Liu Date: Wed, 27 Aug 2025 13:45:54 +1000 Subject: [PATCH 49/66] Fix:Update the elements of global_scheme.py, natural_ventilation.py, psy-chart.py, select.py, summary.py. --- pages/lib/global_scheme.py | 2 +- pages/natural_ventilation.py | 25 +++++++++++++------------ pages/psy-chart.py | 2 +- pages/select.py | 3 ++- pages/summary.py | 4 ++-- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/pages/lib/global_scheme.py b/pages/lib/global_scheme.py index b0ff2e6e..2f984aac 100644 --- a/pages/lib/global_scheme.py +++ b/pages/lib/global_scheme.py @@ -1,5 +1,5 @@ import plotly.io as pio -from global_column_names import ColNames +from pages.lib.global_column_names import ColNames from config import UnitSystem diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 091d8fa4..073f4250 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -1,4 +1,3 @@ - import math import dash @@ -357,17 +356,17 @@ def nv_heatmap( df, time_filter, month, hour, invert_month, invert_hour, var ) - var_unit = mapping_dictionary[var][si_ip]["unit"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] - filter_unit = mapping_dictionary[filter_var][si_ip]["unit"] + filter_unit = mapping_dictionary[filter_var][si_ip][ColNames.UNIT] - var_range = mapping_dictionary[var][si_ip]["range"] + var_range = mapping_dictionary[var][si_ip][ColNames.RANGE] - var_name = mapping_dictionary[var]["name"] + var_name = mapping_dictionary[var][ColNames.NAME] - filter_name = mapping_dictionary[filter_var]["name"] + filter_name = mapping_dictionary[filter_var][ColNames.NAME] - var_color = mapping_dictionary[var]["color"] + var_color = mapping_dictionary[var][ColNames.COLOR] if global_local == "global": range_z = var_range @@ -502,12 +501,12 @@ def nv_bar_chart( var = "DBT" filter_var = "DPT" - var_unit = mapping_dictionary[var][si_ip]["unit"] - filter_unit = mapping_dictionary[filter_var][si_ip]["unit"] + var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] + filter_unit = mapping_dictionary[filter_var][si_ip][ColNames.UNIT] - var_name = mapping_dictionary[var]["name"] + var_name = mapping_dictionary[var][ColNames.NAME] - filter_name = mapping_dictionary[filter_var]["name"] + filter_name = mapping_dictionary[filter_var][ColNames.NAME] color_in = "dodgerblue" @@ -523,7 +522,9 @@ def nv_bar_chart( ) if dbt_data_filter and (min_dbt_val <= max_dbt_val): - df.loc[(df[var] < min_dbt_val) | (df[var] > max_dbt_val), ColNames.NV_ALLOWED] = 0 + df.loc[ + (df[var] < min_dbt_val) | (df[var] > max_dbt_val), ColNames.NV_ALLOWED + ] = 0 if dpt_data_filter: df.loc[(df[filter_var] > max_dpt_val), ColNames.NV_ALLOWED] = 0 diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 9f934723..70c137c1 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -289,7 +289,7 @@ def update_psych_chart( mask = (df[data_filter_var] >= max_val) & (df[data_filter_var] <= min_val) df[mask] = None - if df.dropna(subset=["month"]).shape[0] == 0: + if df.dropna(subset=[ColNames.MONTH]).shape[0] == 0: return ( dbc.Alert( "No data is available in this location under these conditions. Please " diff --git a/pages/select.py b/pages/select.py index 89fd67f0..b4b202cb 100644 --- a/pages/select.py +++ b/pages/select.py @@ -13,6 +13,7 @@ from pages.lib.extract_df import convert_data from pages.lib.extract_df import create_df, get_data, get_location_info +from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds from pages.lib.global_scheme import mapping_dictionary from config import PageUrls, PageInfo, UnitSystem @@ -350,7 +351,7 @@ def plot_location_epw_files(pathname): df_one_building, lat="lat", lon="lon", - hover_name=df_one_building["name"], + hover_name=df_one_building[ColNames.NAME], color_discrete_sequence=["#4895ef"], hover_data=[ "period", diff --git a/pages/summary.py b/pages/summary.py index 199191f1..691fa233 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -260,7 +260,7 @@ def update_location_info(ts, df, meta, si_ip): # global horizontal irradiance # Note that the value is divided by 1000, so a corresponding change is made in the unit: total_solar_rad_value = round(df[ColNames.GLOB_HOR_RAD].sum() / 1000, 2) - total_solar_rad_unit = "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip]["unit"] + total_solar_rad_unit = "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip][ColNames.UNIT] total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" glob_sum = df[ColNames.GLOB_HOR_RAD].sum() @@ -271,7 +271,7 @@ def update_location_info(ts, df, meta, si_ip): total_diffuse_rad = ( f"Percentage of diffuse horizontal solar radiation: {diffuse_percentage} %" ) - tmp_unit = mapping_dictionary[ColNames.DBT][si_ip]["unit"] + tmp_unit = mapping_dictionary[ColNames.DBT][si_ip][ColNames.UNIT] average_yearly_tmp = ( f"Average yearly temperature: {df[ColNames.DBT].mean().round(1)} " + tmp_unit ) From dceb8567a219e9d84dd2c25cd2a7a87a6359ec7d Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Wed, 27 Aug 2025 13:50:32 +1000 Subject: [PATCH 50/66] Fix: Update the column name in file global_scheme.py --- pages/lib/global_scheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/lib/global_scheme.py b/pages/lib/global_scheme.py index b0ff2e6e..2f984aac 100644 --- a/pages/lib/global_scheme.py +++ b/pages/lib/global_scheme.py @@ -1,5 +1,5 @@ import plotly.io as pio -from global_column_names import ColNames +from pages.lib.global_column_names import ColNames from config import UnitSystem From a75124effdeb7718e0f93d39fcada58f565b0be6 Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Wed, 27 Aug 2025 15:15:43 +1000 Subject: [PATCH 51/66] fix: replaced the string that using Class ColNames in utils.py and summary.py and extract_df.py --- pages/lib/extract_df.py | 30 +++++++++++++++--------------- pages/lib/global_column_names.py | 7 +++++++ pages/lib/utils.py | 12 ++++++------ pages/summary.py | 18 +++++++++--------- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/pages/lib/extract_df.py b/pages/lib/extract_df.py index 55b76e76..451e1b8d 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -68,7 +68,7 @@ def get_location_info(lst, file_name): # from OneClimaBuilding files extract info about reference years try: - location_info["period"] = re.search(r'cord=[\'"]?([^\'" >]+);', lst[5]).group(1) + location_info[ColNames.PERIOD] = re.search(r'cord=[\'"]?([^\'" >]+);', lst[5]).group(1) except AttributeError: pass @@ -94,7 +94,7 @@ def create_df(lst, file_name): # from OneClimaBuilding files extract info about reference years try: - location_info["period"] = re.search(r'cord=[\'"]?([^\'" >]+);', lst[5]).group(1) + location_info[ColNames.PERIOD] = re.search(r'cord=[\'"]?([^\'" >]+);', lst[5]).group(1) except AttributeError: pass @@ -150,15 +150,15 @@ def create_df(lst, file_name): epw_df = pd.DataFrame(columns=col_names, data=lst) # from EnergyPlus files extract info about reference years - if not location_info["period"]: + if not location_info[ColNames.PERIOD]: years = epw_df[ColNames.YEAR].astype("int").unique() if len(years) == 1: year_rounded_up = int(math.ceil(years[0] / 10.0)) * 10 - location_info["period"] = f"{year_rounded_up - 10}-{year_rounded_up}" + location_info[ColNames.PERIOD] = f"{year_rounded_up - 10}-{year_rounded_up}" else: min_year = int(math.floor(min(years) / 10.0)) * 10 max_year = int(math.ceil(max(years) / 10.0)) * 10 - location_info["period"] = f"{min_year}-{max_year}" + location_info[ColNames.PERIOD] = f"{min_year}-{max_year}" # Add fake_year epw_df[ColNames.FAKE_YEAR] = ColNames.YEAR @@ -213,16 +213,16 @@ def create_df(lst, file_name): "2019-01-01 00:00:00", "2020-01-01", inclusive="left", freq="h", tz="UTC" ) epw_df[ColNames.UTC_TIME] = pd.to_datetime(times) - delta = timedelta(days=0, hours=location_info["time_zone"] - 1, minutes=0) + delta = timedelta(days=0, hours=location_info[ColNames.TIME_ZONE] - 1, minutes=0) times = times - delta - epw_df["times"] = times + epw_df[ColNames.TIMES] = times epw_df.set_index( - "times", drop=False, append=False, inplace=True, verify_integrity=False + ColNames.TIMES, drop=False, append=False, inplace=True, verify_integrity=False ) # Add in solar position df solar_position = solarposition.get_solarposition( - times, location_info["lat"], location_info["lon"] + times, location_info[ColNames.LAT], location_info[ColNames.LON] ) epw_df = pd.concat([epw_df, solar_position], axis=1) @@ -280,16 +280,16 @@ def create_df(lst, file_name): utci_bins = [-999, -40, -27, -13, 0, 9, 26, 32, 38, 46, 999] utci_labels = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] - epw_df["utci_noSun_Wind_categories"] = pd.cut( + epw_df[ColNames.UTCI_NOSUN_WIND_CATEGORIES] = pd.cut( x=epw_df[ColNames.UTCI_NO_SUN_WIND], bins=utci_bins, labels=utci_labels ) - epw_df["utci_noSun_noWind_categories"] = pd.cut( + epw_df[ColNames.UTCI_NOSUN_NOWIND_CATEGORIES] = pd.cut( x=epw_df[ColNames.UTCI_NO_SUN_NO_WIND], bins=utci_bins, labels=utci_labels ) - epw_df["utci_Sun_Wind_categories"] = pd.cut( + epw_df[ColNames.UTCI_SUN_WIND_CATEGORIES] = pd.cut( x=epw_df[ColNames.UTCI_SUN_WIND], bins=utci_bins, labels=utci_labels ) - epw_df["utci_Sun_noWind_categories"] = pd.cut( + epw_df[ColNames.UTCI_SUN_NOWIND_CATEGORIES] = pd.cut( x=epw_df[ColNames.UTCI_SUN_NO_WIND], bins=utci_bins, labels=utci_labels ) @@ -380,8 +380,8 @@ def convert_data(df, mapping_json): mapping_dict = json.loads(mapping_json) for key in json.loads(mapping_json): - if "conversion_function" in mapping_dict[key]: - conversion_function_name = mapping_dict[key]["conversion_function"] + if ColNames.CONVERSION_FUNCTION in mapping_dict[key]: + conversion_function_name = mapping_dict[key][ColNames.CONVERSION_FUNCTION] if conversion_function_name is not None: conversion_function = globals()[conversion_function_name] conversion_function(df, key) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index ece9a146..dcae7541 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -4,6 +4,7 @@ class ColNames(str, Enum): # ==================== Time related column ==================== YEAR = "year" # year + PERIOD = "period" # period MONTH = "month" # month DAY = "day" # day HOUR = "hour" # hour @@ -63,6 +64,10 @@ class ColNames(str, Enum): UTCI_SUN_NO_WIND = "utci_Sun_noWind" # Utci Sun no Wind UTCI_NO_SUN_WIND = "utci_noSun_Wind" # Utci no Sun Wind UTCI_NO_SUN_NO_WIND = "utci_noSun_noWind" # Utci no Sun no Wind + UTCI_SUN_WIND_CATEGORIES = "utci_Sun_Wind_categories" # Utci Sun Wind Categories + UTCI_SUN_NOWIND_CATEGORIES = "utci_Sun_noWind_categories" # Utci Sun no Wind Categories + UTCI_NOSUN_WIND_CATEGORIES = "utci_noSun_Wind_categories" # Utci no Sun Wind Categories + UTCI_NOSUN_NOWIND_CATEGORIES = "utci_noSun_noWind_categories" # Utci no Sun no Wind Categories ADAPTIVE_COMFORT = "adaptive_comfort" # Adaptive comfort ADAPTIVE_CMF_80_LOW = "adaptive_cmf_80_low" # Adaptive comfort 80 low ADAPTIVE_CMF_80_UP = "adaptive_cmf_80_up" # Adaptive comfort 80 up @@ -75,6 +80,7 @@ class ColNames(str, Enum): TMP_CMF_80_UP = "tmp_cmf_80_up" TMP_CMF_90_LOW = "tmp_cmf_90_low" TMP_CMF_90_UP = "tmp_cmf_90_up" + CONVERSION_FUNCTION = "conversion_function" # ==================== Calculation column ==================== @@ -88,3 +94,4 @@ class ColNames(str, Enum): RANGE = "range" UNIT = "unit" TWENTY_FOUR_HOUR = "24h" + TIMES = "times" diff --git a/pages/lib/utils.py b/pages/lib/utils.py index 89038bae..b40e39a8 100644 --- a/pages/lib/utils.py +++ b/pages/lib/utils.py @@ -55,7 +55,7 @@ def generate_units_degree(si_ip): def generate_custom_inputs(var): if var in mapping_dictionary: - var_fullname = mapping_dictionary[var]["name"] + var_fullname = mapping_dictionary[var][ColNames.NAME] custom_inputs = "".join(word.capitalize() for word in var_fullname.split(" ")) return custom_inputs else: @@ -90,14 +90,14 @@ def generate_custom_inputs_explorer( end_month_abbr = month_names[int(end_month)] if var in mapping_dictionary: var_fullname = "".join( - word.capitalize() for word in mapping_dictionary[var]["name"].split(" ") + word.capitalize() for word in mapping_dictionary[var][ColNames.NAME].split(" ") ) else: var_fullname = var if filter_var in mapping_dictionary: filter_fullname = "".join( word.capitalize() - for word in mapping_dictionary[filter_var]["name"].split(" ") + for word in mapping_dictionary[filter_var][ColNames.NAME].split(" ") ) else: filter_fullname = filter_var @@ -121,14 +121,14 @@ def generate_custom_inputs_psy( if colorby_var in mapping_dictionary: colorby_fullname = "".join( word.capitalize() - for word in mapping_dictionary[colorby_var]["name"].split(" ") + for word in mapping_dictionary[colorby_var][ColNames.NAME].split(" ") ) else: colorby_fullname = colorby_var if data_filter_var in mapping_dictionary: data_filter_fullname = "".join( word.capitalize() - for word in mapping_dictionary[data_filter_var]["name"].split(" ") + for word in mapping_dictionary[data_filter_var][ColNames.NAME].split(" ") ) else: data_filter_fullname = data_filter_var @@ -235,7 +235,7 @@ def summary_table_tmp_rh_tab(df, value, si_ip): df_summary = pd.concat([df_summary, df_sum]) unit = ( - mapping_dictionary[value][si_ip]["unit"] + mapping_dictionary[value][si_ip][ColNames.UNIT] .replace("", "") .replace("", "") ) diff --git a/pages/summary.py b/pages/summary.py index 691fa233..870ccd21 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -224,9 +224,9 @@ def update_map(meta): ) def update_location_info(ts, df, meta, si_ip): """Update the contents of tab two. Passing in the general info (df, meta).""" - location = f"Location: {meta['city']}, {meta['country']}" - lon = f"Longitude: {meta['lon']}" - lat = f"Latitude: {meta['lat']}" + location = f"Location: {meta[ColNames.CITY]}, {meta[ColNames.COUNTRY]}" + lon = f"Longitude: {meta[ColNames.LON]}" + lat = f"Latitude: {meta[ColNames.LAT]}" site_elevation = float(meta["site_elevation"]) site_elevation = round(site_elevation, 2) @@ -237,12 +237,12 @@ def update_location_info(ts, df, meta, si_ip): else: elevation = f"Elevation above sea level: {meta['site_elevation']} m" period = "" - if meta["period"]: - start, stop = meta["period"].split("-") + if meta[ColNames.PERIOD]: + start, stop = meta[ColNames.PERIOD].split("-") period = f"This file is based on data collected between {start} and {stop}" r = requests.get( - f"http://climateapi.scottpinkelman.com/api/v1/location/{meta['lat']}/{meta['lon']}" + f"http://climateapi.scottpinkelman.com/api/v1/location/{meta[ColNames.LAT]}/{meta[ColNames.LON]}" ) climate_text = "" @@ -527,11 +527,11 @@ def download_clima_dataframe(n_clicks, df, meta, si_ip): elif df is not None: if si_ip == UnitSystem.SI: return dcc.send_data_frame( - df.to_csv, f"df_{meta['city']}_{meta['country']}_Clima_SIunit.csv" + df.to_csv, f"df_{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_Clima_SIunit.csv" ) else: return dcc.send_data_frame( - df.to_csv, f"df_{meta['city']}_{meta['country']}_Clima_IPunit.csv" + df.to_csv, f"df_{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_Clima_IPunit.csv" ) else: print("df not loaded yet") @@ -552,7 +552,7 @@ def download_epw(n_clicks, meta): lines[0] = lines[0].replace("b'", "") return dict( content="\n".join(lines), - filename=f"{meta['city']}_{meta['country']}.epw", + filename=f"{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}.epw", ) else: raise PreventUpdate From 7c43d2ad287204bb31cf651a823553ac5b2000b6 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Wed, 27 Aug 2025 15:24:09 +1000 Subject: [PATCH 52/66] Fix: Update the column name in file global_column_names.py, layout.py and template_graphs.py --- pages/lib/global_column_names.py | 4 ++++ pages/lib/layout.py | 10 +++++----- pages/lib/template_graphs.py | 16 ++++++++-------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index dcae7541..c961fa18 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -95,3 +95,7 @@ class ColNames(str, Enum): UNIT = "unit" TWENTY_FOUR_HOUR = "24h" TIMES = "times" + + PATH = "path" + WIND_DIR_BINS = "WindDir_bins" + WIND_SPD_BINS = "WindSpd_bins" \ No newline at end of file diff --git a/pages/lib/layout.py b/pages/lib/layout.py index 558e3745..2ec78075 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -3,7 +3,7 @@ from dash import dcc, html import dash_mantine_components as dmc from dash_iconify import DashIconify - +from pages.lib.global_column_names import ColNames from config import DocLinks, UnitSystem @@ -242,9 +242,9 @@ def build_tabs(): [ dbc.NavItem( dbc.NavLink( - page["name"], - id=page["path"], - href=page["path"], + page[ColNames.NAME], + id=page[ColNames.PATH], + href=page[ColNames.PATH], active="exact", className="nav-link", disabled=True, @@ -252,7 +252,7 @@ def build_tabs(): className="custom-tab", ) for page in dash.page_registry.values() - if page["name"] not in ["404", "changelog"] + if page[ColNames.NAME] not in ["404", "changelog"] ], id="tabs", class_name="tab-container", diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index b0f20f1f..ad7d6249 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -156,9 +156,9 @@ def yearly_profile(df, var, global_local, si_ip): if var == ColNames.DBT: # plot ashrae adaptive comfort limits (80%) - lo80 = df.groupby(ColNames.DOY)["adaptive_cmf_80_low"].mean().values - hi80 = df.groupby(ColNames.DOY)["adaptive_cmf_80_up"].mean().values - rmt = df.groupby(ColNames.DOY)["adaptive_cmf_rmt"].mean().values + lo80 = df.groupby(ColNames.DOY)[ColNames.ADAPTIVE_CMF_80_LOW].mean().values + hi80 = df.groupby(ColNames.DOY)[ColNames.ADAPTIVE_CMF_80_UP].mean().values + rmt = df.groupby(ColNames.DOY)[ColNames.ADAPTIVE_CMF_RMT].mean().values # set color https://github.com/CenterForTheBuiltEnvironment/clima/issues/113 implementation var_bar_colors = np.where((rmt > 40) | (rmt < 10), "lightgray", "darkgray") @@ -175,8 +175,8 @@ def yearly_profile(df, var, global_local, si_ip): ) # plot ashrae adaptive comfort limits (90%) - lo90 = df.groupby(ColNames.DOY)["adaptive_cmf_90_low"].mean().values - hi90 = df.groupby(ColNames.DOY)["adaptive_cmf_90_up"].mean().values + lo90 = df.groupby(ColNames.DOY)[ColNames.ADAPTIVE_CMF_90_LOW].mean().values + hi90 = df.groupby(ColNames.DOY)[ColNames.ADAPTIVE_CMF_90_UP].mean().values trace4 = go.Bar( x=df[ColNames.UTC_TIME].dt.date.unique(), @@ -525,12 +525,12 @@ def wind_rose(df, title, month, hour, labels, si_ip): ) # Rename the category in the 'WindDir_bins' column - df_binned["WindDir_bins"] = df_binned["WindDir_bins"].rename({360.0: 0.0}) + df_binned[ColNames.WIND_DIR_BINS] = df_binned[ColNames.WIND_DIR_BINS].rename({360.0: 0.0}) rose = ( - df_binned.groupby(by=["WindSpd_bins", "WindDir_bins"], observed=False) + df_binned.groupby(by=[ColNames.WIND_SPD_BINS, ColNames.WIND_DIR_BINS], observed=False) .size() - .unstack(level="WindSpd_bins") + .unstack(level=ColNames.WIND_SPD_BINS) .fillna(0) .assign(calm=lambda d: calm_count / d.shape[0]) .sort_index(axis=1) From 49d12c95784345b74c415d0f8639b44402549b76 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:45:50 +1000 Subject: [PATCH 53/66] fix: replaced the string with the constants --- pages/lib/global_column_names.py | 10 +++++++++- pages/lib/utils.py | 10 +++++----- pages/select.py | 12 ++++++------ pages/summary.py | 6 +++--- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index c961fa18..86ebb458 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -97,5 +97,13 @@ class ColNames(str, Enum): TIMES = "times" PATH = "path" + FILE_NAME = "filename" WIND_DIR_BINS = "WindDir_bins" - WIND_SPD_BINS = "WindSpd_bins" \ No newline at end of file + WIND_SPD_BINS = "WindSpd_bins" + + TO_IMAGE_BUTTON_OPTIONS = "toImageButtonOptions" + INVERT = "invert" + FEATURES = "features" + GEOMETRY_COORDINATES = "geometry.coordinates" + PROP_ID = "prop_id" + SITE_ELEVATION = "site_elevation" \ No newline at end of file diff --git a/pages/lib/utils.py b/pages/lib/utils.py index b40e39a8..7bc45ef3 100644 --- a/pages/lib/utils.py +++ b/pages/lib/utils.py @@ -34,10 +34,10 @@ def generate_chart_name(tab_name, meta=None, custom_inputs=None, units=None): if units: custom_str += f"_{units}" if meta: - file_name = f"{meta['city']}_{meta['country']}_{tab_name}{custom_str}" - figure_config["toImageButtonOptions"]["filename"] = file_name + file_name = f"{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_{tab_name}{custom_str}" + figure_config[ColNames.TO_IMAGE_BUTTON_OPTIONS][ColNames.FILE_NAME] = file_name else: - figure_config["toImageButtonOptions"]["filename"] = f"{tab_name}{custom_str}" + figure_config[ColNames.TO_IMAGE_BUTTON_OPTIONS][ColNames.FILE_NAME] = f"{tab_name}{custom_str}" return figure_config @@ -260,10 +260,10 @@ def summary_table_tmp_rh_tab(df, value, si_ip): def determine_month_and_hour_filter(month, hour, invert_month, invert_hour): start_month, end_month = month - if invert_month == ["invert"] and (start_month != 1 or end_month != 12): + if invert_month == [ColNames.INVERT] and (start_month != 1 or end_month != 12): end_month, start_month = month start_hour, end_hour = hour - if invert_hour == ["invert"] and (start_hour != 0 or end_hour != 24): + if invert_hour == [ColNames.INVERT] and (start_hour != 0 or end_hour != 24): end_hour, start_hour = hour return start_month, end_month, start_hour, end_hour diff --git a/pages/select.py b/pages/select.py index b4b202cb..714434d9 100644 --- a/pages/select.py +++ b/pages/select.py @@ -140,7 +140,7 @@ def submitted_data( """Process the uploaded file or download the EPW from the URL""" ctx = dash.callback_context - if ctx.triggered[0]["prop_id"] == "modal-yes-button.n_clicks": + if ctx.triggered[0][ColNames.PROP_ID] == "modal-yes-button.n_clicks": lines = get_data(url_store) if lines is None: return ( @@ -162,7 +162,7 @@ def submitted_data( ) elif ( - ctx.triggered[0]["prop_id"] == "upload-data.contents" + ctx.triggered[0][ColNames.PROP_ID] == "upload-data.contents" and list_of_contents is not None ): content_type, content_string = list_of_contents[0].split(",") @@ -275,7 +275,7 @@ def enable_tabs_when_data_is_loaded(meta, data): False, False, False, - "Current Location: " + meta["city"] + ", " + meta["country"], + "Current Location: " + meta[ColNames.CITY] + ", " + meta[ColNames.COUNTRY], ) @@ -330,9 +330,9 @@ def plot_location_epw_files(pathname): with open("./assets/data/epw_location.json", encoding="utf8") as data_file: data = json.load(data_file) - df = json_normalize(data["features"]) - df[["lon", "lat"]] = pd.DataFrame(df["geometry.coordinates"].tolist()) - df["lat"] += 0.010 + df = json_normalize(data[ColNames.FEATURES]) + df[[ColNames.LON, ColNames.LAT]] = pd.DataFrame(df[ColNames.GEOMETRY_COORDINATES].tolist()) + df[ColNames.LAT] += 0.010 df = df.rename(columns={"properties.epw": "Source"}) fig = px.scatter_mapbox( diff --git a/pages/summary.py b/pages/summary.py index 870ccd21..efaa9235 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -228,14 +228,14 @@ def update_location_info(ts, df, meta, si_ip): lon = f"Longitude: {meta[ColNames.LON]}" lat = f"Latitude: {meta[ColNames.LAT]}" - site_elevation = float(meta["site_elevation"]) + site_elevation = float(meta[ColNames.SITE_ELEVATION.SITE_ELEVATION]) site_elevation = round(site_elevation, 2) if si_ip != UnitSystem.SI: site_elevation = site_elevation * 3.281 site_elevation = round(site_elevation, 2) elevation = f"Elevation above sea level: {str(site_elevation)} ft" else: - elevation = f"Elevation above sea level: {meta['site_elevation']} m" + elevation = f"Elevation above sea level: {meta[ColNames.SITE_ELEVATION]} m" period = "" if meta[ColNames.PERIOD]: start, stop = meta[ColNames.PERIOD].split("-") @@ -333,7 +333,7 @@ def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ ctx = dash.callback_context if ( - ctx.triggered[0]["prop_id"] == "submit-set-points.n_clicks_timestamp" + ctx.triggered[0][ColNames.PROP_ID] == "submit-set-points.n_clicks_timestamp" or n_clicks is None ): hdd_setpoint = hdd_value From 81b2ec8048ce855a6db2512fb51ba7e253f3926e Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:15:59 +1000 Subject: [PATCH 54/66] fix: replaced the string with the constants in outdoor.py --- pages/outdoor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pages/outdoor.py b/pages/outdoor.py index 632256a6..788856ff 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -7,6 +7,7 @@ from config import PageUrls, DocLinks, PageInfo from pages.lib.global_element_ids import ElementIds +from pages.lib.global_column_names import ColNames from pages.lib.global_scheme import ( outdoor_dropdown_names, ) @@ -235,10 +236,10 @@ def update_outdoor_comfort_output(_, df): Description of the best weather condition(s). """ cols = [ - "utci_noSun_Wind_categories", - "utci_noSun_noWind_categories", - "utci_Sun_Wind_categories", - "utci_Sun_noWind_categories", + ColNames.UTCI_NOSUN_WIND_CATEGORIES, + ColNames.UTCI_NOSUN_NOWIND_CATEGORIES, + ColNames.UTCI_SUN_WIND_CATEGORIES, + ColNames.UTCI_SUN_NOWIND_CATEGORIES, ] cols_with_the_highest_number_of_zero = [] highest_count = 0 From f3dca83283ce9b88d932640f287a448ee4a56efb Mon Sep 17 00:00:00 2001 From: federico tartarini Date: Thu, 28 Aug 2025 12:22:46 +1000 Subject: [PATCH 55/66] style: ruff format Refactor multiple files to enhance code clarity by adjusting line breaks, spacing, and indentation. This includes consistent formatting in function calls and variable assignments across various modules. --- pages/explorer.py | 4 +- pages/lib/charts_summary.py | 1 + pages/lib/extract_df.py | 63 +++++++++++++++++++++++++------- pages/lib/global_column_names.py | 15 +++++--- pages/lib/global_scheme.py | 10 +++-- pages/lib/template_graphs.py | 8 +++- pages/lib/utils.py | 15 ++++++-- pages/outdoor.py | 4 +- pages/psy-chart.py | 16 +++++--- pages/select.py | 4 +- pages/summary.py | 13 +++++-- pages/sun.py | 21 ++++++----- pages/t_rh.py | 51 +++++++++++++++++++++++--- pages/wind.py | 62 ++++++++++++++++++++++--------- 14 files changed, 215 insertions(+), 72 deletions(-) diff --git a/pages/explorer.py b/pages/explorer.py index 32ae86db..8573edb3 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -953,5 +953,7 @@ def update_table( & (df[ColNames.HOUR] <= end_hour) ] return summary_table_tmp_rh_tab( - filtered_df[[ColNames.MONTH, ColNames.HOUR, dd_value, ColNames.MONTH_NAMES]], dd_value, si_ip + filtered_df[[ColNames.MONTH, ColNames.HOUR, dd_value, ColNames.MONTH_NAMES]], + dd_value, + si_ip, ) diff --git a/pages/lib/charts_summary.py b/pages/lib/charts_summary.py index 6973564d..d48a0a51 100644 --- a/pages/lib/charts_summary.py +++ b/pages/lib/charts_summary.py @@ -2,6 +2,7 @@ import plotly.express as px from pages.lib.global_column_names import ColNames + def world_map(meta): """Return the world map showing the current location.""" latitude = float(meta[ColNames.LAT]) diff --git a/pages/lib/extract_df.py b/pages/lib/extract_df.py index 451e1b8d..cb4153a3 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -68,7 +68,9 @@ def get_location_info(lst, file_name): # from OneClimaBuilding files extract info about reference years try: - location_info[ColNames.PERIOD] = re.search(r'cord=[\'"]?([^\'" >]+);', lst[5]).group(1) + location_info[ColNames.PERIOD] = re.search( + r'cord=[\'"]?([^\'" >]+);', lst[5] + ).group(1) except AttributeError: pass @@ -94,7 +96,9 @@ def create_df(lst, file_name): # from OneClimaBuilding files extract info about reference years try: - location_info[ColNames.PERIOD] = re.search(r'cord=[\'"]?([^\'" >]+);', lst[5]).group(1) + location_info[ColNames.PERIOD] = re.search( + r'cord=[\'"]?([^\'" >]+);', lst[5] + ).group(1) except AttributeError: pass @@ -165,7 +169,9 @@ def create_df(lst, file_name): # Add in month names month_look_up = {ix + 1: month for ix, month in enumerate(month_lst)} - epw_df[ColNames.MONTH_NAMES] = epw_df[ColNames.MONTH].astype("int").map(month_look_up) + epw_df[ColNames.MONTH_NAMES] = ( + epw_df[ColNames.MONTH].astype("int").map(month_look_up) + ) # Change to int type epw_df[[ColNames.YEAR, ColNames.DAY, ColNames.MONTH, ColNames.HOUR]] = epw_df[ @@ -173,10 +179,17 @@ def create_df(lst, file_name): ].astype(int) # Add in DOY - df_doy = epw_df.groupby([ColNames.MONTH, ColNames.DAY])[ColNames.HOUR].count().reset_index() + df_doy = ( + epw_df.groupby([ColNames.MONTH, ColNames.DAY])[ColNames.HOUR] + .count() + .reset_index() + ) df_doy[ColNames.DOY] = df_doy.index + 1 epw_df = pd.merge( - epw_df, df_doy[[ColNames.MONTH, ColNames.DAY, ColNames.DOY]], on=[ColNames.MONTH, ColNames.DAY], how="left" + epw_df, + df_doy[[ColNames.MONTH, ColNames.DAY, ColNames.DOY]], + on=[ColNames.MONTH, ColNames.DAY], + how="left", ) change_to_float = [ @@ -249,7 +262,9 @@ def create_df(lst, file_name): floor_reflectance, ) mrt_df = pd.DataFrame.from_records(mrt) - mrt_df[ColNames.DELTA_MRT] = mrt_df[ColNames.DELTA_MRT].mask(mrt_df[ColNames.DELTA_MRT] >= 70, 70) + mrt_df[ColNames.DELTA_MRT] = mrt_df[ColNames.DELTA_MRT].mask( + mrt_df[ColNames.DELTA_MRT] >= 70, 70 + ) mrt_df = mrt_df.set_index(epw_df.times) epw_df = epw_df.join(mrt_df) @@ -266,16 +281,28 @@ def create_df(lst, file_name): epw_df[ColNames.WIND_SPEED_UTCI] >= 0, 0.5 ) epw_df[ColNames.UTCI_NO_SUN_WIND] = utci( - epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df[ColNames.WIND_SPEED_UTCI], epw_df[ColNames.RH] + epw_df[ColNames.DBT], + epw_df[ColNames.DBT], + epw_df[ColNames.WIND_SPEED_UTCI], + epw_df[ColNames.RH], ) epw_df[ColNames.UTCI_NO_SUN_NO_WIND] = utci( - epw_df[ColNames.DBT], epw_df[ColNames.DBT], epw_df[ColNames.WIND_SPEED_UTCI_0], epw_df[ColNames.RH] + epw_df[ColNames.DBT], + epw_df[ColNames.DBT], + epw_df[ColNames.WIND_SPEED_UTCI_0], + epw_df[ColNames.RH], ) epw_df[ColNames.UTCI_SUN_WIND] = utci( - epw_df[ColNames.DBT], epw_df[ColNames.MRT], epw_df[ColNames.WIND_SPEED_UTCI], epw_df[ColNames.RH] + epw_df[ColNames.DBT], + epw_df[ColNames.MRT], + epw_df[ColNames.WIND_SPEED_UTCI], + epw_df[ColNames.RH], ) epw_df[ColNames.UTCI_SUN_NO_WIND] = utci( - epw_df[ColNames.DBT], epw_df[ColNames.MRT], epw_df[ColNames.WIND_SPEED_UTCI_0], epw_df[ColNames.RH] + epw_df[ColNames.DBT], + epw_df[ColNames.MRT], + epw_df[ColNames.WIND_SPEED_UTCI_0], + epw_df[ColNames.RH], ) utci_bins = [-999, -40, -27, -13, 0, 9, 26, 32, 38, 46, 999] @@ -330,10 +357,18 @@ def create_df(lst, file_name): ) epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_RMT] = rmt epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_COMFORT] = r[ColNames.TMP_CMF] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_LOW] = r[ColNames.TMP_CMF_80_LOW] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_UP] = r[ColNames.TMP_CMF_80_UP] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_LOW] = r[ColNames.TMP_CMF_90_LOW] - epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_UP] = r[ColNames.TMP_CMF_90_UP] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_LOW] = r[ + ColNames.TMP_CMF_80_LOW + ] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_80_UP] = r[ + ColNames.TMP_CMF_80_UP + ] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_LOW] = r[ + ColNames.TMP_CMF_90_LOW + ] + epw_df.loc[epw_df.DOY == day, ColNames.ADAPTIVE_CMF_90_UP] = r[ + ColNames.TMP_CMF_90_UP + ] return epw_df, location_info diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 86ebb458..e88430c3 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -65,9 +65,15 @@ class ColNames(str, Enum): UTCI_NO_SUN_WIND = "utci_noSun_Wind" # Utci no Sun Wind UTCI_NO_SUN_NO_WIND = "utci_noSun_noWind" # Utci no Sun no Wind UTCI_SUN_WIND_CATEGORIES = "utci_Sun_Wind_categories" # Utci Sun Wind Categories - UTCI_SUN_NOWIND_CATEGORIES = "utci_Sun_noWind_categories" # Utci Sun no Wind Categories - UTCI_NOSUN_WIND_CATEGORIES = "utci_noSun_Wind_categories" # Utci no Sun Wind Categories - UTCI_NOSUN_NOWIND_CATEGORIES = "utci_noSun_noWind_categories" # Utci no Sun no Wind Categories + UTCI_SUN_NOWIND_CATEGORIES = ( + "utci_Sun_noWind_categories" # Utci Sun no Wind Categories + ) + UTCI_NOSUN_WIND_CATEGORIES = ( + "utci_noSun_Wind_categories" # Utci no Sun Wind Categories + ) + UTCI_NOSUN_NOWIND_CATEGORIES = ( + "utci_noSun_noWind_categories" # Utci no Sun no Wind Categories + ) ADAPTIVE_COMFORT = "adaptive_comfort" # Adaptive comfort ADAPTIVE_CMF_80_LOW = "adaptive_cmf_80_low" # Adaptive comfort 80 low ADAPTIVE_CMF_80_UP = "adaptive_cmf_80_up" # Adaptive comfort 80 up @@ -82,7 +88,6 @@ class ColNames(str, Enum): TMP_CMF_90_UP = "tmp_cmf_90_up" CONVERSION_FUNCTION = "conversion_function" - # ==================== Calculation column ==================== FAKE_YEAR = "fake_year" # Fake Year MONTH_NAMES = "month_names" # Month names @@ -106,4 +111,4 @@ class ColNames(str, Enum): FEATURES = "features" GEOMETRY_COORDINATES = "geometry.coordinates" PROP_ID = "prop_id" - SITE_ELEVATION = "site_elevation" \ No newline at end of file + SITE_ELEVATION = "site_elevation" diff --git a/pages/lib/global_scheme.py b/pages/lib/global_scheme.py index 2f984aac..51d51a0d 100644 --- a/pages/lib/global_scheme.py +++ b/pages/lib/global_scheme.py @@ -901,13 +901,17 @@ ] sun_cloud_tab_dropdown_names = { - mapping_dictionary[key][ColNames.NAME]: key for key in variables_sun_cloud_tab_dropdown + mapping_dictionary[key][ColNames.NAME]: key + for key in variables_sun_cloud_tab_dropdown } -dropdown_names = {mapping_dictionary[key][ColNames.NAME]: key for key in variables_dropdown} +dropdown_names = { + mapping_dictionary[key][ColNames.NAME]: key for key in variables_dropdown +} more_variables_dropdown = { - mapping_dictionary[key][ColNames.NAME]: key for key in variables_more_variables_dropdown + mapping_dictionary[key][ColNames.NAME]: key + for key in variables_more_variables_dropdown } sun_cloud_tab_explore_dropdown_names = { diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index ad7d6249..e16ef192 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -525,10 +525,14 @@ def wind_rose(df, title, month, hour, labels, si_ip): ) # Rename the category in the 'WindDir_bins' column - df_binned[ColNames.WIND_DIR_BINS] = df_binned[ColNames.WIND_DIR_BINS].rename({360.0: 0.0}) + df_binned[ColNames.WIND_DIR_BINS] = df_binned[ColNames.WIND_DIR_BINS].rename( + {360.0: 0.0} + ) rose = ( - df_binned.groupby(by=[ColNames.WIND_SPD_BINS, ColNames.WIND_DIR_BINS], observed=False) + df_binned.groupby( + by=[ColNames.WIND_SPD_BINS, ColNames.WIND_DIR_BINS], observed=False + ) .size() .unstack(level=ColNames.WIND_SPD_BINS) .fillna(0) diff --git a/pages/lib/utils.py b/pages/lib/utils.py index 7bc45ef3..2455b144 100644 --- a/pages/lib/utils.py +++ b/pages/lib/utils.py @@ -34,10 +34,14 @@ def generate_chart_name(tab_name, meta=None, custom_inputs=None, units=None): if units: custom_str += f"_{units}" if meta: - file_name = f"{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_{tab_name}{custom_str}" + file_name = ( + f"{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_{tab_name}{custom_str}" + ) figure_config[ColNames.TO_IMAGE_BUTTON_OPTIONS][ColNames.FILE_NAME] = file_name else: - figure_config[ColNames.TO_IMAGE_BUTTON_OPTIONS][ColNames.FILE_NAME] = f"{tab_name}{custom_str}" + figure_config[ColNames.TO_IMAGE_BUTTON_OPTIONS][ColNames.FILE_NAME] = ( + f"{tab_name}{custom_str}" + ) return figure_config @@ -90,7 +94,8 @@ def generate_custom_inputs_explorer( end_month_abbr = month_names[int(end_month)] if var in mapping_dictionary: var_fullname = "".join( - word.capitalize() for word in mapping_dictionary[var][ColNames.NAME].split(" ") + word.capitalize() + for word in mapping_dictionary[var][ColNames.NAME].split(" ") ) else: var_fullname = var @@ -241,7 +246,9 @@ def summary_table_tmp_rh_tab(df, value, si_ip): ) return dash_table.DataTable( columns=[ - {"name": i, "id": i} if i == ColNames.MONTH else {"name": f"{i} ({unit})", "id": i} + {"name": i, "id": i} + if i == ColNames.MONTH + else {"name": f"{i} ({unit})", "id": i} for i in df_summary.columns ], style_table={"overflowX": "auto"}, diff --git a/pages/outdoor.py b/pages/outdoor.py index 788856ff..04380c79 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -54,7 +54,9 @@ def inputs_outdoor_comfort(): options=outdoor_dropdown_names, value="utci_Sun_Wind", ), - html.Div(id=ElementIds.IMAGE_SELECTION, style={"flex": "10%"}), + html.Div( + id=ElementIds.IMAGE_SELECTION, style={"flex": "10%"} + ), ], ), ], diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 70c137c1..54e51ee6 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -173,7 +173,7 @@ def inputs(): children=["Filter Variable:"], style={"flex": "30%"} ), dropdown( - id=ElementIds.PSY_VAR_DROPDOWN , + id=ElementIds.PSY_VAR_DROPDOWN, options=dropdown_names, value=ColNames.RH, style={"flex": "70%"}, @@ -249,7 +249,7 @@ def layout(): State(ElementIds.PSY_HOUR_SLIDER, "value"), State(ElementIds.PSY_MIN_VAL, "value"), State(ElementIds.PSY_MAX_VAL, "value"), - State(ElementIds.PSY_VAR_DROPDOWN , "value"), + State(ElementIds.PSY_VAR_DROPDOWN, "value"), State(ElementIds.ID_PSY_CHART_META_STORE, "data"), State(ElementIds.INVERT_MONTH_PSY, "value"), State(ElementIds.INVERT_HOUR_PSY, "value"), @@ -451,7 +451,9 @@ def update_psych_chart( colorscale=var_color, colorbar=var_colorbar, ), - customdata=np.stack((df[ColNames.RH], df["h"], df[var], df["t_dp"]), axis=-1), + customdata=np.stack( + (df[ColNames.RH], df["h"], df[var], df["t_dp"]), axis=-1 + ), hovertemplate=mapping_dictionary[ColNames.DBT][ColNames.NAME] + ": %{x:.2f}" + mapping_dictionary[ColNames.DBT][si_ip][ColNames.UNIT] @@ -476,8 +478,12 @@ def update_psych_chart( ) ) - xtitle_name = "Temperature" + " " + mapping_dictionary[ColNames.DBT][si_ip][ColNames.UNIT] - ytitle_name = "Humidity Ratio" + " " + mapping_dictionary[ColNames.HR][si_ip][ColNames.UNIT] + xtitle_name = ( + "Temperature" + " " + mapping_dictionary[ColNames.DBT][si_ip][ColNames.UNIT] + ) + ytitle_name = ( + "Humidity Ratio" + " " + mapping_dictionary[ColNames.HR][si_ip][ColNames.UNIT] + ) fig.update_layout(template=template, margin=tight_margins) fig.update_xaxes( title_text=xtitle_name, diff --git a/pages/select.py b/pages/select.py index 714434d9..65805cc4 100644 --- a/pages/select.py +++ b/pages/select.py @@ -331,7 +331,9 @@ def plot_location_epw_files(pathname): data = json.load(data_file) df = json_normalize(data[ColNames.FEATURES]) - df[[ColNames.LON, ColNames.LAT]] = pd.DataFrame(df[ColNames.GEOMETRY_COORDINATES].tolist()) + df[[ColNames.LON, ColNames.LAT]] = pd.DataFrame( + df[ColNames.GEOMETRY_COORDINATES].tolist() + ) df[ColNames.LAT] += 0.010 df = df.rename(columns={"properties.epw": "Source"}) diff --git a/pages/summary.py b/pages/summary.py index efaa9235..03a07843 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -43,7 +43,8 @@ def layout(): @callback( - Output(ElementIds.TAB_TWO_CONTAINER, "children"), [Input(ElementIds.ID_SUMMARY_SI_IP_RADIO_INPUT, "value")] + Output(ElementIds.TAB_TWO_CONTAINER, "children"), + [Input(ElementIds.ID_SUMMARY_SI_IP_RADIO_INPUT, "value")], ) def update_layout(si_ip): if si_ip == UnitSystem.SI: @@ -260,7 +261,9 @@ def update_location_info(ts, df, meta, si_ip): # global horizontal irradiance # Note that the value is divided by 1000, so a corresponding change is made in the unit: total_solar_rad_value = round(df[ColNames.GLOB_HOR_RAD].sum() / 1000, 2) - total_solar_rad_unit = "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip][ColNames.UNIT] + total_solar_rad_unit = ( + "k" + mapping_dictionary[ColNames.GLOB_HOR_RAD][si_ip][ColNames.UNIT] + ) total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" glob_sum = df[ColNames.GLOB_HOR_RAD].sum() @@ -527,11 +530,13 @@ def download_clima_dataframe(n_clicks, df, meta, si_ip): elif df is not None: if si_ip == UnitSystem.SI: return dcc.send_data_frame( - df.to_csv, f"df_{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_Clima_SIunit.csv" + df.to_csv, + f"df_{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_Clima_SIunit.csv", ) else: return dcc.send_data_frame( - df.to_csv, f"df_{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_Clima_IPunit.csv" + df.to_csv, + f"df_{meta[ColNames.CITY]}_{meta[ColNames.COUNTRY]}_Clima_IPunit.csv", ) else: print("df not loaded yet") diff --git a/pages/sun.py b/pages/sun.py index b6663e76..9d6a49bc 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -72,7 +72,7 @@ def sun_path(): style={"width": "10rem"}, ), dropdown( - id= ElementIds.CUSTOM_SUN_VIEW_DROPDOWN, + id=ElementIds.CUSTOM_SUN_VIEW_DROPDOWN, options={ "Spherical": "polar", "Cartesian": "cartesian", @@ -92,7 +92,7 @@ def sun_path(): style={"width": "10rem"}, ), dropdown( - id= ElementIds.CUSTOM_SUN_VAR_DROPDOWN, + id=ElementIds.CUSTOM_SUN_VAR_DROPDOWN, options=sc_dropdown_names, value="None", style={"width": "20rem"}, @@ -102,7 +102,7 @@ def sun_path(): dcc.Loading( type="circle", children=html.Div( - id= ElementIds.CUSTOM_SUNPATH, + id=ElementIds.CUSTOM_SUNPATH, ), ), ], @@ -137,7 +137,7 @@ def explore_daily_heatmap(): ), ], ), - dcc.Loading(type="circle", children=html.Div(id= ElementIds.TAB4_DAILY)), + dcc.Loading(type="circle", children=html.Div(id=ElementIds.TAB4_DAILY)), dcc.Loading( type="circle", children=html.Div(id=ElementIds.TAB4_HEATMAP), @@ -148,7 +148,7 @@ def explore_daily_heatmap(): def static_section(): return html.Div( - id= ElementIds.STATIC_SECTION, + id=ElementIds.STATIC_SECTION, className="container-col full-width", children=[ # ... @@ -160,12 +160,15 @@ def layout(): """Contents of tab four.""" return html.Div( className="container-col", - id= ElementIds.TAB_FOUR_CONTAINER, + id=ElementIds.TAB_FOUR_CONTAINER, children=[sun_path(), static_section(), explore_daily_heatmap()], ) -@callback(Output(ElementIds.STATIC_SECTION, "children"), [Input(ElementIds.ID_SUN_SI_IP_RADIO_INPUT, "value")]) +@callback( + Output(ElementIds.STATIC_SECTION, "children"), + [Input(ElementIds.ID_SUN_SI_IP_RADIO_INPUT, "value")], +) def update_static_section(si_ip): hor_unit = "Wh/m²" if si_ip == UnitSystem.IP: @@ -180,7 +183,7 @@ def update_static_section(si_ip): ), dcc.Loading( type="circle", - children=html.Div(id= ElementIds.MONTHLY_SOLAR), + children=html.Div(id=ElementIds.MONTHLY_SOLAR), ), html.Div( children=title_with_link( @@ -191,7 +194,7 @@ def update_static_section(si_ip): ), dcc.Loading( type="circle", - children=html.Div(id= ElementIds.CLOUD_COVER), + children=html.Div(id=ElementIds.CLOUD_COVER), ), ] diff --git a/pages/t_rh.py b/pages/t_rh.py index 12d89fba..7aff5282 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -148,7 +148,16 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("DryBulbTemperature_daily", meta, units), figure=daily_profile( - df[[ColNames.DBT, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY, ColNames.MONTH]], + df[ + [ + ColNames.DBT, + ColNames.HOUR, + ColNames.UTC_TIME, + ColNames.MONTH_NAMES, + ColNames.DAY, + ColNames.MONTH, + ] + ], ColNames.DBT, global_local, si_ip, @@ -159,7 +168,16 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("RelativeHumidity_daily", meta, units), figure=daily_profile( - df[[ColNames.RH, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY, ColNames.MONTH]], + df[ + [ + ColNames.RH, + ColNames.HOUR, + ColNames.UTC_TIME, + ColNames.MONTH_NAMES, + ColNames.DAY, + ColNames.MONTH, + ] + ], ColNames.RH, global_local, si_ip, @@ -187,7 +205,15 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("DryBulbTemperature_heatmap", meta, units), figure=heatmap( - df[[ColNames.DBT, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY]], + df[ + [ + ColNames.DBT, + ColNames.HOUR, + ColNames.UTC_TIME, + ColNames.MONTH_NAMES, + ColNames.DAY, + ] + ], ColNames.DBT, global_local, si_ip, @@ -198,7 +224,15 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): return dcc.Graph( config=generate_chart_name("RelativeHumidity_heatmap", meta, units), figure=heatmap( - df[[ColNames.RH, ColNames.HOUR, ColNames.UTC_TIME, ColNames.MONTH_NAMES, ColNames.DAY]], + df[ + [ + ColNames.RH, + ColNames.HOUR, + ColNames.UTC_TIME, + ColNames.MONTH_NAMES, + ColNames.DAY, + ] + ], ColNames.RH, global_local, si_ip, @@ -212,10 +246,15 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): Input(ElementIds.ID_T_RH_DF_STORE, "modified_timestamp"), Input(ElementIds.ID_T_RH_DROPDOWN, "value"), ], - [State(ElementIds.ID_T_RH_DF_STORE, "data"), State(ElementIds.ID_T_RH_SI_IP_UNIT_STORE, "data")], + [ + State(ElementIds.ID_T_RH_DF_STORE, "data"), + State(ElementIds.ID_T_RH_SI_IP_UNIT_STORE, "data"), + ], ) def update_table(_, dd_value, df, si_ip): """Update the contents of tab three. Passing in general info (df, meta).""" return summary_table_tmp_rh_tab( - df[[ColNames.MONTH, ColNames.HOUR, dd_value, ColNames.MONTH_NAMES]], dd_value, si_ip + df[[ColNames.MONTH, ColNames.HOUR, dd_value, ColNames.MONTH_NAMES]], + dd_value, + si_ip, ) diff --git a/pages/wind.py b/pages/wind.py index 92cc1284..e917c9b3 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -93,7 +93,8 @@ def seasonal_wind_rose(): ), ), html.P( - className="seasonal-text", id=ElementIds.WINTER_WIND_ROSE_TEXT + className="seasonal-text", + id=ElementIds.WINTER_WIND_ROSE_TEXT, ), ], ), @@ -108,7 +109,8 @@ def seasonal_wind_rose(): ), ), html.P( - className="seasonal-text", id=ElementIds.SPRING_WIND_ROSE_TEXT + className="seasonal-text", + id=ElementIds.SPRING_WIND_ROSE_TEXT, ), ], ), @@ -123,12 +125,13 @@ def seasonal_wind_rose(): dcc.Loading( type="circle", children=html.Div( - id= ElementIds.SUMMER_WIND_ROSE, + id=ElementIds.SUMMER_WIND_ROSE, className="daily-wind-graph", ), ), html.P( - className="seasonal-text", id=ElementIds.SUMMER_WIND_ROSE_TEXT + className="seasonal-text", + id=ElementIds.SUMMER_WIND_ROSE_TEXT, ), ], ), @@ -142,7 +145,10 @@ def seasonal_wind_rose(): className="daily-wind-graph", ), ), - html.P(className="seasonal-text", id=ElementIds.FALL_WIND_ROSE_TEXT), + html.P( + className="seasonal-text", + id=ElementIds.FALL_WIND_ROSE_TEXT, + ), ], ), ], @@ -180,7 +186,10 @@ def daily_wind_rose(): ), ), ), - html.P(className="daily-text", id=ElementIds.MORNING_WIND_ROSE_TEXT), + html.P( + className="daily-text", + id=ElementIds.MORNING_WIND_ROSE_TEXT, + ), ], ), html.Div( @@ -195,7 +204,10 @@ def daily_wind_rose(): ), ), ), - html.P(className="daily-text", id=ElementIds.NOON_WIND_ROSE_TEXT), + html.P( + className="daily-text", + id=ElementIds.NOON_WIND_ROSE_TEXT, + ), ], ), html.Div( @@ -210,7 +222,10 @@ def daily_wind_rose(): ), ), ), - html.P(className="daily-text", id=ElementIds.NIGHT_WIND_ROSE_TEXT), + html.P( + className="daily-text", + id=ElementIds.NIGHT_WIND_ROSE_TEXT, + ), ], ), ], @@ -454,9 +469,13 @@ def update_custom_wind_rose( # Wind Rose Graphs if start_month <= end_month: - df = df.loc[(df[ColNames.MONTH] >= start_month) & (df[ColNames.MONTH] <= end_month)] + df = df.loc[ + (df[ColNames.MONTH] >= start_month) & (df[ColNames.MONTH] <= end_month) + ] else: - df = df.loc[(df[ColNames.MONTH] <= end_month) | (df[ColNames.MONTH] >= start_month)] + df = df.loc[ + (df[ColNames.MONTH] <= end_month) | (df[ColNames.MONTH] >= start_month) + ] if start_hour <= end_hour: df = df.loc[(df[ColNames.HOUR] >= start_hour) & (df[ColNames.HOUR] <= end_hour)] else: @@ -509,25 +528,30 @@ def update_seasonal_graphs(_, df, meta, si_ip): # Text winter_df = df.loc[ - (df[ColNames.MONTH] <= winter_months[1]) | (df[ColNames.MONTH] >= winter_months[0]) + (df[ColNames.MONTH] <= winter_months[1]) + | (df[ColNames.MONTH] >= winter_months[0]) ] query_calm_wind = "wind_speed == 0" winter_total_count = winter_df.shape[0] winter_calm_count = winter_df.query(query_calm_wind).shape[0] spring_df = df.loc[ - (df[ColNames.MONTH] >= spring_months[0]) & (df[ColNames.MONTH] <= spring_months[1]) + (df[ColNames.MONTH] >= spring_months[0]) + & (df[ColNames.MONTH] <= spring_months[1]) ] spring_total_count = spring_df.shape[0] spring_calm_count = spring_df.query(query_calm_wind).shape[0] summer_df = df.loc[ - (df[ColNames.MONTH] >= summer_months[0]) & (df[ColNames.MONTH] <= summer_months[1]) + (df[ColNames.MONTH] >= summer_months[0]) + & (df[ColNames.MONTH] <= summer_months[1]) ] summer_total_count = summer_df.shape[0] summer_calm_count = summer_df.query(query_calm_wind).shape[0] - fall_df = df.loc[(df[ColNames.MONTH] >= fall_months[0]) & (df[ColNames.MONTH] <= fall_months[1])] + fall_df = df.loc[ + (df[ColNames.MONTH] >= fall_months[0]) & (df[ColNames.MONTH] <= fall_months[1]) + ] fall_total_count = fall_df.shape[0] fall_calm_count = fall_df.query(query_calm_wind).shape[0] @@ -623,18 +647,22 @@ def update_daily_graphs(_, df, meta, si_ip): # Text query_calm_wind = "wind_speed == 0" morning_df = df.loc[ - (df[ColNames.HOUR] >= morning_times[0]) & (df[ColNames.HOUR] <= morning_times[1]) + (df[ColNames.HOUR] >= morning_times[0]) + & (df[ColNames.HOUR] <= morning_times[1]) ] morning_total_count = morning_df.shape[0] morning_calm_count = morning_df.query(query_calm_wind).shape[0] noon_df = df.loc[ - (df[ColNames.HOUR] >= morning_times[0]) & (df[ColNames.HOUR] <= morning_times[1]) + (df[ColNames.HOUR] >= morning_times[0]) + & (df[ColNames.HOUR] <= morning_times[1]) ] noon_total_count = noon_df.shape[0] noon_calm_count = noon_df.query(query_calm_wind).shape[0] - night_df = df.loc[(df[ColNames.HOUR] <= night_times[1]) | (df[ColNames.HOUR] >= night_times[0])] + night_df = df.loc[ + (df[ColNames.HOUR] <= night_times[1]) | (df[ColNames.HOUR] >= night_times[0]) + ] night_total_count = night_df.shape[0] night_calm_count = night_df.query(query_calm_wind).shape[0] From 02cd0adcb72c6d0a85e7840fbefbc963fe386674 Mon Sep 17 00:00:00 2001 From: federico tartarini Date: Thu, 28 Aug 2025 12:30:14 +1000 Subject: [PATCH 56/66] fix(layout): Correct label text for yearly chart Update the label text from "Yearly_chart" to "Yearly Chart" for better readability and consistency in the user interface. --- pages/t_rh.py | 2 +- pages/wind.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pages/t_rh.py b/pages/t_rh.py index 7aff5282..92564b67 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -50,7 +50,7 @@ def layout(): children=[ html.Div( children=title_with_link( - text="Yearly_chart", + text="Yearly Chart", id_button="yearly-chart-label", doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), diff --git a/pages/wind.py b/pages/wind.py index e917c9b3..23975ff6 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -75,7 +75,6 @@ def seasonal_wind_rose(): html.Div( children=title_with_link( text="Seasonal Wind Rose", - id_button="seasonal-rose-chart", doc_link=DocLinks.WIND_ROSE, ), ), From 97fbd56d98a89425768154ecec43a745787b075e Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:30:39 +1000 Subject: [PATCH 57/66] fix: updated the mistake for using constant --- pages/summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/summary.py b/pages/summary.py index efaa9235..f420bc67 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -228,7 +228,7 @@ def update_location_info(ts, df, meta, si_ip): lon = f"Longitude: {meta[ColNames.LON]}" lat = f"Latitude: {meta[ColNames.LAT]}" - site_elevation = float(meta[ColNames.SITE_ELEVATION.SITE_ELEVATION]) + site_elevation = float(meta[ColNames.SITE_ELEVATION]) site_elevation = round(site_elevation, 2) if si_ip != UnitSystem.SI: site_elevation = site_elevation * 3.281 From 0c5ff18ca8e50f32fe7e775b00dfc72cdee4c5ad Mon Sep 17 00:00:00 2001 From: federico tartarini Date: Thu, 28 Aug 2025 12:43:41 +1000 Subject: [PATCH 58/66] feat(global): Add seasonal wind rose document ID Introduce a new constant for the seasonal wind rose document ID in global_element_ids.py and update the wind.py to utilize this ID for the corresponding button. --- pages/lib/global_element_ids.py | 1 + pages/wind.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pages/lib/global_element_ids.py b/pages/lib/global_element_ids.py index d36ff4f7..041d5ae7 100644 --- a/pages/lib/global_element_ids.py +++ b/pages/lib/global_element_ids.py @@ -155,6 +155,7 @@ class ElementIds(str, Enum): ID_WIND_META_STORE = "meta-store" ID_WIND_SI_IP_UNIT_STORE = "si-ip-unit-store" ID_WIND_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + SEASONAL_WIND_ROSE_DOC = "seasonal-wind-rose-doc" LOADING_ONE = "loading-1" UPLOAD_DATA = "upload-data" UPLOAD_DATA_BUTTON = "upload-data-button" diff --git a/pages/wind.py b/pages/wind.py index 23975ff6..711f32b5 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -75,6 +75,7 @@ def seasonal_wind_rose(): html.Div( children=title_with_link( text="Seasonal Wind Rose", + id_button=ElementIds.SEASONAL_WIND_ROSE_DOC, doc_link=DocLinks.WIND_ROSE, ), ), From f62dfb4d8c0c80ddfefe99255db1f1044f0f7fa8 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Fri, 29 Aug 2025 16:04:24 +1000 Subject: [PATCH 59/66] Fix: Add a new file called global_id_buttons.py --- pages/lib/global_id_buttons.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 pages/lib/global_id_buttons.py diff --git a/pages/lib/global_id_buttons.py b/pages/lib/global_id_buttons.py new file mode 100644 index 00000000..89807520 --- /dev/null +++ b/pages/lib/global_id_buttons.py @@ -0,0 +1,26 @@ +class IdButtons: + TABLE_EXPLORE = "table-explore" + NV_NORMALIZE = "nv_normalize" + OUTDOOR_COMFORT_NORMALIZE = "outdoor-comfort-normalize" + TABLE_TMP_RH = "table-tmp-rh" + + DAILY_ROSE_CHART = "daily-rose-chart" + CLIMATE_PROFILES_CHART = "climate-profiles-chart" + PSYCHROMETRIC_CHART_CHART = "Psychrometric-Chart-chart" + + MORE_CHARTS_LABEL = "more-charts-label" + DOWNLOAD_BUTTON_LABEL = "download-button-label" + CUSTOM_HEATMAP_CHART_LABEL = "custom-heatmap-chart-label" + + EXPLORE_YEARLY_CHART_LABEL = "explore-yearly-chart-label" + EXPLORE_DAILY_CHART_LABEL = "explore-daily-chart-label" + EXPLORE_HEATMAP_CHART_LABEL = "explore-heatmap-chart-label" + NATURAL_VENTILATION_LABEL = "natural-ventilation-label" + UTCI_CHARTS_LABEL = "utci-charts-label" + SUN_PATH_CHART_LABEL = "sun-path-chart-label" + DAILY_CHART_LABEL = "daily-chart-label" + MONTHLY_CHART_LABEL = "monthly-chart-label" + CLOUD_CHART_LABEL = "cloud-chart-label" + YEARLY_CHART_LABEL = "yearly-chart-label" + HEATMAP_CHART_LABEL = "heatmap-chart-label" + WIND_ROSE_LABEL = "wind-rose-label" \ No newline at end of file From 530037becfc84c44d3bca86cf786ff65f9ae4e56 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Fri, 29 Aug 2025 18:38:25 +1000 Subject: [PATCH 60/66] fix(layout): fixed the class name to avoid can not load Normalize data chart --- pages/lib/global_column_names.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index e88430c3..589e0483 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -1,7 +1,7 @@ from enum import Enum -class ColNames(str, Enum): +class ColNames: # ==================== Time related column ==================== YEAR = "year" # year PERIOD = "period" # period From cf6ded03288fc8197de177ce163f29d7652f52e7 Mon Sep 17 00:00:00 2001 From: Wenshu Lyu Date: Fri, 29 Aug 2025 21:01:51 +1000 Subject: [PATCH 61/66] fix: replaced the string that using Class IdButtons in explorer.py, natural_ventilation.py, psy-chart.py, summary.py, sun.py, wind.py, t-rh.py and outdoor.py --- pages/explorer.py | 13 +++++++------ pages/lib/charts_sun.py | 8 ++++---- pages/lib/global_column_names.py | 1 + pages/lib/global_id_buttons.py | 2 ++ pages/natural_ventilation.py | 5 +++-- pages/outdoor.py | 7 ++++--- pages/psy-chart.py | 3 ++- pages/summary.py | 7 ++++--- pages/sun.py | 12 +++++++----- pages/t_rh.py | 9 +++++---- pages/wind.py | 7 ++++--- 11 files changed, 43 insertions(+), 31 deletions(-) diff --git a/pages/explorer.py b/pages/explorer.py index 8573edb3..2910157d 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -14,6 +14,7 @@ ) from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames +from pages.lib.global_id_buttons import IdButtons from pages.lib.global_scheme import ( fig_config, dropdown_names, @@ -84,7 +85,7 @@ def section_one(): html.Div( children=title_with_link( text="Yearly chart", - id_button="explore-yearly-chart-label", + id_button=IdButtons.EXPLORE_YEARLY_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), @@ -95,7 +96,7 @@ def section_one(): html.Div( children=title_with_link( text="Daily chart", - id_button="explore-daily-chart-label", + id_button=IdButtons.EXPLORE_DAILY_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), @@ -106,7 +107,7 @@ def section_one(): html.Div( children=title_with_link( text="Heatmap chart", - id_button="explore-heatmap-chart-label", + id_button=IdButtons.EXPLORE_HEATMAP_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), @@ -118,7 +119,7 @@ def section_one(): children=title_with_tooltip( text="Descriptive statistics", tooltip_text="count, mean, std, min, max, and percentiles", - id_button="table-explore", + id_button=IdButtons.TABLE_EXPLORE, ), ), html.Div( @@ -215,7 +216,7 @@ def section_two_inputs(): children=title_with_tooltip( text="Customizable heatmap", tooltip_text=None, - id_button="custom-heatmap-chart-label", + id_button=IdButtons.CUSTOM_HEATMAP_CHART_LABEL, ), ), html.Div( @@ -599,7 +600,7 @@ def section_three(): children=title_with_tooltip( text="More charts", tooltip_text=None, - id_button="more-charts-label", + id_button=IdButtons.MORE_CHARTS_LABEL, ), ), section_three_inputs(), diff --git a/pages/lib/charts_sun.py b/pages/lib/charts_sun.py index 17028014..6d979956 100644 --- a/pages/lib/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -248,7 +248,7 @@ def polar_graph(df, meta, global_local, var, si_ip): # draw equinox and sostices for date in pd.to_datetime(["2019-03-21", "2019-06-21", "2019-12-21"]): - times = pd.date_range(date, date + pd.Timedelta("24h"), freq="5min", tz=tz) + times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] @@ -272,7 +272,7 @@ def polar_graph(df, meta, global_local, var, si_ip): # draw sunpath on the 21st of each other month for date in pd.to_datetime(["2019-01-21", "2019-02-21", "2019-4-21", "2019-5-21"]): - times = pd.date_range(date, date + pd.Timedelta("24h"), freq="5min", tz=tz) + times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] @@ -427,7 +427,7 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): # draw equinox and sostices for date in pd.to_datetime(["2019-03-21", "2019-06-21", "2019-12-21"]): - times = pd.date_range(date, date + pd.Timedelta("24h"), freq="5min", tz=tz) + times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) delta = timedelta(days=0, hours=time_zone - 1, minutes=0) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) @@ -451,7 +451,7 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): # draw sunpath on the 21st of each other month for date in pd.to_datetime(["2019-01-21", "2019-02-21", "2019-4-21", "2019-5-21"]): - times = pd.date_range(date, date + pd.Timedelta("24h"), freq="5min", tz=tz) + times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) delta = timedelta(days=0, hours=time_zone - 1, minutes=0) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index 589e0483..c498ea76 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -99,6 +99,7 @@ class ColNames: RANGE = "range" UNIT = "unit" TWENTY_FOUR_HOUR = "24h" + FIVE_MINUTE = "5min" TIMES = "times" PATH = "path" diff --git a/pages/lib/global_id_buttons.py b/pages/lib/global_id_buttons.py index 89807520..eb5546c8 100644 --- a/pages/lib/global_id_buttons.py +++ b/pages/lib/global_id_buttons.py @@ -7,6 +7,8 @@ class IdButtons: DAILY_ROSE_CHART = "daily-rose-chart" CLIMATE_PROFILES_CHART = "climate-profiles-chart" PSYCHROMETRIC_CHART_CHART = "Psychrometric-Chart-chart" + CUSTOM_ROSE_CHART = "custom-rose-chart" + HDD_CDD_CHART = "hdd-cdd-chart" MORE_CHARTS_LABEL = "more-charts-label" DOWNLOAD_BUTTON_LABEL = "download-button-label" diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 073f4250..4e89455d 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -20,6 +20,7 @@ from pages.lib.template_graphs import filter_df_by_month_and_hour from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds +from pages.lib.global_id_buttons import IdButtons from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -67,7 +68,7 @@ def update_layout(si_ip): html.Div( children=title_with_link( text="Natural Ventilation Potential", - id_button="natural-ventilation-label", + id_button=IdButtons.NATURAL_VENTILATION_LABEL, doc_link=DocLinks.NATURAL_VENTILATION, ), ), @@ -102,7 +103,7 @@ def update_layout(si_ip): "If normalized is enabled it calculates the % " "time otherwise it calculates the total number of hours" ), - id_button="nv_normalize", + id_button=IdButtons.NV_NORMALIZE, ), ), ], diff --git a/pages/outdoor.py b/pages/outdoor.py index 04380c79..fd5d3de6 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -8,6 +8,7 @@ from config import PageUrls, DocLinks, PageInfo from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames +from pages.lib.global_id_buttons import IdButtons from pages.lib.global_scheme import ( outdoor_dropdown_names, ) @@ -148,7 +149,7 @@ def outdoor_comfort_chart(): html.Div( children=title_with_link( text="UTCI heatmap chart", - id_button="utci-charts-label", + id_button=IdButtons.UTCI_CHARTS_LABEL, doc_link=DocLinks.UTCI_CHART, ) ), @@ -159,7 +160,7 @@ def outdoor_comfort_chart(): html.Div( children=title_with_link( text="UTCI thermal stress chart", - id_button="utci-charts-label", + id_button=IdButtons.UTCI_CHARTS_LABEL, doc_link=DocLinks.UTCI_CHART, ) ), @@ -190,7 +191,7 @@ def outdoor_comfort_chart(): "If normalized is enabled it calculates the % " "time otherwise it calculates the total number of hours" ), - id_button="outdoor-comfort-normalize", + id_button=IdButtons.OUTDOOR_COMFORT_NORMALIZE, ), ), ], diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 54e51ee6..35978f34 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -14,6 +14,7 @@ from config import PageUrls, DocLinks, PageInfo, UnitSystem from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames +from pages.lib.global_id_buttons import IdButtons from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, @@ -219,7 +220,7 @@ def layout(): html.Div( children=title_with_link( text="Psychrometric Chart", - id_button="Psychrometric-Chart-chart", + id_button=IdButtons.PSYCHROMETRIC_CHART_CHART, doc_link=DocLinks.PSYCHROMETRIC_CHART, ), ), diff --git a/pages/summary.py b/pages/summary.py index 6c2facac..83609288 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -13,6 +13,7 @@ from pages.lib.template_graphs import violin from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds +from pages.lib.global_id_buttons import IdButtons from pages.lib.utils import ( generate_chart_name, generate_units, @@ -73,7 +74,7 @@ def update_layout(si_ip): html.Div( children=title_with_tooltip( text="Download", - id_button="download-button-label", + id_button=IdButtons.DOWNLOAD_BUTTON_LABEL, tooltip_text="Use the following buttons to download either the Clima sourcefile or the EPW file", ), ), @@ -110,7 +111,7 @@ def update_layout(si_ip): html.Div( children=title_with_link( text="Heating and Cooling Degree Days", - id_button="hdd-cdd-chart", + id_button=IdButtons.HDD_CDD_CHART, doc_link=DocLinks.DEGREE_DAYS, ), ), @@ -171,7 +172,7 @@ def update_layout(si_ip): html.Div( children=title_with_link( text="Climate Profiles", - id_button="climate-profiles-chart", + id_button=IdButtons.CLIMATE_PROFILES_CHART, doc_link=DocLinks.CLIMATE_PROFILES, ), ), diff --git a/pages/sun.py b/pages/sun.py index 9d6a49bc..0fcb4d51 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -7,6 +7,8 @@ from dash import html, dcc from dash_extensions.enrich import Output, Input, State, callback +from pages.lib.global_column_names import ColNames +from pages.lib.global_id_buttons import IdButtons from config import PageUrls, DocLinks, PageInfo, UnitSystem from pages.lib.charts_sun import ( monthly_solar, @@ -58,7 +60,7 @@ def sun_path(): html.Div( children=title_with_link( text="Sun path chart", - id_button="sun-path-chart-label", + id_button=IdButtons.SUN_PATH_CHART_LABEL, doc_link=DocLinks.SUN_PATH_DIAGRAM, ), ), @@ -117,7 +119,7 @@ def explore_daily_heatmap(): html.Div( children=title_with_link( text="Daily charts", - id_button="daily-chart-label", + id_button=IdButtons.DAILY_CHART_LABEL, doc_link=DocLinks.CUSTOM_HEATMAP, ), ), @@ -177,7 +179,7 @@ def update_static_section(si_ip): html.Div( children=title_with_link( text="Global and Diffuse Horizontal Solar Radiation (" + hor_unit + ")", - id_button="monthly-chart-label", + id_button=IdButtons.MONTHLY_CHART_LABEL, doc_link=DocLinks.SOLAR_RADIATION, ), ), @@ -188,7 +190,7 @@ def update_static_section(si_ip): html.Div( children=title_with_link( text="Cloud coverage", - id_button="cloud-chart-label", + id_button=IdButtons.CLOUD_CHART_LABEL, doc_link=DocLinks.CLOUD_COVER, ), ), @@ -221,7 +223,7 @@ def monthly_and_cloud_chart(_, df, meta, si_ip): monthly = monthly.update_layout(margin=tight_margins) # Cloud Cover - cover = barchart(df, "tot_sky_cover", [False], [False, "", 3, 7], True, si_ip) + cover = barchart(df, ColNames.TOT_SKY_COVER, [False], [False, "", 3, 7], True, si_ip) cover = cover.update_layout( margin=tight_margins, title="", diff --git a/pages/t_rh.py b/pages/t_rh.py index 92564b67..3d4a8caa 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -5,6 +5,7 @@ from pages.lib.template_graphs import heatmap, yearly_profile, daily_profile from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds +from pages.lib.global_id_buttons import IdButtons from pages.lib.utils import ( generate_chart_name, generate_units, @@ -51,7 +52,7 @@ def layout(): html.Div( children=title_with_link( text="Yearly Chart", - id_button="yearly-chart-label", + id_button=IdButtons.YEARLY_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), @@ -62,7 +63,7 @@ def layout(): html.Div( children=title_with_link( text="Daily chart", - id_button="daily-chart-label", + id_button=IdButtons.DAILY_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), @@ -73,7 +74,7 @@ def layout(): html.Div( children=title_with_link( text="Heatmap chart", - id_button="heatmap-chart-label", + id_button=IdButtons.HEATMAP_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), @@ -85,7 +86,7 @@ def layout(): children=title_with_tooltip( text="Descriptive statistics", tooltip_text="count, mean, std, min, max, and percentiles", - id_button="table-tmp-rh", + id_button=IdButtons.TABLE_TMP_RH, ), ), html.Div( diff --git a/pages/wind.py b/pages/wind.py index 711f32b5..2a691831 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -7,6 +7,7 @@ from pages.lib.global_scheme import month_lst, container_row_center_full from pages.lib.template_graphs import heatmap, wind_rose from pages.lib.global_column_names import ColNames +from pages.lib.global_id_buttons import IdButtons from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -166,7 +167,7 @@ def daily_wind_rose(): html.Div( children=title_with_link( text="Daily Wind Rose", - id_button="daily-rose-chart", + id_button=IdButtons.DAILY_ROSE_CHART, doc_link=DocLinks.WIND_ROSE, ), ), @@ -242,7 +243,7 @@ def custom_wind_rose(): children=title_with_tooltip( text="Customizable Wind Rose", tooltip_text=None, - id_button="custom-rose-chart", + id_button=IdButtons.CUSTOM_ROSE_CHART, ), ), html.Div( @@ -345,7 +346,7 @@ def layout(): html.Div( children=title_with_link( text="Annual Wind Rose", - id_button="wind-rose-label", + id_button=IdButtons.WIND_ROSE_LABEL, doc_link=DocLinks.WIND_ROSE, ), ), From 01d2a8596c6d07cd99dcc6eee7e38919920b2db3 Mon Sep 17 00:00:00 2001 From: Tianchi Liu Date: Fri, 29 Aug 2025 22:39:40 +1000 Subject: [PATCH 62/66] Fix: Updated ids of files global_element_ids.py,layout.py,main.py,select.py --- main.py | 3 ++- pages/lib/global_element_ids.py | 26 +++++++++++++++++++-- pages/lib/layout.py | 41 +++++++++++++++++---------------- pages/select.py | 8 +++---- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/main.py b/main.py index d6fccbfa..d9ceebaf 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ from app import app from pages.lib.layout import banner, footer, build_tabs from config import AppConfig +from pages.lib.global_element_ids import ElementIds server = app.server @@ -22,7 +23,7 @@ # callback for survey alert (dbc.Toast) -@callback(Output("alert-auto", "is_open"), Input("interval-component", "n_intervals")) +@callback(Output(ElementIds.ID_MAIN_ALERT_AUTO, "is_open"), Input(ElementIds.ID_MAIN_INTERVAL_COMPONENT, "n_intervals")) def display_alert(n): return n == 1 diff --git a/pages/lib/global_element_ids.py b/pages/lib/global_element_ids.py index 041d5ae7..c67dea33 100644 --- a/pages/lib/global_element_ids.py +++ b/pages/lib/global_element_ids.py @@ -167,12 +167,12 @@ class ElementIds(str, Enum): MODAL = "modal" ALERT = "alert" ID_SELECT_META_STORE = "meta-store" - LINES_STORE = "lines-store" ID_SELECT_URL_STORE = "url-store" ID_SELECT_DF_STORE = "df-store" ID_SELECT_SI_IP_UNIT_STORE = "si-ip-unit-store" ID_SELECT_SI_IP_RADIO_INPUT = "si-ip-radio-input" - BANNER_SUBTITLE = "banner-subtitle" + ID_SELECT_BANNER_SUBTITLE = "banner-subtitle" + ID_SELECT_LINES_STORE = "lines-store" TAB_TWO_CONTAINER = "tab-two-container" ID_SUMMARY_SI_IP_RADIO_INPUT = "si-ip-radio-input" TAB2_SCE1_CONTAINER = "tab2-sec1-container" @@ -201,3 +201,25 @@ class ElementIds(str, Enum): WIND_PROFILE_GRAPH = "wind-profile-graph" TDB_PROFILE_GRAPH = "tdb-profile-graph" RH_PROFILE_GRAPH = "rh-profile-graph" + ALERT_CONTAINER = "alert-container" + ID_LAYOUT_ALERT_AUTO = "alert-auto" + ID_MAIN_ALERT_AUTO = "alert-auto" + ID_LAYOUT_INTERVAL_COMPONENT = "interval-component" + ID_MAIN_INTERVAL_COMPONENT = "interval-component" + FOOTER_CONTAINER = "footer-container" + BANNER = "banner" + BANNER_TITLE = "banner-title" + ID_LAYOUT_BANNER_SUBTITLE = "banner-subtitle" + ID_LAYOUT_GLOBAL_LOCAL_RADIO_INPUT = "global-local-radio-input" + ID_LAYOUT_SI_IP_RADIO_INPUT = "si-ip-radio-input" + STORE = "store" + ID_LAYOUT_DF_STORE = "df-store" + ID_LAYOUT_META_STORE = "meta-store" + ID_LAYOUT_URL_STORE = "url-store" + ID_LAYOUT_SI_IP_UNIT_STORE = "si-ip-unit-store" + ID_LAYOUT_LINES_STORE = "lines-store" + TABS_CONTAINER = "tabs-container" + TABS_PARENT = "tabs-parent" + TABS ="tabs" + STORE_CONTAINER = "store-container" + TABS_CONTENT = "tabs-content" \ No newline at end of file diff --git a/pages/lib/layout.py b/pages/lib/layout.py index 2ec78075..795f4044 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -5,12 +5,13 @@ from dash_iconify import DashIconify from pages.lib.global_column_names import ColNames from config import DocLinks, UnitSystem +from pages.lib.global_element_ids import ElementIds def alert(): """Alert for survey.""" return html.Div( - id="alert-container", + id=ElementIds.ALERT_CONTAINER, children=[ dbc.Toast( [ @@ -23,7 +24,7 @@ def alert(): ), "! ☀️", ], - id="alert-auto", + id=ElementIds.ID_LAYOUT_ALERT_AUTO, header="CBE Clima User Survey", icon="info", is_open=False, @@ -31,7 +32,7 @@ def alert(): className="survey-alert", style={"position": "fixed", "top": 25, "right": 10, "width": 400}, ), - dcc.Interval(id="interval-component", interval=12 * 1000, n_intervals=0), + dcc.Interval(id=ElementIds.ID_LAYOUT_INTERVAL_COMPONENT, interval=12 * 1000, n_intervals=0), ], ) @@ -41,7 +42,7 @@ def footer(): return dbc.Row( align="center", justify="between", - id="footer-container", + id=ElementIds.FOOTER_CONTAINER, children=[ dbc.Col( children=[ @@ -134,7 +135,7 @@ def footer(): def banner(): """Build the banner at the top of the page.""" return html.Div( - id="banner", + id=ElementIds.BANNER, children=[ dmc.Group( position="apart", @@ -159,12 +160,12 @@ def banner(): dmc.Title( "CBE Clima Tool", order=1, - id="banner-title", + id=ElementIds.BANNER_TITLE, style={"fontSize": "2rem"}, ), dmc.Text( "Current Location:", - id="banner-subtitle", + id=ElementIds.ID_LAYOUT_BANNER_SUBTITLE, size="sm", ), ], @@ -186,7 +187,7 @@ def banner(): style={"textDecoration": "none"}, ), dmc.SegmentedControl( - id="global-local-radio-input", + id=ElementIds.ID_LAYOUT_GLOBAL_LOCAL_RADIO_INPUT, value="local", radius="md", data=[ @@ -195,7 +196,7 @@ def banner(): ], ), dmc.SegmentedControl( - id="si-ip-radio-input", + id=ElementIds.ID_LAYOUT_SI_IP_RADIO_INPUT, value=UnitSystem.SI, radius="md", data=[ @@ -219,23 +220,23 @@ def banner(): def store(): return html.Div( - id="store", + id=ElementIds.STORE, children=[ - dcc.Store(id="df-store", storage_type="session"), - dcc.Store(id="meta-store", storage_type="session"), - dcc.Store(id="url-store", storage_type="session"), - dcc.Store(id="si-ip-unit-store", storage_type="session"), - dcc.Store(id="lines-store", storage_type="session"), + dcc.Store(id=ElementIds.ID_LAYOUT_DF_STORE, storage_type="session"), + dcc.Store(id=ElementIds.ID_LAYOUT_META_STORE, storage_type="session"), + dcc.Store(id=ElementIds.ID_LAYOUT_URL_STORE, storage_type="session"), + dcc.Store(id=ElementIds.ID_LAYOUT_SI_IP_UNIT_STORE, storage_type="session"), + dcc.Store(id=ElementIds.ID_LAYOUT_LINES_STORE, storage_type="session"), ], ) def build_tabs(): return html.Div( - id="tabs-container", + id=ElementIds.TABS_CONTAINER, children=[ html.Div( - id="tabs-parent", + id=ElementIds.TABS_PARENT, className="custom-tabs", children=[ dbc.Nav( @@ -254,7 +255,7 @@ def build_tabs(): for page in dash.page_registry.values() if page[ColNames.NAME] not in ["404", "changelog"] ], - id="tabs", + id=ElementIds.TABS, class_name="tab-container", pills=True, justified=True, @@ -262,11 +263,11 @@ def build_tabs(): ], ), html.Div( - id="store-container", + id=ElementIds.STORE_CONTAINER, children=[ store(), html.Div( - id="tabs-content", + id=ElementIds.TABS_CONTENT, children=[ alert(), # alert can be removed after survey is done dash.page_container, diff --git a/pages/select.py b/pages/select.py index 65805cc4..5020ffc8 100644 --- a/pages/select.py +++ b/pages/select.py @@ -113,7 +113,7 @@ def alert(): @callback( [ Output(ElementIds.ID_SELECT_META_STORE, "data"), - Output(ElementIds.LINES_STORE, "data"), + Output(ElementIds.ID_SELECT_LINES_STORE, "data"), Output(ElementIds.ALERT, "is_open"), Output(ElementIds.ALERT, "children"), Output(ElementIds.ALERT, "color"), @@ -211,10 +211,10 @@ def submitted_data( Output(ElementIds.ID_SELECT_SI_IP_UNIT_STORE, "data"), ], [ - Input(ElementIds.LINES_STORE, "modified_timestamp"), + Input(ElementIds.ID_SELECT_LINES_STORE, "modified_timestamp"), Input(ElementIds.ID_SELECT_SI_IP_RADIO_INPUT, "value"), ], - [State(ElementIds.ID_SELECT_URL_STORE, "data"), State("lines-store", "data")], + [State(ElementIds.ID_SELECT_URL_STORE, "data"), State(ElementIds.ID_SELECT_LINES_STORE, "data")], ) def switch_si_ip(_, si_ip_input, url_store, lines): if lines is not None: @@ -241,7 +241,7 @@ def switch_si_ip(_, si_ip_input, url_store, lines): Output("/explorer", "disabled"), Output("/outdoor", "disabled"), Output("/natural-ventilation", "disabled"), - Output(ElementIds.BANNER_SUBTITLE, "children"), + Output(ElementIds.ID_SELECT_BANNER_SUBTITLE, "children"), ], [ Input(ElementIds.ID_SELECT_META_STORE, "data"), From d5eda1ecc060744e9ebd4dd57df7bcc10c848d0c Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:45:55 +1000 Subject: [PATCH 63/66] fix: fixed code sanitisation - add global tab names python file to store tab name value - replaced the tab_name via using global_tab_names.py - removed unused import: `enum.Enum` - formatted the code style via running ruff and pre-commit --- main.py | 5 +++- pages/explorer.py | 31 +++++++++++++++------ pages/lib/charts_sun.py | 28 ++++++++++++++++--- pages/lib/global_column_names.py | 3 -- pages/lib/global_element_ids.py | 4 +-- pages/lib/global_id_buttons.py | 2 +- pages/lib/global_tab_names.py | 47 ++++++++++++++++++++++++++++++++ pages/lib/layout.py | 6 +++- pages/natural_ventilation.py | 5 ++-- pages/outdoor.py | 9 ++++-- pages/psy-chart.py | 3 +- pages/select.py | 8 ++++-- pages/summary.py | 13 +++++---- pages/sun.py | 21 +++++++++----- pages/t_rh.py | 19 +++++++++---- pages/wind.py | 25 +++++++++-------- 16 files changed, 171 insertions(+), 58 deletions(-) create mode 100644 pages/lib/global_tab_names.py diff --git a/main.py b/main.py index d9ceebaf..e5c23662 100644 --- a/main.py +++ b/main.py @@ -23,7 +23,10 @@ # callback for survey alert (dbc.Toast) -@callback(Output(ElementIds.ID_MAIN_ALERT_AUTO, "is_open"), Input(ElementIds.ID_MAIN_INTERVAL_COMPONENT, "n_intervals")) +@callback( + Output(ElementIds.ID_MAIN_ALERT_AUTO, "is_open"), + Input(ElementIds.ID_MAIN_INTERVAL_COMPONENT, "n_intervals"), +) def display_alert(n): return n == 1 diff --git a/pages/explorer.py b/pages/explorer.py index 2910157d..dde7df5c 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -15,6 +15,7 @@ from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from pages.lib.global_scheme import ( fig_config, dropdown_names, @@ -652,7 +653,9 @@ def update_tab_yearly(_, var, global_local, df, meta, si_ip): custom_inputs = generate_custom_inputs(var) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("yearly_explore", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.YEARLY_EXPLORE, meta, custom_inputs, units + ), figure=yearly_profile(df, var, global_local, si_ip), ) @@ -676,7 +679,9 @@ def update_tab_daily(_, var, global_local, df, meta, si_ip): units = generate_units(si_ip) return ( dcc.Graph( - config=generate_chart_name("daily_explore", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.DAILY_EXPLORE, meta, custom_inputs, units + ), figure=daily_profile(df, var, global_local, si_ip), ), ) @@ -701,7 +706,9 @@ def update_tab_heatmap(_, var, global_local, df, meta, si_ip): units = generate_units(si_ip) return ( dcc.Graph( - config=generate_chart_name("heatmap_explore", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.HEATMAP_EXPLORE, meta, custom_inputs, units + ), figure=heatmap(df, var, global_local, si_ip), ), ) @@ -800,7 +807,9 @@ def update_heatmap( units = generate_units(si_ip) return ( dcc.Graph( - config=generate_chart_name("heatmap", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.HEATMAP, meta, custom_inputs, units + ), figure=heat_map, ), {}, @@ -815,7 +824,7 @@ def update_heatmap( return ( dcc.Graph( - config=generate_chart_name("heatmap", meta, custom_inputs, units), + config=generate_chart_name(TabNames.HEATMAP, meta, custom_inputs, units), figure=heat_map, ), no_display, @@ -900,7 +909,9 @@ def update_more_charts( color="danger", style={"text-align": "center", "marginTop": "2rem"}, ), dcc.Graph( - config=generate_chart_name("scatter", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.SCATTER, meta, custom_inputs, units + ), figure=two, ) else: @@ -908,10 +919,14 @@ def update_more_charts( custom_inputs_two = f"{var_x}-{var_y}" units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("scatter", meta, custom_inputs_three, units), + config=generate_chart_name( + TabNames.SCATTER, meta, custom_inputs_three, units + ), figure=three, ), dcc.Graph( - config=generate_chart_name("scatter", meta, custom_inputs_two, units), + config=generate_chart_name( + TabNames.SCATTER, meta, custom_inputs_two, units + ), figure=two, ) diff --git a/pages/lib/charts_sun.py b/pages/lib/charts_sun.py index 6d979956..c6449ddc 100644 --- a/pages/lib/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -248,7 +248,12 @@ def polar_graph(df, meta, global_local, var, si_ip): # draw equinox and sostices for date in pd.to_datetime(["2019-03-21", "2019-06-21", "2019-12-21"]): - times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) + times = pd.date_range( + date, + date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), + freq=ColNames.FIVE_MINUTE, + tz=tz, + ) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] @@ -272,7 +277,12 @@ def polar_graph(df, meta, global_local, var, si_ip): # draw sunpath on the 21st of each other month for date in pd.to_datetime(["2019-01-21", "2019-02-21", "2019-4-21", "2019-5-21"]): - times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) + times = pd.date_range( + date, + date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), + freq=ColNames.FIVE_MINUTE, + tz=tz, + ) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] @@ -427,7 +437,12 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): # draw equinox and sostices for date in pd.to_datetime(["2019-03-21", "2019-06-21", "2019-12-21"]): - times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) + times = pd.date_range( + date, + date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), + freq=ColNames.FIVE_MINUTE, + tz=tz, + ) delta = timedelta(days=0, hours=time_zone - 1, minutes=0) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) @@ -451,7 +466,12 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): # draw sunpath on the 21st of each other month for date in pd.to_datetime(["2019-01-21", "2019-02-21", "2019-4-21", "2019-5-21"]): - times = pd.date_range(date, date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), freq=ColNames.FIVE_MINUTE, tz=tz) + times = pd.date_range( + date, + date + pd.Timedelta(ColNames.TWENTY_FOUR_HOUR), + freq=ColNames.FIVE_MINUTE, + tz=tz, + ) delta = timedelta(days=0, hours=time_zone - 1, minutes=0) times = times - delta solpos = solarposition.get_solarposition(times, latitude, longitude) diff --git a/pages/lib/global_column_names.py b/pages/lib/global_column_names.py index c498ea76..c2ef2135 100644 --- a/pages/lib/global_column_names.py +++ b/pages/lib/global_column_names.py @@ -1,6 +1,3 @@ -from enum import Enum - - class ColNames: # ==================== Time related column ==================== YEAR = "year" # year diff --git a/pages/lib/global_element_ids.py b/pages/lib/global_element_ids.py index c67dea33..644dcd3e 100644 --- a/pages/lib/global_element_ids.py +++ b/pages/lib/global_element_ids.py @@ -220,6 +220,6 @@ class ElementIds(str, Enum): ID_LAYOUT_LINES_STORE = "lines-store" TABS_CONTAINER = "tabs-container" TABS_PARENT = "tabs-parent" - TABS ="tabs" + TABS = "tabs" STORE_CONTAINER = "store-container" - TABS_CONTENT = "tabs-content" \ No newline at end of file + TABS_CONTENT = "tabs-content" diff --git a/pages/lib/global_id_buttons.py b/pages/lib/global_id_buttons.py index eb5546c8..79e0d918 100644 --- a/pages/lib/global_id_buttons.py +++ b/pages/lib/global_id_buttons.py @@ -25,4 +25,4 @@ class IdButtons: CLOUD_CHART_LABEL = "cloud-chart-label" YEARLY_CHART_LABEL = "yearly-chart-label" HEATMAP_CHART_LABEL = "heatmap-chart-label" - WIND_ROSE_LABEL = "wind-rose-label" \ No newline at end of file + WIND_ROSE_LABEL = "wind-rose-label" diff --git a/pages/lib/global_tab_names.py b/pages/lib/global_tab_names.py new file mode 100644 index 00000000..0e7af9ce --- /dev/null +++ b/pages/lib/global_tab_names.py @@ -0,0 +1,47 @@ +class TabNames: + DAILY = "daily" + DAILY_EXPLORE = "daily_explore" + YEARLY_EXPLORE = "yearly_explore" + + PSY = "psy" + MAP = "map" + HEATMAP = "heatmap" + HEATMAP_CATEGORY = "heatmap_category" + HEATMAP_EXPLORE = "heatmap_explore" + + SCATTER = "scatter" + SUMMARY = "summary" + BARCHART = "barchart" + + HDD_CDD = "hdd_cdd" + WIND_SPEED = "WindSpeed" + CLOUD_COVER = "cloud_cover" + EPW_LOCATION_SELECT = "epw_location_select" + RELATIVE_HUMIDITY = "RelativeHumidity" + RELATIVE_HUMIDITY_HEATMAP = "RelativeHumidity_heatmap" + DRY_BULB_TEMPERATURE = "DryBulbTemperature" + DRY_BULB_TEMPERATURE_HEATMAP = "DryBulbTemperature_heatmap" + + GLOBAL_HORIZONTAL_RADIATION = "GlobalHorizontalRadiation" + GLOBAL_AND_DIFFUSE_HORIZONTAL_SOLAR_RADIATION = ( + "global_and_diffuse_horizontal_solar_radiation" + ) + + SPHERICAL_SUNPATH = "spherical_sunpath" + CARTESIAN_SUNPATH = "cartesian_sunpath" + + DRY_BULB_TEMPERATURE_DAILY = "DryBulbTemperature_daily" + RELATIVE_HUMIDITY_DAILY = "RelativeHumidity_daily" + DRY_BULB_TEMPERATURE_YEARLY = "DryBulbTemperature_yearly" + RELATIVE_HUMIDITY_YEARLY = "RelativeHumidity_yearly" + + ANNUAL_WIND_ROSE = "annual_wind_rose" + WIND_DIRECTION = "wind_direction" + CUSTOM_WIND_ROSE = "custom_wind_rose" + SPRING_WIND_ROSE = "spring_wind_rose" + SUMMER_WIND_ROSE = "summer_wind_rose" + FALL_WIND_ROSE = "fall_wind_rose" + WINTER_WIND_ROSE = "winter_wind_rose" + MORNING_WIND_ROSE = "morning_wind_rose" + NOON_WIND_ROSE = "noon_wind_rose" + NIGHT_WIND_ROSE = "night_wind_rose" diff --git a/pages/lib/layout.py b/pages/lib/layout.py index 795f4044..f6cff013 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -32,7 +32,11 @@ def alert(): className="survey-alert", style={"position": "fixed", "top": 25, "right": 10, "width": 400}, ), - dcc.Interval(id=ElementIds.ID_LAYOUT_INTERVAL_COMPONENT, interval=12 * 1000, n_intervals=0), + dcc.Interval( + id=ElementIds.ID_LAYOUT_INTERVAL_COMPONENT, + interval=12 * 1000, + n_intervals=0, + ), ], ) diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 4e89455d..287512ee 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -21,6 +21,7 @@ from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -446,7 +447,7 @@ def nv_heatmap( ) units = generate_units_degree(si_ip) return dcc.Graph( - config=generate_chart_name("heatmap", meta, custom_inputs, units), + config=generate_chart_name(TabNames.HEATMAP, meta, custom_inputs, units), figure=fig, ) @@ -614,7 +615,7 @@ def nv_bar_chart( ) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("barchart", meta, custom_inputs, units), + config=generate_chart_name(TabNames.BARCHART, meta, custom_inputs, units), figure=fig, ) diff --git a/pages/outdoor.py b/pages/outdoor.py index fd5d3de6..0e4885ad 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -9,6 +9,7 @@ from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from pages.lib.global_scheme import ( outdoor_dropdown_names, ) @@ -295,7 +296,7 @@ def update_tab_utci_value( custom_inputs = f"{var}" units = generate_units_degree(si_ip) return dcc.Graph( - config=generate_chart_name("heatmap", meta, custom_inputs, units), + config=generate_chart_name(TabNames.HEATMAP, meta, custom_inputs, units), figure=heatmap_with_filter( df, var, @@ -393,7 +394,9 @@ def update_tab_utci_category( custom_inputs = f"{var}" units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("heatmap_category", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.HEATMAP_CATEGORY, meta, custom_inputs, units + ), figure=utci_stress_cat, ) @@ -432,6 +435,6 @@ def update_tab_utci_summary_chart( custom_inputs = f"{var}" units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("summary", meta, custom_inputs, units), + config=generate_chart_name(TabNames.SUMMARY, meta, custom_inputs, units), figure=utci_summary_chart, ) diff --git a/pages/psy-chart.py b/pages/psy-chart.py index 35978f34..fc49e8f2 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -15,6 +15,7 @@ from pages.lib.global_element_ids import ElementIds from pages.lib.global_column_names import ColNames from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, @@ -514,5 +515,5 @@ def update_psych_chart( ) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("psy", meta, custom_inputs, units), figure=fig + config=generate_chart_name(TabNames.PSY, meta, custom_inputs, units), figure=fig ) diff --git a/pages/select.py b/pages/select.py index 5020ffc8..86c22d87 100644 --- a/pages/select.py +++ b/pages/select.py @@ -15,6 +15,7 @@ from pages.lib.extract_df import create_df, get_data, get_location_info from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds +from pages.lib.global_tab_names import TabNames from pages.lib.global_scheme import mapping_dictionary from config import PageUrls, PageInfo, UnitSystem from pages.lib.utils import generate_chart_name @@ -214,7 +215,10 @@ def submitted_data( Input(ElementIds.ID_SELECT_LINES_STORE, "modified_timestamp"), Input(ElementIds.ID_SELECT_SI_IP_RADIO_INPUT, "value"), ], - [State(ElementIds.ID_SELECT_URL_STORE, "data"), State(ElementIds.ID_SELECT_LINES_STORE, "data")], + [ + State(ElementIds.ID_SELECT_URL_STORE, "data"), + State(ElementIds.ID_SELECT_LINES_STORE, "data"), + ], ) def switch_si_ip(_, si_ip_input, url_store, lines): if lines is not None: @@ -374,6 +378,6 @@ def plot_location_epw_files(pathname): dcc.Graph( id=ElementIds.TAB_ONE_MAP, figure=fig, - config=generate_chart_name("epw_location_select"), + config=generate_chart_name(TabNames.EPW_LOCATION_SELECT), ), ) diff --git a/pages/summary.py b/pages/summary.py index 83609288..3177e70e 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -14,6 +14,7 @@ from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from pages.lib.utils import ( generate_chart_name, generate_units, @@ -208,7 +209,7 @@ def update_map(meta): """Update the contents of tab two. Passing in the general info (df, meta).""" map_world = dcc.Graph( id=ElementIds.GH_RAD_PROFILE_GRAPH, - config=generate_chart_name("map", meta), + config=generate_chart_name(TabNames.MAP, meta), figure=world_map(meta), ) @@ -417,7 +418,7 @@ def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ chart = dcc.Graph( id=ElementIds.DEGREE_DAYS_CHART, - config=generate_chart_name("hdd_cdd", meta, custom_inputs, units), + config=generate_chart_name(TabNames.HDD_CDD, meta, custom_inputs, units), figure=fig, ) @@ -441,7 +442,7 @@ def update_violin_tdb(ts, global_local, df, meta, si_ip): return dcc.Graph( id=ElementIds.TDB_PROFILE_GRAPH, className="violin-container", - config=generate_chart_name("DryBulbTemperature", meta, units), + config=generate_chart_name(TabNames.DRY_BULB_TEMPERATURE, meta, units), figure=violin(df, ColNames.DBT, global_local, si_ip), ) @@ -464,7 +465,7 @@ def update_tab_wind(ts, global_local, df, meta, si_ip): return dcc.Graph( id=ElementIds.WIND_PROFILE_GRAPH, className="violin-container", - config=generate_chart_name("WindSpeed", meta, units), + config=generate_chart_name(TabNames.WIND_SPEED, meta, units), figure=violin(df, ColNames.WIND_SPEED, global_local, si_ip), ) @@ -487,7 +488,7 @@ def update_tab_rh(ts, global_local, df, meta, si_ip): return dcc.Graph( id=ElementIds.RH_PROFILE_GRAPH, className="violin-container", - config=generate_chart_name("RelativeHumidity", meta, units), + config=generate_chart_name(TabNames.RELATIVE_HUMIDITY, meta, units), figure=violin(df, ColNames.RH, global_local, si_ip), ) @@ -510,7 +511,7 @@ def update_tab_gh_rad(ts, global_local, df, meta, si_ip): return dcc.Graph( id=ElementIds.GH_RAD_PROFILE_GRAPH, className="violin-container", - config=generate_chart_name("GlobalHorizontalRadiation", meta, units), + config=generate_chart_name(TabNames.GLOBAL_HORIZONTAL_RADIATION, meta, units), figure=violin(df, ColNames.GLOB_HOR_RAD, global_local, si_ip), ) diff --git a/pages/sun.py b/pages/sun.py index 0fcb4d51..919655f4 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -9,6 +9,7 @@ from pages.lib.global_column_names import ColNames from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from config import PageUrls, DocLinks, PageInfo, UnitSystem from pages.lib.charts_sun import ( monthly_solar, @@ -223,7 +224,9 @@ def monthly_and_cloud_chart(_, df, meta, si_ip): monthly = monthly.update_layout(margin=tight_margins) # Cloud Cover - cover = barchart(df, ColNames.TOT_SKY_COVER, [False], [False, "", 3, 7], True, si_ip) + cover = barchart( + df, ColNames.TOT_SKY_COVER, [False], [False, "", 3, 7], True, si_ip + ) cover = cover.update_layout( margin=tight_margins, title="", @@ -235,11 +238,11 @@ def monthly_and_cloud_chart(_, df, meta, si_ip): units = generate_units(si_ip) return dcc.Graph( config=generate_chart_name( - "Global_and_Diffuse_Horizontal_Solar_Radiation", meta, units + TabNames.GLOBAL_AND_DIFFUSE_HORIZONTAL_SOLAR_RADIATION, meta, units ), figure=monthly, ), dcc.Graph( - config=generate_chart_name("cloud_cover", meta, units), + config=generate_chart_name(TabNames.CLOUD_COVER, meta, units), figure=cover, ) @@ -264,12 +267,16 @@ def sun_path_chart(_, view, var, global_local, df, meta, si_ip): units = "" if var == "None" else generate_units(si_ip) if view == "polar": return dcc.Graph( - config=generate_chart_name("spherical_sunpath", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.SPHERICAL_SUNPATH, meta, custom_inputs, units + ), figure=polar_graph(df, meta, global_local, var, si_ip), ) else: return dcc.Graph( - config=generate_chart_name("cartesian_sunpath", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.CARTESIAN_SUNPATH, meta, custom_inputs, units + ), figure=custom_cartesian_solar(df, meta, global_local, var, si_ip), ) @@ -292,7 +299,7 @@ def daily(_, var, global_local, df, meta, si_ip): custom_inputs = generate_custom_inputs(var) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("daily", meta, custom_inputs, units), + config=generate_chart_name(TabNames.DAILY, meta, custom_inputs, units), figure=daily_profile(df, var, global_local, si_ip), ) @@ -314,6 +321,6 @@ def update_heatmap(_, var, global_local, df, meta, si_ip): custom_inputs = generate_custom_inputs(var) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("heatmap", meta, custom_inputs, units), + config=generate_chart_name(TabNames.HEATMAP, meta, custom_inputs, units), figure=heatmap(df, var, global_local, si_ip), ) diff --git a/pages/t_rh.py b/pages/t_rh.py index 3d4a8caa..6729a31d 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -6,6 +6,7 @@ from pages.lib.global_column_names import ColNames from pages.lib.global_element_ids import ElementIds from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from pages.lib.utils import ( generate_chart_name, generate_units, @@ -117,7 +118,9 @@ def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): dbt_yearly.update_layout(xaxis=dict(rangeslider=dict(visible=True))) units = generate_units_degree(si_ip) return dcc.Graph( - config=generate_chart_name("DryBulbTemperature_yearly", meta, units), + config=generate_chart_name( + TabNames.DRY_BULB_TEMPERATURE_YEARLY, meta, units + ), figure=dbt_yearly, ) else: @@ -125,7 +128,7 @@ def update_yearly_chart(_, global_local, dd_value, df, meta, si_ip): rh_yearly.update_layout(xaxis=dict(rangeslider=dict(visible=True))) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("RelativeHumidity_yearly", meta, units), + config=generate_chart_name(TabNames.RELATIVE_HUMIDITY_YEARLY, meta, units), figure=rh_yearly, ) @@ -147,7 +150,9 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): if dd_value == dropdown_names[var_to_plot[0]]: units = generate_units_degree(si_ip) return dcc.Graph( - config=generate_chart_name("DryBulbTemperature_daily", meta, units), + config=generate_chart_name( + TabNames.DRY_BULB_TEMPERATURE_DAILY, meta, units + ), figure=daily_profile( df[ [ @@ -167,7 +172,7 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): else: units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("RelativeHumidity_daily", meta, units), + config=generate_chart_name(TabNames.RELATIVE_HUMIDITY_DAILY, meta, units), figure=daily_profile( df[ [ @@ -204,7 +209,9 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): if dd_value == dropdown_names[var_to_plot[0]]: units = generate_units_degree(si_ip) return dcc.Graph( - config=generate_chart_name("DryBulbTemperature_heatmap", meta, units), + config=generate_chart_name( + TabNames.DRY_BULB_TEMPERATURE_HEATMAP, meta, units + ), figure=heatmap( df[ [ @@ -223,7 +230,7 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): else: units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("RelativeHumidity_heatmap", meta, units), + config=generate_chart_name(TabNames.RELATIVE_HUMIDITY_HEATMAP, meta, units), figure=heatmap( df[ [ diff --git a/pages/wind.py b/pages/wind.py index 2a691831..a4f7b948 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -8,6 +8,7 @@ from pages.lib.template_graphs import heatmap, wind_rose from pages.lib.global_column_names import ColNames from pages.lib.global_id_buttons import IdButtons +from pages.lib.global_tab_names import TabNames from pages.lib.utils import ( title_with_tooltip, generate_chart_name, @@ -387,7 +388,7 @@ def update_annual_wind_rose(_, df, meta, si_ip): annual = wind_rose(df, "", [1, 12], [1, 24], True, si_ip) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("annual_wind_rose", meta, units), + config=generate_chart_name(TabNames.ANNUAL_WIND_ROSE, meta, units), figure=annual, ) @@ -412,7 +413,7 @@ def update_tab_wind_speed(_, global_local, df, meta, si_ip): speed = heatmap(df, ColNames.WIND_SPEED, global_local, si_ip) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name(ColNames.WIND_SPEED, meta, units), + config=generate_chart_name(TabNames.WIND_SPEED, meta, units), figure=speed, ) @@ -436,7 +437,7 @@ def update_tab_wind_direction(global_local, df, meta, si_ip): direction = heatmap(df, ColNames.WIND_DIR, global_local, si_ip) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("wind_direction", meta, units), + config=generate_chart_name(TabNames.WIND_DIRECTION, meta, units), figure=direction, ) @@ -489,7 +490,9 @@ def update_custom_wind_rose( ) units = generate_units(si_ip) return dcc.Graph( - config=generate_chart_name("custom_wind_rose", meta, custom_inputs, units), + config=generate_chart_name( + TabNames.CUSTOM_WIND_ROSE, meta, custom_inputs, units + ), figure=custom, ) @@ -592,19 +595,19 @@ def seasonal_chart_caption(month_start, month_end, count, n_calm): units = generate_units(si_ip) return ( dcc.Graph( - config=generate_chart_name("winter_wind_rose", meta, units), + config=generate_chart_name(TabNames.WINTER_WIND_ROSE, meta, units), figure=winter, ), dcc.Graph( - config=generate_chart_name("spring_wind_rose", meta, units), + config=generate_chart_name(TabNames.SPRING_WIND_ROSE, meta, units), figure=spring, ), dcc.Graph( - config=generate_chart_name("summer_wind_rose", meta, units), + config=generate_chart_name(TabNames.SUMMER_WIND_ROSE, meta, units), figure=summer, ), dcc.Graph( - config=generate_chart_name("fall_wind_rose", meta, units), + config=generate_chart_name(TabNames.FALL_WIND_ROSE, meta, units), figure=fall, ), winter_text, @@ -690,15 +693,15 @@ def daily_chart_caption(hour_start, hour_end, count, calm_count): units = generate_units(si_ip) return ( dcc.Graph( - config=generate_chart_name("morning_wind_rose", meta, units), + config=generate_chart_name(TabNames.MORNING_WIND_ROSE, meta, units), figure=morning, ), dcc.Graph( - config=generate_chart_name("noon_wind_rose", meta, units), + config=generate_chart_name(TabNames.NOON_WIND_ROSE, meta, units), figure=noon, ), dcc.Graph( - config=generate_chart_name("night_wind_rose", meta, units), + config=generate_chart_name(TabNames.NIGHT_WIND_ROSE, meta, units), figure=night, ), morning_text, From 965a5379e4f038319691eb115a784d8d1fd8e7a0 Mon Sep 17 00:00:00 2001 From: yluo0664 <154036738+LeoLuosifen@users.noreply.github.com> Date: Sat, 30 Aug 2025 15:08:18 +1000 Subject: [PATCH 64/66] fix: fixed the id_button aligned with IdButtons.* and aligned var/file_var to constants. --- pages/lib/global_id_buttons.py | 1 + pages/natural_ventilation.py | 4 ++-- pages/wind.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pages/lib/global_id_buttons.py b/pages/lib/global_id_buttons.py index 79e0d918..afd4fb88 100644 --- a/pages/lib/global_id_buttons.py +++ b/pages/lib/global_id_buttons.py @@ -5,6 +5,7 @@ class IdButtons: TABLE_TMP_RH = "table-tmp-rh" DAILY_ROSE_CHART = "daily-rose-chart" + SEASONAL_WIND_ROSE_DOC = "seasonal-wind-rose-doc" CLIMATE_PROFILES_CHART = "climate-profiles-chart" PSYCHROMETRIC_CHART_CHART = "Psychrometric-Chart-chart" CUSTOM_ROSE_CHART = "custom-rose-chart" diff --git a/pages/natural_ventilation.py b/pages/natural_ventilation.py index 287512ee..1f5f7ce7 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -500,8 +500,8 @@ def nv_bar_chart( month, hour, invert_month, invert_hour ) - var = "DBT" - filter_var = "DPT" + var = ColNames.DBT + filter_var = ColNames.DPT var_unit = mapping_dictionary[var][si_ip][ColNames.UNIT] filter_unit = mapping_dictionary[filter_var][si_ip][ColNames.UNIT] diff --git a/pages/wind.py b/pages/wind.py index a4f7b948..25c0bd9f 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -77,7 +77,7 @@ def seasonal_wind_rose(): html.Div( children=title_with_link( text="Seasonal Wind Rose", - id_button=ElementIds.SEASONAL_WIND_ROSE_DOC, + id_button=IdButtons.SEASONAL_WIND_ROSE_DOC, doc_link=DocLinks.WIND_ROSE, ), ), From d5223c8c13d1a64a9c28c5a315fc18ff035c3574 Mon Sep 17 00:00:00 2001 From: federico tartarini Date: Mon, 1 Sep 2025 12:53:27 +1000 Subject: [PATCH 65/66] fix(data): Replace string references with ColNames for wind speed Update instances of "wind_speed" to ColNames.WIND_SPEED in multiple files to improve code consistency and maintainability. --- pages/lib/extract_df.py | 4 ++-- pages/lib/global_scheme.py | 6 +++--- pages/lib/template_graphs.py | 2 +- pages/summary.py | 5 +++-- pages/wind.py | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pages/lib/extract_df.py b/pages/lib/extract_df.py index cb4153a3..bda5e380 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -132,7 +132,7 @@ def create_df(lst, file_name): "dif_hor_ill", "Zlumi", "wind_dir", - "wind_speed", + ColNames.WIND_SPEED, "tot_sky_cover", "Oskycover", "Vis", @@ -207,7 +207,7 @@ def create_df(lst, file_name): "dif_hor_ill", "Zlumi", "wind_dir", - "wind_speed", + ColNames.WIND_SPEED, "tot_sky_cover", "Oskycover", "Vis", diff --git a/pages/lib/global_scheme.py b/pages/lib/global_scheme.py index 51d51a0d..0029c946 100644 --- a/pages/lib/global_scheme.py +++ b/pages/lib/global_scheme.py @@ -365,7 +365,7 @@ }, "conversion_function": None, }, - "wind_speed": { + ColNames.WIND_SPEED: { "name": "Wind speed", "color": [ "#D3D3D3", @@ -840,7 +840,7 @@ "dif_hor_ill", "Zlumi", "wind_dir", - "wind_speed", + ColNames.WIND_SPEED, "tot_sky_cover", "Oskycover", "Vis", @@ -862,7 +862,7 @@ "dif_hor_ill", "Zlumi", "wind_dir", - "wind_speed", + ColNames.WIND_SPEED, "tot_sky_cover", "Oskycover", "Vis", diff --git a/pages/lib/template_graphs.py b/pages/lib/template_graphs.py index e16ef192..c57fd22d 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -512,7 +512,7 @@ def wind_rose(df, title, month, hour, labels, si_ip): dir_bins = np.arange(-22.5 / 2, 360 + 22.5, 22.5) dir_labels = (dir_bins[:-1] + dir_bins[1:]) / 2 total_count = df.shape[0] - calm_count = df.query("wind_speed == 0").shape[0] + calm_count = df.query(f"{ColNames.WIND_SPEED} == 0").shape[0] # Create a temporary DataFrame with binned data df_binned = df.assign( diff --git a/pages/summary.py b/pages/summary.py index 3177e70e..de5412d9 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -233,12 +233,13 @@ def update_location_info(ts, df, meta, si_ip): site_elevation = float(meta[ColNames.SITE_ELEVATION]) site_elevation = round(site_elevation, 2) + + elevation = f"Elevation above sea level: {str(site_elevation)} m" if si_ip != UnitSystem.SI: site_elevation = site_elevation * 3.281 site_elevation = round(site_elevation, 2) elevation = f"Elevation above sea level: {str(site_elevation)} ft" - else: - elevation = f"Elevation above sea level: {meta[ColNames.SITE_ELEVATION]} m" + period = "" if meta[ColNames.PERIOD]: start, stop = meta[ColNames.PERIOD].split("-") diff --git a/pages/wind.py b/pages/wind.py index 25c0bd9f..8b744447 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -535,7 +535,7 @@ def update_seasonal_graphs(_, df, meta, si_ip): (df[ColNames.MONTH] <= winter_months[1]) | (df[ColNames.MONTH] >= winter_months[0]) ] - query_calm_wind = "wind_speed == 0" + query_calm_wind = f"{ColNames.WIND_SPEED} == 0" winter_total_count = winter_df.shape[0] winter_calm_count = winter_df.query(query_calm_wind).shape[0] @@ -649,7 +649,7 @@ def update_daily_graphs(_, df, meta, si_ip): night = wind_rose(df, "", months, night_times, True, si_ip) # Text - query_calm_wind = "wind_speed == 0" + query_calm_wind = f"{ColNames.WIND_SPEED} == 0" morning_df = df.loc[ (df[ColNames.HOUR] >= morning_times[0]) & (df[ColNames.HOUR] <= morning_times[1]) From 54d6016c20a5cf808a140ec5aca657fd84c952c0 Mon Sep 17 00:00:00 2001 From: federico tartarini Date: Mon, 1 Sep 2025 12:56:33 +1000 Subject: [PATCH 66/66] fix(layout): Correct capitalization of yearly chart label Update the label for the yearly chart to use consistent capitalization for improved readability and consistency in the UI. --- tests/node/cypress/e2e/spec.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/node/cypress/e2e/spec.cy.js b/tests/node/cypress/e2e/spec.cy.js index dcc84470..8cb41333 100644 --- a/tests/node/cypress/e2e/spec.cy.js +++ b/tests/node/cypress/e2e/spec.cy.js @@ -35,7 +35,7 @@ describe('Clima', () => { // Temperature and Humidity click_tab('Temperature and Humidity'); - cy.contains('Yearly chart'); + cy.contains('Yearly Chart'); cy.contains('Dry bulb temperature (°C)'); // TODO: simulate mouseover cy.contains('Daily chart');