diff --git a/Pipfile.lock b/Pipfile.lock index 26a3f80..15c208f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -133,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", @@ -273,7 +281,7 @@ "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd" ], - "markers": "python_version >= '3.7'", + "markers": "python_version >= '3.9'", "version": "==8.7.0" }, "itsdangerous": { diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 48aa286..2f9007e 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -5,11 +5,72 @@ 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. + +> 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 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 repositories. + +```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 notice 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) @@ -35,6 +96,57 @@ 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/). +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 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 +pipenv install black +``` + +Format your code before committing: + +```bash +black . +``` + +## Testing + +Before submitting a Pull Request, please make sure: +- All tests should pass. +- You have installed project dependencies: + +```bash +npm install + +pipenv 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: @@ -45,6 +157,43 @@ $ 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 Thank you again for being interested in this project! You are awesome! diff --git a/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md b/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md index 3e1b5dc..88dacb9 100644 --- a/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md +++ b/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md @@ -18,3 +18,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). + diff --git a/docs/documentation/tabs-explained/psychrometric-chart/README.md b/docs/documentation/tabs-explained/psychrometric-chart/README.md index 1304110..a0817de 100644 --- a/docs/documentation/tabs-explained/psychrometric-chart/README.md +++ b/docs/documentation/tabs-explained/psychrometric-chart/README.md @@ -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 08ba263..28a3b28 100644 --- a/docs/documentation/tabs-explained/psychrometric-chart/psychrometric-chart-explained.md +++ b/docs/documentation/tabs-explained/psychrometric-chart/psychrometric-chart-explained.md @@ -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 9f354fd..29cbb1d 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/README.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/README.md @@ -26,4 +26,3 @@ This allows the user to identify climatic patterns in relation to the apparent s 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" %} - 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 25755ec..bc250e1 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md @@ -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 34481ad..e226ac9 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 @@ -11,4 +11,3 @@ 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

- 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 fcb336e..1103a96 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 @@ -12,3 +12,4 @@ Typical daily graphs showing the amount of energy gained from the sun have many * 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) + 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 f93f94d..fd87f85 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 diff --git a/docs/documentation/tabs-explained/tab-summary/README.md b/docs/documentation/tabs-explained/tab-summary/README.md index 06a3b3f..8024c92 100644 --- a/docs/documentation/tabs-explained/tab-summary/README.md +++ b/docs/documentation/tabs-explained/tab-summary/README.md @@ -14,4 +14,3 @@ 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" %} - diff --git a/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md b/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md index 2f985e6..0806f43 100644 --- a/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md +++ b/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md @@ -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 1334a7d..51756f3 100644 --- a/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md +++ b/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md @@ -15,4 +15,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 115d98d..0d6d9d0 100644 --- a/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md +++ b/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md @@ -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/main.py b/main.py index d6fccbf..e5c2366 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,10 @@ # 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/explorer.py b/pages/explorer.py index f2c899b..dde7df5 100644 --- a/pages/explorer.py +++ b/pages/explorer.py @@ -12,6 +12,10 @@ two_var_graph, three_var_graph, ) +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, @@ -65,7 +69,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", ), @@ -82,41 +86,41 @@ 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, ), ), 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( text="Daily chart", - id_button="explore-daily-chart-label", + id_button=IdButtons.EXPLORE_DAILY_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( - html.Div(className="full-width", id="query-daily"), + html.Div(className="full-width", id=ElementIds.QUERY_DAILY), type="circle", ), 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, ), ), dcc.Loading( - html.Div(className="full-width", id="query-heatmap"), + html.Div(className="full-width", id=ElementIds.QUERY_HEATMAP), type="circle", ), html.Div( 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( @@ -128,7 +132,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 +144,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 +163,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 +174,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 +193,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 +203,7 @@ def section_one(): ], ), html.Div( - id="table-data-explorer", + id=ElementIds.TABLE_DATA_EXPLORER, ), ], ) @@ -213,7 +217,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( @@ -230,9 +234,9 @@ def section_two_inputs(): style={"flex": "30%"}, ), dropdown( - id="sec2-var-dropdown", + id=ElementIds.SEC2_VAR_DROPDOWN, options=explore_dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -245,7 +249,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 +261,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 +280,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 +291,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 +310,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 +323,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,9 +335,9 @@ 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", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -345,7 +349,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 +365,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 +385,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 +427,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,9 +439,9 @@ 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", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -445,7 +451,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 +466,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 +476,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 +495,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 +506,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 +525,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 +538,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,9 +549,9 @@ 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", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -556,7 +562,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 +577,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, @@ -595,16 +601,16 @@ 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(), 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 +626,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.ID_EXPLORER_DF_STORE, "modified_timestamp"), + Input(ElementIds.SEC1_VAR_DROPDOWN, "value"), + Input(ElementIds.ID_EXPLORER_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.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): @@ -647,22 +653,24 @@ 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), ) @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.ID_EXPLORER_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.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): @@ -671,23 +679,25 @@ 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), ), ) @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.ID_EXPLORER_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.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): @@ -696,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), ), ) @@ -704,31 +716,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.ID_EXPLORER_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.ID_EXPLORER_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_EXPLORE_HEATMAP, "value"), + State(ElementIds.INVERT_HOUR_EXPLORE_HEATMAP, "value"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), ], ) def update_heatmap( @@ -795,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, ), {}, @@ -810,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, @@ -820,27 +834,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.ID_EXPLORER_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.ID_EXPLORER_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_EXPLORE_MORE_CHARTS, "value"), + State(ElementIds.INVERT_HOUR_EXPLORE_MORE_CHARTS, "value"), + State(ElementIds.ID_EXPLORER_SI_IP_UNIT_STORE, "data"), ], ) def update_more_charts( @@ -895,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: @@ -903,28 +919,32 @@ 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, ) @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.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"), + State(ElementIds.INVERT_HOUR_EXPLORE_DESCRIPTIVE, "value"), ], ) def update_table( @@ -943,11 +963,13 @@ 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_data_explorer.py b/pages/lib/charts_data_explorer.py index 2a085bd..c747763 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): @@ -30,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 @@ -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 @@ -111,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 @@ -132,15 +133,15 @@ 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 = ( - 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( @@ -167,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, diff --git a/pages/lib/charts_summary.py b/pages/lib/charts_summary.py index ede7b26..d48a0a5 100644 --- a/pages/lib/charts_summary.py +++ b/pages/lib/charts_summary.py @@ -1,14 +1,15 @@ 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 73d43a2..c6449dd 100644 --- a/pages/lib/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -15,14 +15,19 @@ ) 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 +42,11 @@ 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 +54,14 @@ 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][ColNames.UNIT] + "
" + "Month: %{customdata}
" + "Hour: %{x}:00
" @@ -66,10 +75,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 +86,14 @@ 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][ColNames.UNIT] + "
" + "Month: %{customdata}
" + "Hour: %{x}:00
" @@ -109,16 +120,16 @@ 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"]) - solpos = df.loc[df["apparent_elevation"] > 0, :] + 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": - 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 @@ -134,7 +145,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" @@ -165,19 +176,19 @@ 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"])), - theta=solpos["azimuth"], + r=90 * np.cos(np.radians(90 - solpos[ColNames.APPARENT_ZENITH])), + theta=solpos[ColNames.AZIMUTH], mode="markers", marker_color="orange", marker_size=marker_size, marker_line_width=0, customdata=np.stack( ( - solpos["day"], - solpos["month_names"], - solpos["hour"], - solpos["elevation"], - solpos["azimuth"], + solpos[ColNames.DAY], + solpos[ColNames.MONTH_NAMES], + solpos[ColNames.HOUR], + solpos[ColNames.ELEVATION], + solpos[ColNames.AZIMUTH], ), axis=-1, ), @@ -195,8 +206,8 @@ 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"])), - theta=solpos["azimuth"], + r=90 * np.cos(np.radians(90 - solpos[ColNames.APPARENT_ZENITH])), + theta=solpos[ColNames.AZIMUTH], mode="markers", marker=dict( color=solpos[var], @@ -209,11 +220,11 @@ def polar_graph(df, meta, global_local, var, si_ip): ), customdata=np.stack( ( - solpos["day"], - solpos["month_names"], - solpos["hour"], - solpos["elevation"], - solpos["azimuth"], + solpos[ColNames.DAY], + solpos[ColNames.MONTH_NAMES], + solpos[ColNames.HOUR], + solpos[ColNames.ELEVATION], + solpos[ColNames.AZIMUTH], solpos[var], ), axis=-1, @@ -237,10 +248,15 @@ 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["apparent_elevation"] > 0, :] + solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] fig.add_trace( go.Scatterpolar( @@ -261,10 +277,15 @@ 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["apparent_elevation"] > 0, :] + solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] fig.add_trace( go.Scatterpolar( @@ -312,16 +333,16 @@ 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": - 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 @@ -344,19 +365,19 @@ 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, marker_line_width=0, customdata=np.stack( ( - df["day"], - df["month_names"], - df["hour"], - df["elevation"], - df["azimuth"], + df[ColNames.DAY], + df[ColNames.MONTH_NAMES], + df[ColNames.HOUR], + df[ColNames.ELEVATION], + df[ColNames.AZIMUTH], ), axis=-1, ), @@ -374,8 +395,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], @@ -388,11 +409,11 @@ def custom_cartesian_solar(df, meta, global_local, var, si_ip): ), customdata=np.stack( ( - df["day"], - df["month_names"], - df["hour"], - df["elevation"], - df["azimuth"], + df[ColNames.DAY], + df[ColNames.MONTH_NAMES], + df[ColNames.HOUR], + df[ColNames.ELEVATION], + df[ColNames.AZIMUTH], df[var], ), axis=-1, @@ -416,11 +437,16 @@ 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) - solpos = solpos.loc[solpos["apparent_elevation"] > 0, :] + solpos = solpos.loc[solpos[ColNames.APPARENT_ELEVATION] > 0, :] fig.add_trace( go.Scatter( @@ -440,11 +466,16 @@ 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) - 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 b030a3e..bda5e38 100644 --- a/pages/lib/extract_df.py +++ b/pages/lib/extract_df.py @@ -19,6 +19,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 @@ -67,7 +68,9 @@ 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 @@ -93,7 +96,9 @@ 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 @@ -127,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", @@ -149,33 +154,42 @@ 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"]: - years = epw_df["year"].astype("int").unique() + 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["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 = [ @@ -193,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", @@ -211,24 +225,24 @@ 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) - delta = timedelta(days=0, hours=location_info["time_zone"] - 1, minutes=0) + epw_df[ColNames.UTC_TIME] = pd.to_datetime(times) + 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) # 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 @@ -248,65 +262,79 @@ 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["DBT"] - epw_df["wind_speed_utci"] = epw_df["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["DBT"], epw_df["DBT"], epw_df["wind_speed_utci"], epw_df["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["DBT"], epw_df["DBT"], epw_df["wind_speed_utci_0"], epw_df["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["DBT"], epw_df["MRT"], epw_df["wind_speed_utci"], epw_df["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["DBT"], epw_df["MRT"], epw_df["wind_speed_utci_0"], epw_df["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 + 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( - x=epw_df["utci_noSun_noWind"], bins=utci_bins, labels=utci_labels + 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( - x=epw_df["utci_Sun_Wind"], bins=utci_bins, labels=utci_labels + 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( - x=epw_df["utci_Sun_noWind"], bins=utci_bins, labels=utci_labels + epw_df[ColNames.UTCI_SUN_NOWIND_CATEGORIES] = pd.cut( + x=epw_df[ColNames.UTCI_SUN_NO_WIND], bins=utci_bins, labels=utci_labels ) # 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 - 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: @@ -327,12 +355,20 @@ 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[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 @@ -371,16 +407,16 @@ 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): - 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 new file mode 100644 index 0000000..c2ef213 --- /dev/null +++ b/pages/lib/global_column_names.py @@ -0,0 +1,112 @@ +class ColNames: + # ==================== Time related column ==================== + YEAR = "year" # year + PERIOD = "period" # period + MONTH = "month" # month + DAY = "day" # day + 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 + 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 ==================== + 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 columns ==================== + 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 + 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 + ELEVATION = "elevation" # Elevation + APPARENT_ELEVATION = "apparent_elevation" # Apparent Elevation + APPARENT_ZENITH = "apparent_zenith" # Apparent Zenith + 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 + 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 + 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" + 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" + CONVERSION_FUNCTION = "conversion_function" + + # ==================== 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 + + COLOR = "color" + NAME = "name" + RANGE = "range" + UNIT = "unit" + TWENTY_FOUR_HOUR = "24h" + FIVE_MINUTE = "5min" + TIMES = "times" + + PATH = "path" + FILE_NAME = "filename" + WIND_DIR_BINS = "WindDir_bins" + 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" diff --git a/pages/lib/global_element_ids.py b/pages/lib/global_element_ids.py new file mode 100644 index 0000000..644dcd3 --- /dev/null +++ b/pages/lib/global_element_ids.py @@ -0,0 +1,225 @@ +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" + SEASONAL_WIND_ROSE_DOC = "seasonal-wind-rose-doc" + 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" + 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" + 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" + 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" + DEGREE_DAYS_CHART = "degree-days-chart" + 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" + 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" diff --git a/pages/lib/global_id_buttons.py b/pages/lib/global_id_buttons.py new file mode 100644 index 0000000..afd4fb8 --- /dev/null +++ b/pages/lib/global_id_buttons.py @@ -0,0 +1,29 @@ +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" + 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" + HDD_CDD_CHART = "hdd-cdd-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" diff --git a/pages/lib/global_scheme.py b/pages/lib/global_scheme.py index b71d008..0029c94 100644 --- a/pages/lib/global_scheme.py +++ b/pages/lib/global_scheme.py @@ -1,4 +1,5 @@ import plotly.io as pio +from pages.lib.global_column_names import ColNames from config import UnitSystem @@ -364,7 +365,7 @@ }, "conversion_function": None, }, - "wind_speed": { + ColNames.WIND_SPEED: { "name": "Wind speed", "color": [ "#D3D3D3", @@ -839,7 +840,7 @@ "dif_hor_ill", "Zlumi", "wind_dir", - "wind_speed", + ColNames.WIND_SPEED, "tot_sky_cover", "Oskycover", "Vis", @@ -861,7 +862,7 @@ "dif_hor_ill", "Zlumi", "wind_dir", - "wind_speed", + ColNames.WIND_SPEED, "tot_sky_cover", "Oskycover", "Vis", @@ -900,20 +901,24 @@ ] 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/lib/global_tab_names.py b/pages/lib/global_tab_names.py new file mode 100644 index 0000000..0e7af9c --- /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 558e374..f6cff01 100644 --- a/pages/lib/layout.py +++ b/pages/lib/layout.py @@ -3,14 +3,15 @@ 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 +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,11 @@ 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 +46,7 @@ def footer(): return dbc.Row( align="center", justify="between", - id="footer-container", + id=ElementIds.FOOTER_CONTAINER, children=[ dbc.Col( children=[ @@ -134,7 +139,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 +164,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 +191,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 +200,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,32 +224,32 @@ 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( [ 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,9 +257,9 @@ 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", + id=ElementIds.TABS, class_name="tab-container", pills=True, justified=True, @@ -262,11 +267,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/lib/template_graphs.py b/pages/lib/template_graphs.py index 514b0ad..c57fd22 100644 --- a/pages/lib/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -9,17 +9,17 @@ 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) - var_unit = mapping_dictionary[var][si_ip]["unit"] - var_range = mapping_dictionary[var][si_ip]["range"] - var_name = mapping_dictionary[var]["name"] + 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][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] @@ -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", @@ -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 @@ -107,14 +107,18 @@ 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 +133,18 @@ 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 +154,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)[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") 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 +175,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)[ColNames.ADAPTIVE_CMF_90_LOW].mean().values + hi90 = df.groupby(ColNames.DOY)[ColNames.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,17 +191,17 @@ 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 - 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["UTC_time"].dt.date.unique(), - y=hi_rh_df["hiRH"] - lo_rh_df["loRH"], - base=lo_rh_df["loRH"], + x=df[ColNames.UTC_TIME].dt.date.unique(), + 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", @@ -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(["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 +273,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 +296,10 @@ 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, @@ -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 @@ -343,7 +355,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 +376,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 @@ -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 @@ -423,13 +435,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 +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["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][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) @@ -496,25 +512,29 @@ 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( 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 ), ) # 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) @@ -618,7 +638,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 +651,15 @@ 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) @@ -714,12 +737,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] @@ -740,13 +763,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 +853,19 @@ 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 diff --git a/pages/lib/utils.py b/pages/lib/utils.py index 860a040..2455b14 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): @@ -33,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['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 @@ -54,7 +59,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: @@ -89,14 +94,15 @@ 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 @@ -120,14 +126,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 @@ -215,13 +221,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,18 +235,20 @@ 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]) unit = ( - mapping_dictionary[value][si_ip]["unit"] + mapping_dictionary[value][si_ip][ColNames.UNIT] .replace("", "") .replace("", "") ) 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"}, @@ -259,10 +267,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/natural_ventilation.py b/pages/natural_ventilation.py index 32c62cd..1f5f7ce 100644 --- a/pages/natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -18,6 +18,10 @@ 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_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, @@ -40,14 +44,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_VENTILATION_SI_IP_RADIO_INPUT, "value")], +) def update_layout(si_ip): if si_ip == UnitSystem.IP: tdb_set_min = 50 @@ -62,14 +69,14 @@ 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, ), ), 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", @@ -82,7 +89,7 @@ def update_layout(si_ip): {"label": "", "value": 1}, ], value=[1], - id="switches-input", + id=ElementIds.SWITCHES_INPUT, switch=True, style={ "padding": "1rem", @@ -97,14 +104,14 @@ 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, ), ), ], ), dcc.Loading( html.Div( - id="nv-bar-chart", + id=ElementIds.NV_BAR_CHART, style={"marginTop": "1rem"}, ), type="circle", @@ -122,7 +129,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, ), @@ -132,7 +139,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, @@ -146,7 +153,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, @@ -163,7 +170,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, ), @@ -173,7 +180,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, @@ -192,7 +199,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%"}, ), ], @@ -203,7 +210,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, @@ -222,7 +229,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%"}, ), ], @@ -235,7 +242,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, @@ -253,7 +260,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, @@ -263,7 +270,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, @@ -279,26 +286,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_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_VENTILATION_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_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_VENTILATION_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_NV, "value"), + State(ElementIds.INVERT_HOUR_NV, "value"), + State(ElementIds.ID_NATURAL_VENTILATION_SI_IP_UNIT_STORE, "data"), ], ) def nv_heatmap( @@ -326,8 +333,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 +342,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" @@ -351,17 +358,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 @@ -386,15 +393,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 @@ -440,32 +447,32 @@ 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, ) @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_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.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_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_VENTILATION_META_STORE, "data"), + State(ElementIds.INVERT_MONTH_NV, "value"), + State(ElementIds.INVERT_HOUR_NV, "value"), + State(ElementIds.ID_NATURAL_VENTILATION_SI_IP_UNIT_STORE, "data"), ], ) def nv_bar_chart( @@ -493,36 +500,40 @@ 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]["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" - 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" ) # 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)[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["UTC_time"].dt.month)["nv_allowed"] + df.dropna(subset=ColNames.NV_ALLOWED) + .groupby(df[ColNames.UTC_TIME].dt.month)[ColNames.NV_ALLOWED] .sum() .values ) @@ -532,7 +543,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 +565,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, @@ -604,12 +615,15 @@ 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, ) -@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 diff --git a/pages/outdoor.py b/pages/outdoor.py index ab8d53e..0e4885a 100644 --- a/pages/outdoor.py +++ b/pages/outdoor.py @@ -6,6 +6,10 @@ import numpy as np 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_tab_names import TabNames from pages.lib.global_scheme import ( outdoor_dropdown_names, ) @@ -47,12 +51,14 @@ 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%"} + ), ], ), ], @@ -67,7 +73,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, ), @@ -77,7 +83,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, @@ -96,7 +102,7 @@ def inputs_outdoor_comfort(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-outdoor-comfort", + id=ElementIds.INVERT_MONTH_OUTDOOR_COMFORT, labelStyle={"flex": "30%"}, ), ], @@ -107,7 +113,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, @@ -126,7 +132,7 @@ def inputs_outdoor_comfort(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-outdoor-comfort", + id=ElementIds.INVERT_HOUR_OUTDOOR_COMFORT, labelStyle={"flex": "30%"}, ), ], @@ -140,27 +146,27 @@ 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", - id_button="utci-charts-label", + id_button=IdButtons.UTCI_CHARTS_LABEL, doc_link=DocLinks.UTCI_CHART, ) ), dcc.Loading( - html.Div(id="utci-heatmap"), + html.Div(id=ElementIds.UTCI_HEATMAP), type="circle", ), 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, ) ), dcc.Loading( - html.Div(id="utci-category-heatmap"), + html.Div(id=ElementIds.UTCI_CATEGORY_HEATMAP), type="circle", ), html.Div( @@ -171,7 +177,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", @@ -186,13 +192,13 @@ 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, ), ), ], ), dcc.Loading( - html.Div(id="utci-summary-chart"), + html.Div(id=ElementIds.UTCI_SUMMARY_CHART), type="circle", ), ], @@ -212,12 +218,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): @@ -234,10 +240,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 @@ -257,21 +263,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( @@ -290,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, @@ -307,8 +313,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": @@ -324,21 +330,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.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_category( @@ -388,26 +394,28 @@ 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, ) @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( @@ -427,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 2c46b88..fc49e8f 100644 --- a/pages/psy-chart.py +++ b/pages/psy-chart.py @@ -12,6 +12,10 @@ from pythermalcomfort import psychrometrics as psy 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_tab_names import TabNames from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, @@ -71,7 +75,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%"}, @@ -88,7 +92,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, ), @@ -98,7 +102,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, @@ -117,7 +121,7 @@ def inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-month-psy", + id=ElementIds.INVERT_MONTH_PSY, labelStyle={"flex": "30%"}, ), ], @@ -128,7 +132,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, @@ -147,7 +151,7 @@ def inputs(): {"label": "Invert", "value": "invert"}, ], value=[], - id="invert-hour-psy", + id=ElementIds.INVERT_HOUR_PSY, labelStyle={"flex": "30%"}, ), ], @@ -160,7 +164,7 @@ def inputs(): dbc.Button( "Apply filter", color="primary", - id="data-filter", + id=ElementIds.DATA_FILTER, className="mb-2", n_clicks=0, ), @@ -171,9 +175,9 @@ def inputs(): children=["Filter Variable:"], style={"flex": "30%"} ), dropdown( - id="psy-var-dropdown", + id=ElementIds.PSY_VAR_DROPDOWN, options=dropdown_names, - value="RH", + value=ColNames.RH, style={"flex": "70%"}, ), ], @@ -183,7 +187,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, @@ -197,7 +201,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, @@ -217,7 +221,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, ), ), @@ -225,7 +229,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)], ), ), ) @@ -233,25 +237,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( @@ -287,7 +291,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 " @@ -304,25 +308,25 @@ 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["DBT"][si_ip]["range"] - var_range_y = mapping_dictionary["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 - 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" @@ -338,7 +342,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() @@ -368,13 +372,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", @@ -384,16 +388,16 @@ def update_psych_chart( showscale=False, opacity=0.2, ), - hovertemplate=mapping_dictionary["DBT"]["name"] + hovertemplate=mapping_dictionary[ColNames.DBT][ColNames.NAME] + ": %{x:.2f}" - + mapping_dictionary["DBT"]["name"], + + mapping_dictionary[ColNames.DBT][ColNames.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 +441,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,22 +453,24 @@ 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][ColNames.NAME] + ": %{x:.2f}" - + mapping_dictionary["DBT"][si_ip]["unit"] + + mapping_dictionary[ColNames.DBT][si_ip][ColNames.UNIT] + "
" - + mapping_dictionary["RH"]["name"] + + mapping_dictionary[ColNames.RH][ColNames.NAME] + ": %{customdata[0]:.2f}" - + mapping_dictionary["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 @@ -474,8 +480,12 @@ 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][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, @@ -505,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 d4e4af1..86c22d8 100644 --- a/pages/select.py +++ b/pages/select.py @@ -13,6 +13,9 @@ 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_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 @@ -40,19 +43,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 +67,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 +103,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 +113,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.ID_SELECT_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, ) @@ -138,7 +141,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 ( @@ -160,7 +163,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(",") @@ -205,14 +208,17 @@ 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.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("url-store", "data"), State("lines-store", "data")], ) def switch_si_ip(_, si_ip_input, url_store, lines): if lines is not None: @@ -239,11 +245,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.ID_SELECT_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): @@ -273,21 +279,21 @@ 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], ) @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 +308,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 +323,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): @@ -328,9 +334,11 @@ 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( @@ -349,7 +357,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", @@ -368,8 +376,8 @@ 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"), + config=generate_chart_name(TabNames.EPW_LOCATION_SELECT), ), ) diff --git a/pages/summary.py b/pages/summary.py index 733056a..de5412d 100644 --- a/pages/summary.py +++ b/pages/summary.py @@ -11,6 +11,10 @@ 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.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, @@ -33,7 +37,7 @@ def layout(): return html.Div( className="container-col", - id="tab-two-container", + id=ElementIds.TAB_TWO_CONTAINER, children=[ # ], @@ -41,7 +45,8 @@ def layout(): @callback( - Output("tab-two-container", "children"), [Input("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: @@ -53,24 +58,24 @@ 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( 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", ), ), @@ -82,7 +87,7 @@ def update_layout(si_ip): dbc.Button( "Download EPW", color="primary", - id="download-epw-button", + id=ElementIds.DOWN_EPW_BUTTON, ), width="auto", ), @@ -90,14 +95,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, ), @@ -107,7 +112,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, ), ), @@ -115,7 +120,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( [ @@ -127,7 +132,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"}, @@ -142,7 +147,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"}, @@ -151,7 +156,7 @@ def update_layout(si_ip): ), dbc.Col( dbc.Button( - id="submit-set-points", + id=ElementIds.SUBMIT_SET_POINTS, children="Submit", color="primary", ), @@ -163,22 +168,22 @@ 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( text="Climate Profiles", - id_button="climate-profiles-chart", + id_button=IdButtons.CLIMATE_PROFILES_CHART, doc_link=DocLinks.CLIMATE_PROFILES, ), ), 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), ], ), ], @@ -197,14 +202,14 @@ 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", - config=generate_chart_name("map", meta), + id=ElementIds.GH_RAD_PROFILE_GRAPH, + config=generate_chart_name(TabNames.MAP, meta), figure=world_map(meta), ) @@ -212,35 +217,36 @@ 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): """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 = 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['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 = "" @@ -257,28 +263,30 @@ 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][ColNames.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 = ( f"Percentage of diffuse horizontal solar radiation: {diffuse_percentage} %" ) - tmp_unit = mapping_dictionary["DBT"][si_ip]["unit"] + tmp_unit = mapping_dictionary[ColNames.DBT][si_ip][ColNames.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 ) @@ -309,20 +317,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): @@ -331,7 +339,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 @@ -346,14 +354,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) @@ -361,7 +369,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) @@ -410,8 +418,8 @@ 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", - config=generate_chart_name("hdd_cdd", meta, custom_inputs, units), + id=ElementIds.DEGREE_DAYS_CHART, + config=generate_chart_name(TabNames.HDD_CDD, meta, custom_inputs, units), figure=fig, ) @@ -419,103 +427,103 @@ 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, "DBT", global_local, si_ip), + config=generate_chart_name(TabNames.DRY_BULB_TEMPERATURE, meta, units), + figure=violin(df, ColNames.DBT, global_local, 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): """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, "wind_speed", global_local, si_ip), + config=generate_chart_name(TabNames.WIND_SPEED, meta, units), + figure=violin(df, ColNames.WIND_SPEED, global_local, 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, "RH", global_local, si_ip), + config=generate_chart_name(TabNames.RELATIVE_HUMIDITY, meta, units), + figure=violin(df, ColNames.RH, global_local, 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, "glob_hor_rad", global_local, si_ip), + config=generate_chart_name(TabNames.GLOBAL_HORIZONTAL_RADIATION, meta, units), + figure=violin(df, ColNames.GLOB_HOR_RAD, global_local, 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, ) @@ -525,20 +533,22 @@ 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") @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): @@ -550,7 +560,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 diff --git a/pages/sun.py b/pages/sun.py index a77c4c7..919655f 100644 --- a/pages/sun.py +++ b/pages/sun.py @@ -1,4 +1,5 @@ from copy import deepcopy +from pages.lib.global_element_ids import ElementIds import dash import dash_bootstrap_components as dbc @@ -6,6 +7,9 @@ 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 pages.lib.global_tab_names import TabNames from config import PageUrls, DocLinks, PageInfo, UnitSystem from pages.lib.charts_sun import ( monthly_solar, @@ -57,7 +61,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, ), ), @@ -71,7 +75,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 +95,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 +105,7 @@ def sun_path(): dcc.Loading( type="circle", children=html.Div( - id="custom-sunpath", + id=ElementIds.CUSTOM_SUNPATH, ), ), ], @@ -116,7 +120,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, ), ), @@ -129,17 +133,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 +151,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 +163,15 @@ 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: @@ -173,40 +180,40 @@ 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, ), ), dcc.Loading( type="circle", - children=html.Div(id="monthly-solar"), + children=html.Div(id=ElementIds.MONTHLY_SOLAR), ), 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, ), ), 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): @@ -217,7 +224,9 @@ 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="", @@ -229,27 +238,27 @@ 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, ) @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): @@ -258,27 +267,31 @@ 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), ) @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): @@ -286,28 +299,28 @@ 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), ) @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): 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 c3d90f8..6729a31 100644 --- a/pages/t_rh.py +++ b/pages/t_rh.py @@ -1,9 +1,12 @@ import dash from dash_extensions.enrich import Output, Input, State, dcc, html, callback - 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 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, @@ -37,7 +40,7 @@ def layout(): className="text-next-to-input", children=["Select a variable: "] ), dropdown( - id="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]], @@ -49,46 +52,46 @@ def layout(): children=[ html.Div( children=title_with_link( - text="Yearly chart", - id_button="yearly-chart-label", + text="Yearly Chart", + id_button=IdButtons.YEARLY_CHART_LABEL, doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( type="circle", - children=html.Div(id="yearly-chart"), + children=html.Div(id=ElementIds.YEARLY_CHART), ), 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, ), ), dcc.Loading( type="circle", - children=html.Div(id="daily"), + children=html.Div(id=ElementIds.DAILY), ), 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, ), ), dcc.Loading( type="circle", - children=html.Div(id="heatmap"), + children=html.Div(id=ElementIds.HEATMAP), ), html.Div( 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( - id="table-tmp-hum", + id=ElementIds.TABLE_TMP_HUM, ), ], ), @@ -97,58 +100,71 @@ def layout(): @callback( - Output("yearly-chart", "children"), + Output(ElementIds.YEARLY_CHART, "children"), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), - Input("dropdown", "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("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "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): 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( - config=generate_chart_name("DryBulbTemperature_yearly", meta, units), + config=generate_chart_name( + TabNames.DRY_BULB_TEMPERATURE_YEARLY, meta, units + ), 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( - config=generate_chart_name("RelativeHumidity_yearly", meta, units), + config=generate_chart_name(TabNames.RELATIVE_HUMIDITY_YEARLY, meta, units), figure=rh_yearly, ) @callback( - Output("daily", "children"), + Output(ElementIds.DAILY, "children"), [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), - Input("dropdown", "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("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "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): 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[["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, ), @@ -156,10 +172,19 @@ 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[["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, ), @@ -167,16 +192,16 @@ def update_daily(_, global_local, dd_value, df, meta, si_ip): @callback( - [Output("heatmap", "children")], + [Output(ElementIds.HEATMAP, "children")], [ - Input("df-store", "modified_timestamp"), - Input("global-local-radio-input", "value"), - Input("dropdown", "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("df-store", "data"), - State("meta-store", "data"), - State("si-ip-unit-store", "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): @@ -184,10 +209,20 @@ 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[["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, ), @@ -195,10 +230,18 @@ 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[["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, ), @@ -206,15 +249,20 @@ def update_heatmap(_, global_local, dd_value, df, meta, si_ip): @callback( - Output("table-tmp-hum", "children"), + Output(ElementIds.TABLE_TMP_HUM, "children"), + [ + Input(ElementIds.ID_T_RH_DF_STORE, "modified_timestamp"), + Input(ElementIds.ID_T_RH_DROPDOWN, "value"), + ], [ - Input("df-store", "modified_timestamp"), - Input("dropdown", "value"), + State(ElementIds.ID_T_RH_DF_STORE, "data"), + State(ElementIds.ID_T_RH_SI_IP_UNIT_STORE, "data"), ], - [State("df-store", "data"), State("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[["month", "hour", dd_value, "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 0e48d6d..8b74444 100644 --- a/pages/wind.py +++ b/pages/wind.py @@ -1,10 +1,14 @@ import dash from dash import dcc, html from dash_extensions.enrich import Output, Input, State, callback +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 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, @@ -27,14 +31,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, @@ -50,7 +54,7 @@ def sliders(): children=[ html.P("Hour Range"), dcc.RangeSlider( - id="hour-slider", + id=ElementIds.HOUR_SLIDER, min=1, max=24, step=1, @@ -73,7 +77,7 @@ def seasonal_wind_rose(): html.Div( children=title_with_link( text="Seasonal Wind Rose", - id_button="seasonal-rose-chart", + id_button=IdButtons.SEASONAL_WIND_ROSE_DOC, doc_link=DocLinks.WIND_ROSE, ), ), @@ -86,12 +90,13 @@ 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, ), ], ), @@ -101,12 +106,13 @@ 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, ), ], ), @@ -121,12 +127,13 @@ 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, ), ], ), @@ -136,11 +143,14 @@ 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, + ), ], ), ], @@ -153,17 +163,17 @@ 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( text="Daily Wind Rose", - id_button="daily-rose-chart", + id_button=IdButtons.DAILY_ROSE_CHART, doc_link=DocLinks.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( @@ -174,11 +184,14 @@ 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( @@ -189,11 +202,14 @@ 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( @@ -204,11 +220,14 @@ 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, + ), ], ), ], @@ -225,12 +244,12 @@ 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( 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", @@ -243,7 +262,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) }, @@ -260,7 +279,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) }, @@ -282,7 +301,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) }, @@ -299,7 +318,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) }, @@ -314,7 +333,7 @@ def custom_wind_rose(): ), dcc.Loading( type="circle", - children=html.Div(id="custom-wind-rose"), + children=html.Div(id=ElementIds.CUSTOM_WIND_ROSE), ), ], ) @@ -328,23 +347,23 @@ 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, ), ), 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(), @@ -355,12 +374,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): @@ -369,75 +388,75 @@ 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, ) # wind speed @callback( - Output("wind-speed", "children"), + Output(ElementIds.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_WIND_GLOBAL_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): """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(TabNames.WIND_SPEED, meta, units), figure=speed, ) # wind direction @callback( - Output("wind-direction", "children"), + Output(ElementIds.WIND_DIRECTION, "children"), # General [ - Input("global-local-radio-input", "value"), + Input(ElementIds.ID_WIND_GLOBAL_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): """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), + config=generate_chart_name(TabNames.WIND_DIRECTION, meta, units), figure=direction, ) # 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( @@ -452,13 +471,17 @@ 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 ) @@ -467,29 +490,31 @@ 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, ) @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): @@ -507,25 +532,30 @@ 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" + 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] 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] @@ -565,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, @@ -590,19 +620,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): @@ -619,20 +649,24 @@ 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["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] @@ -659,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, diff --git a/tests/node/cypress/e2e/spec.cy.js b/tests/node/cypress/e2e/spec.cy.js index dcc8447..8cb4133 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');