diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6e613412..c6fbd8fa 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,9 +1,9 @@ [bumpversion] -current_version = 0.8.17 +current_version = 0.9.0 commit = True tag = True -[bumpversion:file:my_project/layout.py] +[bumpversion:file:pages/lib/layout.py] search = Version: {current_version} replace = Version: {new_version} diff --git a/.dockerignore b/.dockerignore index 90a83e9d..56153a8a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,16 +2,24 @@ README.md LICENSE Procfile Procfile.windows +Pipfile.lock +Pipfile *.pyo *.pyd __pycache__ .pytest_cache file_system_store .git +.gitbook +.gitbook.yaml docs -test +tests +node_modules +.my_cache assets/data/Region*.kml +assets/data/OneBuilding*.zip +file_system_backend venv .idea @@ -162,10 +170,6 @@ typings/ .serverless ### Python template # Byte-compiled / optimized / DLL files -__pycache__/ -*/__pycache__/* -*.pyc -*.py[cod] *$py.class # C extensions @@ -173,9 +177,7 @@ __pycache__/ # Distribution / packaging .Python -build/ develop-eggs/ -dist/ downloads/ eggs/ .eggs/ diff --git a/.gcloudignore b/.gcloudignore index f7c97aad..1a8d45f8 100644 --- a/.gcloudignore +++ b/.gcloudignore @@ -7,13 +7,16 @@ README.md LICENSE -Procfile -Procfile.windows *.pyo *.pyd __pycache__ .pytest_cache +.gitbook +docs +file_system_backend +tests +.gitbook.yaml +assets/data/OneBuilding*.zip assets/data/Region*.kml -file_system_store -test \ No newline at end of file +file_system_store \ No newline at end of file diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index f1810e28..36aa2465 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -1,5 +1,11 @@ -name: Cypress 🌲 -on: [push, pull_request] +name: Cypress Testing 🌲 +on: + push: + branches: + - development + pull_request: + branches: + - development jobs: cypress: runs-on: ubuntu-latest @@ -10,7 +16,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Build Clima run: |- @@ -37,14 +43,14 @@ jobs: npm run cy:run - name: Archive screenshots - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: cypress-screenshots path: tests/node/cypress/screenshots - name: Archive videos - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: cypress-videos diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e550f2d6..ee39d462 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,46 +1,31 @@ # .github/workflows/deploy.yml name: Deploy 🚀 Clima to Google Cloud Run (☁🏃) + on: push: branches: - main + jobs: deploy: name: Deploying 🚀 Clima runs-on: ubuntu-latest - if: "contains(github.event.head_commit.message, 'bump version')" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Setup python - uses: actions/setup-python@v4 + - id: auth + uses: google-github-actions/auth@v2 with: - python-version: '3.11' - - - name: Export gcloud related env variable - run: export CLOUDSDK_PYTHON="/usr/bin/python3" + credentials_json: ${{ secrets.GCP_SA_KEY_JSON }} - # Build and push image to Google Container Registry - - name: Setting up - uses: google-github-actions/setup-gcloud@v0 + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 with: - version: '318.0.0' - service_account_key: ${{ secrets.GCP_SA_KEY_JSON }} - service_account_email: "federico.tartarini@bears-berkeley.sg" - project_id: clima-316917 + project_id: heat-stress-scale - - name: Building (🏗️) - run: |- + - name: Building (🏗️) and Deploying (🚀) + run: | gcloud builds submit \ - --tag us-docker.pkg.dev/clima-316917/gcr.io/clima - - # Setup gcloud CLI - - name: Deploy (🚀) - uses: google-github-actions/deploy-cloudrun@v1 - with: - service: clima - image: us-docker.pkg.dev/clima-316917/gcr.io/clima - region: us-central1 - credentials: ${{ secrets.GCP_SA_KEY_JSON }} - project_id: clima-316917 \ No newline at end of file + --project=clima-316917 \ + --substitutions=_REPO_NAME="clima",_PROJ_NAME="clima-316917",_IMG_NAME="main",_GCR="us.gcr.io",_REGION="us-central1",_MEMORY="4Gi",_CPU="2" diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 919693a6..32f7e7c3 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -1,5 +1,11 @@ -name: Python 🐍 -on: [push, pull_request] +name: Python Testing 🐍 +on: + push: + branches: + - development + pull_request: + branches: + - development jobs: pytest: runs-on: ubuntu-latest @@ -17,13 +23,14 @@ jobs: pip install pipenv pipenv install --dev - - name: Test Clima + - name: Ruff Check run: |- - pipenv run python -m pytest + pipenv run ruff check . --output-format=github + + - name: Ruff Format (verify) + run: |- + pipenv run ruff format --check . - - name: Run Black - # TODO: Add to dev dependencies: Adding it right now - # bumps other dependencies and the application won't run. + - name: Test Clima run: |- - pip install black - black . --check + pipenv run python -m pytest diff --git a/.gitignore b/.gitignore index 33964f3b..5da7aa3f 100644 --- a/.gitignore +++ b/.gitignore @@ -220,9 +220,6 @@ target/ # Jupyter Notebook .ipynb_checkpoints -# pyenv -.python-version - # celery beat schedule file celerybeat-schedule @@ -279,4 +276,6 @@ bh_unicode_properties.cache # Sublime-github package stores a github token in this file # https://packagecontrol.io/packages/sublime-github -GitHub.sublime-settings \ No newline at end of file +GitHub.sublime-settings + +file_system_backend \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..8a2b92b7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.12.9 + hooks: + # Run the linter. + - id: ruff-check + args: [ --fix ] + # Run the formatter. + - id: ruff-format \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..7c7a975f --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10 \ No newline at end of file diff --git a/Pipfile b/Pipfile index e0fd4b77..91288833 100644 --- a/Pipfile +++ b/Pipfile @@ -4,23 +4,26 @@ verify_ssl = true name = "pypi" [packages] -dash = "==2.14.2" +dash = "==2.15" pvlib = "==0.9.1" -pythermalcomfort = "*" +pythermalcomfort = "==2.9.1" dash-bootstrap-components = "==1.2.0" dash-extensions = "==1.0.7" -dash-mantine-components = "*" -requests = "*" -plotly = "*" -pandas = "*" -numpy = "*" +dash-mantine-components = "==0.12.1" +requests = "==2.32.4" +plotly = "==5.18.0" +pandas = "==2.2.0" +numpy = "==1.26.3" +dash-iconify = "*" +scipy = "==1.12.0" [dev-packages] cleanpy = "*" pytest = "*" bump2version = "*" -pandas = "*" -requests = "*" +black = "*" +ruff = "*" +pre-commit = "*" [requires] python_version = "3.11" diff --git a/Pipfile.lock b/Pipfile.lock index 35bee841..26a3f806 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "869990943a6e4779782ac8e9242dd5d8b8e9a43905f47f2c0c11d7369ad19073" + "sha256": "7214a1158f64483648ecff36a57b61b67020408e0f16770b77d7165a9d6f47e0" }, "pipfile-spec": 6, "requires": { @@ -16,21 +16,13 @@ ] }, "default": { - "ansi2html": { - "hashes": [ - "sha256:29ccdb1e83520d648ebdc9c9544059ea4d424ecc33d3ef723657f7f5a9ae5225", - "sha256:5c6837a13ecc1903aab7a545353312049dfedfe5105362ad3a8d9d207871ec71" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.1" - }, "blinker": { "hashes": [ - "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", - "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" + "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", + "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc" ], - "markers": "python_version >= '3.8'", - "version": "==1.7.0" + "markers": "python_version >= '3.9'", + "version": "==1.9.0" }, "cachelib": { "hashes": [ @@ -42,123 +34,113 @@ }, "certifi": { "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", + "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5" ], - "markers": "python_version >= '3.6'", - "version": "==2023.11.17" + "markers": "python_version >= '3.7'", + "version": "==2025.8.3" }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", + "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", + "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", + "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", + "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", + "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", + "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c", + "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", + "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", + "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", + "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432", + "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", + "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", + "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", + "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", + "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19", + "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", + "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", + "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4", + "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7", + "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312", + "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", + "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", + "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", + "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", + "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", + "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b", + "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", + "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", + "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", + "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", + "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", + "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", + "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc", + "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", + "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", + "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a", + "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40", + "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", + "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", + "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", + "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", + "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", + "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", + "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", + "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", + "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", + "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34", + "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9", + "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", + "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", + "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", + "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b", + "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", + "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942", + "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", + "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", + "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b", + "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", + "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", + "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", + "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", + "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", + "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", + "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", + "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", + "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", + "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", + "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca", + "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", + "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", + "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb", + "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", + "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557", + "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", + "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7", + "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", + "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", + "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.3" }, "click": { "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", + "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b" ], - "markers": "python_version >= '3.7'", - "version": "==8.1.7" + "markers": "python_version >= '3.10'", + "version": "==8.2.1" }, "dash": { "hashes": [ - "sha256:602e7b0047cc9c48a81244377b812e79198678ef759a0039dadfe81023e20e96", - "sha256:8e1a280f1c7be0714825f04786beab41d40752095e116204e64b13ac978b654d" + "sha256:d38891337fc855d5673f75e5346354daa063c4ff45a8a6a21f25e858fcae41c2", + "sha256:df1882bbf613e4ca4372281c8facbeb68e97d76720336b051bf84c75d2de8588" ], "index": "pypi", - "version": "==2.14.2" + "markers": "python_version >= '3.6'", + "version": "==2.15.0" }, "dash-bootstrap-components": { "hashes": [ @@ -166,6 +148,7 @@ "sha256:d7fd69cb2b1e86f9cc4bcee4036302e5d534d0bf102d331b29392a3c355d776a" ], "index": "pypi", + "markers": "python_version >= '3.6' and python_version < '4'", "version": "==1.2.0" }, "dash-core-components": { @@ -181,6 +164,7 @@ "sha256:a582f79debd6d1332b4382b575d77a9cfca220860ef9b17c24541e7e6d77d8eb" ], "index": "pypi", + "markers": "python_version >= '3.8' and python_version < '4'", "version": "==1.0.7" }, "dash-html-components": { @@ -190,6 +174,14 @@ ], "version": "==2.0.0" }, + "dash-iconify": { + "hashes": [ + "sha256:564774be6b11b0ac3a8999b7137c3d17a1d351d69b673aa313c7228eacc9d143", + "sha256:9ab0eda19bb4514e177bf1f8f36947bb9e5b44aba6ce947f22a1cf0f1a45fc14" + ], + "index": "pypi", + "version": "==0.1.2" + }, "dash-mantine-components": { "hashes": [ "sha256:2630bca31cb96d96fb2c4f986e639b9f92d6319aba8cba02f76da6c0d8f5ca48", @@ -207,17 +199,18 @@ }, "dataclass-wizard": { "hashes": [ - "sha256:211f842e5e9a8ace50ba891ef428cd78c82579fb98024f80f3e630ca8d1946f6", - "sha256:49be36ecc64bc5a1e9a35a6bad1d71d33b6b9b06877404931a17c6a3a6dfbb10" + "sha256:4c46591782265058f1148cfd1f54a3a91221e63986fdd04c9d59f4ced61f4424", + "sha256:63751203e54b9b9349212cc185331da73c1adc99c51312575eb73bb5c00c1962" ], - "version": "==0.22.2" + "version": "==0.22.3" }, "editorconfig": { "hashes": [ - "sha256:57f8ce78afcba15c8b18d46b5170848c88d56fd38f05c2ec60dbbfcb8996e89e", - "sha256:6b0851425aa875b08b16789ee0eeadbd4ab59666e9ebe728e526314c4a2e52c1" + "sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82", + "sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745" ], - "version": "==0.12.3" + "markers": "python_version >= '3.9'", + "version": "==0.17.1" }, "flask": { "hashes": [ @@ -237,168 +230,168 @@ }, "h5py": { "hashes": [ - "sha256:012ab448590e3c4f5a8dd0f3533255bc57f80629bf7c5054cf4c87b30085063c", - "sha256:212bb997a91e6a895ce5e2f365ba764debeaef5d2dca5c6fb7098d66607adf99", - "sha256:2381e98af081b6df7f6db300cd88f88e740649d77736e4b53db522d8874bf2dc", - "sha256:2c8e4fda19eb769e9a678592e67eaec3a2f069f7570c82d2da909c077aa94339", - "sha256:3074ec45d3dc6e178c6f96834cf8108bf4a60ccb5ab044e16909580352010a97", - "sha256:3c97d03f87f215e7759a354460fb4b0d0f27001450b18b23e556e7856a0b21c3", - "sha256:43a61b2c2ad65b1fabc28802d133eed34debcc2c8b420cb213d3d4ef4d3e2229", - "sha256:492305a074327e8d2513011fa9fffeb54ecb28a04ca4c4227d7e1e9616d35641", - "sha256:5dfc65ac21fa2f630323c92453cadbe8d4f504726ec42f6a56cf80c2f90d6c52", - "sha256:667fe23ab33d5a8a6b77970b229e14ae3bb84e4ea3382cc08567a02e1499eedd", - "sha256:6c013d2e79c00f28ffd0cc24e68665ea03ae9069e167087b2adb5727d2736a52", - "sha256:781a24263c1270a62cd67be59f293e62b76acfcc207afa6384961762bb88ea03", - "sha256:86df4c2de68257b8539a18646ceccdcf2c1ce6b1768ada16c8dcfb489eafae20", - "sha256:90286b79abd085e4e65e07c1bd7ee65a0f15818ea107f44b175d2dfe1a4674b7", - "sha256:92273ce69ae4983dadb898fd4d3bea5eb90820df953b401282ee69ad648df684", - "sha256:93dd840bd675787fc0b016f7a05fc6efe37312a08849d9dd4053fd0377b1357f", - "sha256:9450464b458cca2c86252b624279115dcaa7260a40d3cb1594bf2b410a2bd1a3", - "sha256:ae2f0201c950059676455daf92700eeb57dcf5caaf71b9e1328e6e6593601770", - "sha256:aece0e2e1ed2aab076c41802e50a0c3e5ef8816d60ece39107d68717d4559824", - "sha256:b963fb772964fc1d1563c57e4e2e874022ce11f75ddc6df1a626f42bd49ab99f", - "sha256:ba9ab36be991119a3ff32d0c7cbe5faf9b8d2375b5278b2aea64effbeba66039", - "sha256:d4682b94fd36ab217352be438abd44c8f357c5449b8995e63886b431d260f3d3", - "sha256:d93adc48ceeb33347eb24a634fb787efc7ae4644e6ea4ba733d099605045c049", - "sha256:f42e6c30698b520f0295d70157c4e202a9e402406f50dc08f5a7bc416b24e52d", - "sha256:fd6f6d1384a9f491732cee233b99cd4bfd6e838a8815cc86722f9d2ee64032af" + "sha256:016e89d3be4c44f8d5e115fab60548e518ecd9efe9fa5c5324505a90773e6f03", + "sha256:0cbd41f4e3761f150aa5b662df991868ca533872c95467216f2bec5fcad84882", + "sha256:1223b902ef0b5d90bcc8a4778218d6d6cd0f5561861611eda59fa6c52b922f4d", + "sha256:2372116b2e0d5d3e5e705b7f663f7c8d96fa79a4052d250484ef91d24d6a08f4", + "sha256:24df6b2622f426857bda88683b16630014588a0e4155cba44e872eb011c4eaed", + "sha256:4f025cf30ae738c4c4e38c7439a761a71ccfcce04c2b87b2a2ac64e8c5171d43", + "sha256:543877d7f3d8f8a9828ed5df6a0b78ca3d8846244b9702e99ed0d53610b583a8", + "sha256:554ef0ced3571366d4d383427c00c966c360e178b5fb5ee5bb31a435c424db0c", + "sha256:573c33ad056ac7c1ab6d567b6db9df3ffc401045e3f605736218f96c1e0490c6", + "sha256:5e59d2136a8b302afd25acdf7a89b634e0eb7c66b1a211ef2d0457853768a2ef", + "sha256:6da62509b7e1d71a7d110478aa25d245dd32c8d9a1daee9d2a42dba8717b047a", + "sha256:6ff2389961ee5872de697054dd5a033b04284afc3fb52dc51d94561ece2c10c6", + "sha256:723a40ee6505bd354bfd26385f2dae7bbfa87655f4e61bab175a49d72ebfc06b", + "sha256:852b81f71df4bb9e27d407b43071d1da330d6a7094a588efa50ef02553fa7ce4", + "sha256:8c497600c0496548810047257e36360ff551df8b59156d3a4181072eed47d8ad", + "sha256:aa4b7bbce683379b7bf80aaba68e17e23396100336a8d500206520052be2f812", + "sha256:ae18e3de237a7a830adb76aaa68ad438d85fe6e19e0d99944a3ce46b772c69b3", + "sha256:bf4897d67e613ecf5bdfbdab39a1158a64df105827da70ea1d90243d796d367f", + "sha256:ccbe17dc187c0c64178f1a10aa274ed3a57d055117588942b8a08793cc448216", + "sha256:d2744b520440a996f2dae97f901caa8a953afc055db4673a993f2d87d7f38713", + "sha256:d90e6445ab7c146d7f7981b11895d70bc1dd91278a4f9f9028bc0c95e4a53f13", + "sha256:e0045115d83272090b0717c555a31398c2c089b87d212ceba800d3dc5d952e23", + "sha256:e8cbaf6910fa3983c46172666b0b8da7b7bd90d764399ca983236f2400436eeb", + "sha256:ef9603a501a04fcd0ba28dd8f0995303d26a77a980a1f9474b3417543d4c6174", + "sha256:f30dbc58f2a0efeec6c8836c97f6c94afd769023f44e2bb0ed7b17a16ec46088", + "sha256:f5cc1601e78027cedfec6dd50efb4802f018551754191aeb58d948bd3ec3bd7a" ], - "markers": "python_version >= '3.8'", - "version": "==3.10.0" + "markers": "python_version >= '3.9'", + "version": "==3.14.0" }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "importlib-metadata": { "hashes": [ - "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e", - "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc" + "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", + "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd" ], "markers": "python_version >= '3.7'", - "version": "==7.0.1" + "version": "==8.7.0" }, "itsdangerous": { "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", + "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "markers": "python_version >= '3.8'", + "version": "==2.2.0" }, "jinja2": { "hashes": [ - "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", - "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", + "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" ], "markers": "python_version >= '3.7'", - "version": "==3.1.3" + "version": "==3.1.6" }, "jsbeautifier": { "hashes": [ - "sha256:6b632581ea60dd1c133cd25a48ad187b4b91f526623c4b0fb5443ef805250505" + "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592", + "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528" ], - "version": "==1.14.11" + "version": "==1.15.4" }, "llvmlite": { "hashes": [ - "sha256:04725975e5b2af416d685ea0769f4ecc33f97be541e301054c9f741003085802", - "sha256:0dd0338da625346538f1173a17cabf21d1e315cf387ca21b294ff209d176e244", - "sha256:150d0bc275a8ac664a705135e639178883293cf08c1a38de3bbaa2f693a0a867", - "sha256:1eee5cf17ec2b4198b509272cf300ee6577229d237c98cc6e63861b08463ddc6", - "sha256:210e458723436b2469d61b54b453474e09e12a94453c97ea3fbb0742ba5a83d8", - "sha256:2181bb63ef3c607e6403813421b46982c3ac6bfc1f11fa16a13eaafb46f578e6", - "sha256:24091a6b31242bcdd56ae2dbea40007f462260bc9bdf947953acc39dffd54f8f", - "sha256:2b76acee82ea0e9304be6be9d4b3840208d050ea0dcad75b1635fa06e949a0ae", - "sha256:2d92c51e6e9394d503033ffe3292f5bef1566ab73029ec853861f60ad5c925d0", - "sha256:5940bc901fb0325970415dbede82c0b7f3e35c2d5fd1d5e0047134c2c46b3281", - "sha256:8454c1133ef701e8c050a59edd85d238ee18bb9a0eb95faf2fca8b909ee3c89a", - "sha256:855f280e781d49e0640aef4c4af586831ade8f1a6c4df483fb901cbe1a48d127", - "sha256:880cb57ca49e862e1cd077104375b9d1dfdc0622596dfa22105f470d7bacb309", - "sha256:8b0a9a47c28f67a269bb62f6256e63cef28d3c5f13cbae4fab587c3ad506778b", - "sha256:92c32356f669e036eb01016e883b22add883c60739bc1ebee3a1cc0249a50828", - "sha256:92f093986ab92e71c9ffe334c002f96defc7986efda18397d0f08534f3ebdc4d", - "sha256:9564c19b31a0434f01d2025b06b44c7ed422f51e719ab5d24ff03b7560066c9a", - "sha256:b67340c62c93a11fae482910dc29163a50dff3dfa88bc874872d28ee604a83be", - "sha256:bf14aa0eb22b58c231243dccf7e7f42f7beec48970f2549b3a6acc737d1a4ba4", - "sha256:c1e1029d47ee66d3a0c4d6088641882f75b93db82bd0e6178f7bd744ebce42b9", - "sha256:df75594e5a4702b032684d5481db3af990b69c249ccb1d32687b8501f0689432", - "sha256:f19f767a018e6ec89608e1f6b13348fa2fcde657151137cb64e56d48598a92db", - "sha256:f8afdfa6da33f0b4226af8e64cfc2b28986e005528fbf944d0a24a72acfc9432", - "sha256:fa1469901a2e100c17eb8fe2678e34bd4255a3576d1a543421356e9c14d6e2ae" - ], - "markers": "python_version >= '3.8'", - "version": "==0.41.1" + "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", + "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", + "sha256:2fb7c4f2fb86cbae6dca3db9ab203eeea0e22d73b99bc2341cdf9de93612e930", + "sha256:319bddd44e5f71ae2689859b7203080716448a3cd1128fb144fe5c055219d516", + "sha256:40526fb5e313d7b96bda4cbb2c85cd5374e04d80732dd36a282d72a560bb6408", + "sha256:41e3839150db4330e1b2716c0be3b5c4672525b4c9005e17c7597f835f351ce2", + "sha256:46224058b13c96af1365290bdfebe9a6264ae62fb79b2b55693deed11657a8bf", + "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", + "sha256:7202b678cdf904823c764ee0fe2dfe38a76981f4c1e51715b4cb5abb6cf1d9e8", + "sha256:9c58867118bad04a0bb22a2e0068c693719658105e40009ffe95c7000fcde88e", + "sha256:9fbadbfba8422123bab5535b293da1cf72f9f478a65645ecd73e781f962ca614", + "sha256:aa0097052c32bf721a4efc03bd109d335dfa57d9bffb3d4c24cc680711b8b4fc", + "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427", + "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", + "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1", + "sha256:cccf8eb28f24840f2689fb1a45f9c0f7e582dd24e088dcf96e424834af11f791", + "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", + "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955", + "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", + "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3", + "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610" + ], + "markers": "python_version >= '3.10'", + "version": "==0.44.0" }, "markupsafe": { "hashes": [ - "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69", - "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0", - "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d", - "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec", - "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5", - "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411", - "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3", - "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74", - "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0", - "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949", - "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d", - "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279", - "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f", - "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6", - "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc", - "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e", - "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954", - "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656", - "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc", - "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518", - "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56", - "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc", - "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa", - "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565", - "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4", - "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb", - "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250", - "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4", - "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959", - "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc", - "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474", - "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863", - "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8", - "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f", - "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2", - "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e", - "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e", - "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb", - "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f", - "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a", - "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26", - "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d", - "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2", - "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131", - "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789", - "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6", - "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a", - "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858", - "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e", - "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb", - "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e", - "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84", - "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7", - "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea", - "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b", - "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6", - "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475", - "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74", - "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a", - "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00" + "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", + "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", + "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", + "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", + "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", + "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", + "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", + "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", + "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", + "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", + "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", + "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", + "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", + "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", + "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", + "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", + "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", + "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", + "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", + "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", + "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", + "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", + "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", + "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", + "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", + "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", + "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", + "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", + "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", + "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", + "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", + "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", + "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", + "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", + "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", + "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", + "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", + "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", + "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", + "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", + "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", + "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", + "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", + "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", + "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", + "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", + "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", + "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", + "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", + "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", + "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", + "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", + "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", + "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", + "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", + "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", + "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", + "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", + "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", + "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", + "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.4" + "markers": "python_version >= '3.9'", + "version": "==3.0.2" }, "more-itertools": { "hashes": [ @@ -418,30 +411,30 @@ }, "numba": { "hashes": [ - "sha256:07f2fa7e7144aa6f275f27260e73ce0d808d3c62b30cff8906ad1dec12d87bbe", - "sha256:240e7a1ae80eb6b14061dc91263b99dc8d6af9ea45d310751b780888097c1aaa", - "sha256:45698b995914003f890ad839cfc909eeb9c74921849c712a05405d1a79c50f68", - "sha256:487ded0633efccd9ca3a46364b40006dbdaca0f95e99b8b83e778d1195ebcbaa", - "sha256:4e79b6cc0d2bf064a955934a2e02bf676bc7995ab2db929dbbc62e4c16551be6", - "sha256:55a01e1881120e86d54efdff1be08381886fe9f04fc3006af309c602a72bc44d", - "sha256:5c765aef472a9406a97ea9782116335ad4f9ef5c9f93fc05fd44aab0db486954", - "sha256:6fe7a9d8e3bd996fbe5eac0683227ccef26cba98dae6e5cee2c1894d4b9f16c1", - "sha256:7bf1ddd4f7b9c2306de0384bf3854cac3edd7b4d8dffae2ec1b925e4c436233f", - "sha256:811305d5dc40ae43c3ace5b192c670c358a89a4d2ae4f86d1665003798ea7a1a", - "sha256:81fe5b51532478149b5081311b0fd4206959174e660c372b94ed5364cfb37c82", - "sha256:898af055b03f09d33a587e9425500e5be84fc90cd2f80b3fb71c6a4a17a7e354", - "sha256:9e9356e943617f5e35a74bf56ff6e7cc83e6b1865d5e13cee535d79bf2cae954", - "sha256:a1eaa744f518bbd60e1f7ccddfb8002b3d06bd865b94a5d7eac25028efe0e0ff", - "sha256:bc2d904d0319d7a5857bd65062340bed627f5bfe9ae4a495aef342f072880d50", - "sha256:bcecd3fb9df36554b342140a4d77d938a549be635d64caf8bd9ef6c47a47f8aa", - "sha256:bd3dda77955be03ff366eebbfdb39919ce7c2620d86c906203bed92124989032", - "sha256:bf68df9c307fb0aa81cacd33faccd6e419496fdc621e83f1efce35cdc5e79cac", - "sha256:d3e2fe81fe9a59fcd99cc572002101119059d64d31eb6324995ee8b0f144a306", - "sha256:e63d6aacaae1ba4ef3695f1c2122b30fa3d8ba039c8f517784668075856d79e2", - "sha256:ea5bfcf7d641d351c6a80e8e1826eb4a145d619870016eeaf20bbd71ef5caa22" - ], - "markers": "python_version >= '3.8'", - "version": "==0.58.1" + "sha256:34fba9406078bac7ab052efbf0d13939426c753ad72946baaa5bf9ae0ebb8dd2", + "sha256:3945615cd73c2c7eba2a85ccc9c1730c21cd3958bfcf5a44302abae0fb07bb60", + "sha256:3a10a8fc9afac40b1eac55717cece1b8b1ac0b946f5065c89e00bde646b5b154", + "sha256:48a53a3de8f8793526cbe330f2a39fe9a6638efcbf11bd63f3d2f9757ae345cd", + "sha256:49c980e4171948ffebf6b9a2520ea81feed113c1f4890747ba7f59e74be84b1b", + "sha256:4ddce10009bc097b080fc96876d14c051cc0c7679e99de3e0af59014dab7dfe8", + "sha256:59321215e2e0ac5fa928a8020ab00b8e57cda8a97384963ac0dfa4d4e6aa54e7", + "sha256:5b1bb509d01f23d70325d3a5a0e237cbc9544dd50e50588bc581ba860c213546", + "sha256:5f154aaea625fb32cfbe3b80c5456d514d416fcdf79733dd69c0df3a11348e9e", + "sha256:76bcec9f46259cedf888041b9886e257ae101c6268261b19fda8cfbc52bec9d1", + "sha256:7d3bcada3c9afba3bed413fba45845f2fb9cd0d2b27dd58a1be90257e293d140", + "sha256:8750ee147940a6637b80ecf7f95062185ad8726c8c28a2295b8ec1160a196f7d", + "sha256:97cf4f12c728cf77c9c1d7c23707e4d8fb4632b46275f8f3397de33e5877af18", + "sha256:ae45830b129c6137294093b269ef0a22998ccc27bf7cf096ab8dcf7bca8946f9", + "sha256:ae8c7a522c26215d5f62ebec436e3d341f7f590079245a2f1008dfd498cc1642", + "sha256:bbfdf4eca202cebade0b7d43896978e146f39398909a42941c9303f82f403a18", + "sha256:bd1e74609855aa43661edffca37346e4e8462f6903889917e9f41db40907daa2", + "sha256:bdbca73ad81fa196bd53dc12e3aaf1564ae036e0c125f237c7644fe64a4928ab", + "sha256:cf9f9fc00d6eca0c23fc840817ce9f439b9f03c8f03d6246c0e7f0cb15b7162a", + "sha256:ea0247617edcb5dd61f6106a56255baab031acc4257bddaeddb3a1003b4ca3fd", + "sha256:efd3db391df53aaa5cfbee189b6c910a5b471488749fd6606c3f33fc984c2ae2" + ], + "markers": "python_version >= '3.10'", + "version": "==0.61.2" }, "numpy": { "hashes": [ @@ -483,15 +476,16 @@ "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511" ], "index": "pypi", + "markers": "python_version >= '3.9'", "version": "==1.26.3" }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==25.0" }, "pandas": { "hashes": [ @@ -526,6 +520,7 @@ "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9" ], "index": "pypi", + "markers": "python_version >= '3.9'", "version": "==2.2.0" }, "plotly": { @@ -534,6 +529,7 @@ "sha256:360a31e6fbb49d12b007036eb6929521343d6bee2236f8459915821baefa2cbb" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==5.18.0" }, "pvlib": { @@ -542,6 +538,7 @@ "sha256:f03b63e2d2fca13d5ca4c69010929dca5046e935da943d2dd1806e265aa44e93" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==0.9.1" }, "pythermalcomfort": { @@ -550,37 +547,40 @@ "sha256:9b221ba003e8ea17267d496dc082ce89c0b00ed8de0fc349babea0e76fc53358" ], "index": "pypi", + "markers": "python_full_version >= '3.9.0'", "version": "==2.9.1" }, "python-dateutil": { "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" + "version": "==2.9.0.post0" }, "pytz": { "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" + "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", + "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" ], - "version": "==2023.3.post1" + "version": "==2025.2" }, "requests": { "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", + "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422" ], "index": "pypi", - "version": "==2.31.0" + "markers": "python_version >= '3.8'", + "version": "==2.32.4" }, "retrying": { "hashes": [ - "sha256:345da8c5765bd982b1d1915deb9102fd3d1f7ad16bd84a9700b85f64d24e8f3e", - "sha256:8cc4d43cb8e1125e0ff3344e9de678fefd85db3b750b81b2240dc0183af37b35" + "sha256:bbc004aeb542a74f3569aeddf42a2516efefcdaff90df0eb38fbfbf19f179f59", + "sha256:d102e75d53d8d30b88562d45361d6c6c934da06fab31bd81c0420acb97a8ba39" ], - "version": "==1.3.4" + "markers": "python_version >= '3.6'", + "version": "==1.4.2" }, "scipy": { "hashes": [ @@ -610,358 +610,336 @@ "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1", "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3" ], + "index": "pypi", "markers": "python_version >= '3.9'", "version": "==1.12.0" }, "setuptools": { "hashes": [ - "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05", - "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78" + "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", + "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" ], - "markers": "python_version >= '3.8'", - "version": "==69.0.3" + "markers": "python_version >= '3.9'", + "version": "==80.9.0" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "version": "==1.17.0" }, "tenacity": { "hashes": [ - "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a", - "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c" + "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", + "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138" ], - "markers": "python_version >= '3.7'", - "version": "==8.2.3" + "markers": "python_version >= '3.9'", + "version": "==9.1.2" }, "typing-extensions": { "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", + "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76" ], - "markers": "python_version >= '3.8'", - "version": "==4.9.0" + "markers": "python_version >= '3.9'", + "version": "==4.14.1" }, "tzdata": { "hashes": [ - "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3", - "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9" + "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", + "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9" ], "markers": "python_version >= '2'", - "version": "==2023.4" + "version": "==2025.2" }, "urllib3": { "hashes": [ - "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", - "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" + "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", + "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" ], - "markers": "python_version >= '3.8'", - "version": "==2.1.0" + "markers": "python_version >= '3.9'", + "version": "==2.5.0" }, "werkzeug": { "hashes": [ - "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", - "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" + "sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17", + "sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d" ], "markers": "python_version >= '3.8'", - "version": "==3.0.1" + "version": "==3.0.6" }, "zipp": { "hashes": [ - "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31", - "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0" + "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", + "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166" ], - "markers": "python_version >= '3.8'", - "version": "==3.17.0" + "markers": "python_version >= '3.9'", + "version": "==3.23.0" } }, "develop": { + "black": { + "hashes": [ + "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", + "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", + "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", + "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", + "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", + "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", + "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", + "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", + "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", + "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", + "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", + "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", + "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0", + "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", + "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", + "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", + "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355", + "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", + "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e", + "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", + "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", + "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==25.1.0" + }, "bump2version": { "hashes": [ "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410", "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6" ], "index": "pypi", + "markers": "python_version >= '3.5'", "version": "==1.0.1" }, - "certifi": { + "cfgv": { "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", + "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" ], - "markers": "python_version >= '3.6'", - "version": "==2023.11.17" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "markers": "python_version >= '3.8'", + "version": "==3.4.0" }, "cleanpy": { "hashes": [ - "sha256:291dd6b2643b2f434a95c0a4bbefac8b152e7b63cad1d369b0b605b156dbfa90", - "sha256:9d047ed8c6d3e1439f99b02633a94b1cbae65bbd4d928b5484367a674d66b5d6" + "sha256:9ddfa7ce80dd888b597a8b0bfeea3b69567839b6f41b775a4f76f46914d5170e", + "sha256:c60589d5da68527ca0c9151e28ed56fffae69df4ab6c9bfd8c1cf1d9e76a09b8" ], "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==0.5.1" + }, + "click": { + "hashes": [ + "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", + "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b" + ], + "markers": "python_version >= '3.10'", + "version": "==8.2.1" + }, + "distlib": { + "hashes": [ + "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", + "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d" + ], "version": "==0.4.0" }, - "idna": { + "filelock": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", + "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d" ], - "markers": "python_version >= '3.5'", - "version": "==3.6" + "markers": "python_version >= '3.9'", + "version": "==3.19.1" }, - "iniconfig": { + "identify": { "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", + "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" + "markers": "python_version >= '3.9'", + "version": "==2.6.13" }, - "numpy": { + "iniconfig": { "hashes": [ - "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd", - "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b", - "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e", - "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f", - "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f", - "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178", - "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3", - "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4", - "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e", - "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0", - "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00", - "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419", - "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4", - "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6", - "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166", - "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b", - "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3", - "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf", - "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2", - "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2", - "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36", - "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03", - "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce", - "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6", - "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13", - "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5", - "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e", - "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485", - "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137", - "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374", - "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58", - "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b", - "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb", - "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b", - "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda", - "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511" + "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", + "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" ], - "index": "pypi", - "version": "==1.26.3" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" }, - "packaging": { + "mypy-extensions": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", + "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558" ], - "markers": "python_version >= '3.7'", - "version": "==23.2" + "markers": "python_version >= '3.8'", + "version": "==1.1.0" }, - "pandas": { + "nodeenv": { "hashes": [ - "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1", - "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e", - "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f", - "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2", - "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18", - "sha256:3de918a754bbf2da2381e8a3dcc45eede8cd7775b047b923f9006d5f876802ae", - "sha256:52826b5f4ed658fa2b729264d63f6732b8b29949c7fd234510d57c61dbeadfcd", - "sha256:57abcaeda83fb80d447f28ab0cc7b32b13978f6f733875ebd1ed14f8fbc0f4ab", - "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430", - "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a", - "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106", - "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5", - "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670", - "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88", - "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b", - "sha256:9f66419d4a41132eb7e9a73dcec9486cf5019f52d90dd35547af11bc58f8637d", - "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71", - "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5", - "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e", - "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc", - "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30", - "sha256:bde2bc699dbd80d7bc7f9cab1e23a95c4375de615860ca089f34e7c64f4a8de7", - "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3", - "sha256:e60f1f7dba3c2d5ca159e18c46a34e7ca7247a73b5dd1a22b6d59707ed6b899a", - "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440", - "sha256:eb61dc8567b798b969bcc1fc964788f5a68214d333cade8319c7ab33e2b5d88a", - "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042", - "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1", - "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9" + "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", + "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" ], - "index": "pypi", - "version": "==2.2.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==1.9.1" }, - "pluggy": { + "packaging": { "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" ], "markers": "python_version >= '3.8'", - "version": "==1.3.0" + "version": "==25.0" }, - "pytest": { + "pathspec": { "hashes": [ - "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", - "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8" + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" ], - "index": "pypi", - "version": "==7.4.4" + "markers": "python_version >= '3.8'", + "version": "==0.12.1" }, - "python-dateutil": { + "platformdirs": { "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", + "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" + "markers": "python_version >= '3.9'", + "version": "==4.3.8" }, - "pytz": { + "pluggy": { "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" + "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", + "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" ], - "version": "==2023.3.post1" + "markers": "python_version >= '3.9'", + "version": "==1.6.0" }, - "requests": { + "pre-commit": { "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", + "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16" ], "index": "pypi", - "version": "==2.31.0" + "markers": "python_version >= '3.9'", + "version": "==4.3.0" }, - "six": { + "pygments": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", + "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "markers": "python_version >= '3.8'", + "version": "==2.19.2" }, - "tzdata": { + "pytest": { "hashes": [ - "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3", - "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9" + "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", + "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c" ], - "markers": "python_version >= '2'", - "version": "==2023.4" + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==8.4.1" + }, + "pyyaml": { + "hashes": [ + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" + ], + "markers": "python_version >= '3.8'", + "version": "==6.0.2" + }, + "ruff": { + "hashes": [ + "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", + "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", + "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", + "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", + "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", + "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", + "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", + "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", + "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", + "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", + "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", + "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", + "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", + "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", + "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", + "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", + "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", + "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", + "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.12.9" }, - "urllib3": { + "virtualenv": { "hashes": [ - "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", - "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" + "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", + "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a" ], "markers": "python_version >= '3.8'", - "version": "==2.1.0" + "version": "==20.34.0" } } } diff --git a/Procfile b/Procfile deleted file mode 100644 index 4808b15c..00000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: gunicorn main:server \ No newline at end of file diff --git a/Procfile.windows b/Procfile.windows deleted file mode 100644 index 19b83ee6..00000000 --- a/Procfile.windows +++ /dev/null @@ -1 +0,0 @@ -web: python main.py runserver 0.0.0.0:5000 \ No newline at end of file diff --git a/README.md b/README.md index 9de3c272..ab1cf7e2 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,32 @@ # CBE Clima Tool -The CBE Clima Tool is a web-based application built to support climate analysis specifically designed to support the need of architects and engineers interested in climate-adapted design. -It allows users to analyze the climate data of more than 27,500 locations worldwide from both [Energy Plus](https://energyplus.net/weather) and [Climate.One.Building.org](http://climate.onebuilding.org/). -You can, however, also choose to upload your own EPW weather file. Our tool can be used to analyze and visualize data contained in EnergyPlus Weather \(EPW\) files. -It furthermore calculates a number of climate-related values \(i.e. -solar azimuth and altitude, Universal Thermal Climate Index \(UTCI\), comfort indices, etc.\) that are not contained in the EPW files but can be derived from the information therein contained. -It can be freely accessed at [clima.cbe.berkeley.edu](http://clima.cbe.berkeley.edu) +The CBE Clima Tool is a web-based application built to support climate analysis specifically designed to support the need of architects and engineers interested in climate-adapted design. It can be freely accessed at [clima.cbe.berkeley.edu](http://clima.cbe.berkeley.edu). -If you use this tool please consider citing us. +It allows users to analyze the climate data of more than 27,500 locations worldwide from both [Energy Plus](https://energyplus.net/weather) and [Climate.One.Building.org](http://climate.onebuilding.org/). You can, however, also choose to upload your own EPW weather file. -## Official documentation +Our tool can be used to analyze and visualize data contained in EnergyPlus Weather (EPW) files. It furthermore calculates a number of climate-related values (i.e. solar azimuth and altitude, Universal Thermal Climate Index (UTCI), comfort indices, etc.) that are not contained in the EPW files but can be derived from the information therein contained. -[Official documentation](https://center-for-the-built-environment.gitbook.io/clima/). +## Key Features + +* **Interactive Climate Analysis:** Visualize EPW weather data through a variety of interactive charts. +* **Extensive Weather Data:** Access weather files for over 27,500 locations from EnergyPlus and Climate.One.Building.org. +* **Custom Data Upload:** Analyze your own custom `.epw` weather files. +* **Advanced Calculations:** Computes derived metrics like solar positions, UTCI, and various thermal comfort indices. +* **Data Export:** Download charts, data, and psychrometric chart overlays. + +## Documentation + +The official documentation for the tool can be found [here](https://center-for-the-built-environment.gitbook.io/clima/). + +## Citation + +If you use this tool in your research or work, please cite our paper: + +Betti, G., Tartarini, F. & Schiavon, S. CBE Clima Tool: a free and open-source web application for climate analysis for architects and building engineers. *Build. Simul.* (2023). https://doi.org/10.1007/s12273-023-1090-5 + +## Getting Support + +For any questions, feedback, or bug reports, please use the [GitHub Discussions](https://github.com/CenterForTheBuiltEnvironment/clima/discussions) section. ## Authors * [Giovanni Betti](https://www.linkedin.com/in/gbetti/) @@ -20,6 +35,6 @@ If you use this tool please consider citing us. ## Built with -* [Dash](https://plotly.com/dash/) - Framework for building the web app -* [Plotly Python](https://plotly.com/python/) - Used to create the interactive plots - +* [Dash](https://plotly.com/dash/) - Main web framework +* [Plotly Python](https://plotly.com/python/) - Interactive scientific graphing +* [Pandas](https://pandas.pydata.org/) - Data analysis and manipulation diff --git a/app.py b/app.py index 66fd6a5f..6eff14d5 100644 --- a/app.py +++ b/app.py @@ -3,6 +3,7 @@ app = DashProxy( __name__, + use_pages=True, external_stylesheets=[dbc.themes.BOOTSTRAP], transforms=[ServersideOutputTransform()], suppress_callback_exceptions=True, @@ -21,6 +22,7 @@ gtag('config', 'G-JDQTBEPS4B'); + + + + + diff --git a/assets/manifest.json b/assets/manifest.json index 0fae249d..591f5946 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -467,7 +467,7 @@ "orientation": "portrait", "background_color": "#ffffff", "display": "standalone", - "id": "0.8.17", + "id": "0.9.0", "description": "CBE Clima Tool: a free and open-source web application for climate analysis tailored to sustainable building design", "start_url": "/", "scope": "/", diff --git a/assets/tabs.css b/assets/tabs.css index 0be9dda2..15590dff 100644 --- a/assets/tabs.css +++ b/assets/tabs.css @@ -34,34 +34,64 @@ .custom-tabs-container { width: 85%; } + .custom-tabs { - border-top-left-radius: 3px; background-color: #f9f9f9; padding: 0 24px; border-bottom: 1px solid #d6d6d6; } .custom-tab { - color:#586069; - border-color: lightgrey; + border-color: rgb(238, 236, 236); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top: 3px solid transparent !important; border-left: 1px solid lightgrey !important; border-right: 1px solid lightgrey !important; - background-color: #fafbfc; + border-bottom: 1px solid #d6d6d6; + background-color: #f6f8f8; padding: 12px !important; font-family: "system-ui"; display: flex !important; align-items: center; justify-content: center; } -.custom-tab--selected { - color: black; + +.custom-tab:has(.active) { + color:#586069; + background-color: white; box-shadow: 1px 1px 0 white; border-left: 1px solid lightgrey !important; border-right: 1px solid lightgrey !important; border-top: 6px solid #abd2ff !important; + border-bottom: 1px solid transparent; +} + +.nav-pills { + display: flex; + flex-wrap: wrap; +} + +@media (max-width: 900px) { + .nav-pills { + flex-direction: column; + } +} + +.nav-pills .nav-link { + padding: 0; + color: #586069; + font-family: "system-ui"; + background-color: transparent; +} + +.nav-pills .nav-link.disabled { + color: #c8c6c6; +} + +.nav-pills .nav-link.active { + color: black; + background-color: white; } /* Tab One */ @@ -246,7 +276,13 @@ p { align-items: stretch; } - - - - +.survey-alert { + color: white; + background-color: #0c2772; + opacity: 0.98; + font-family: "system-ui"; + font-size: 15px; + border-radius: 0.25rem; + border: 0.5px solid lightgrey; + z-index: 1000; +} \ No newline at end of file diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 00000000..8eb6ac4a --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,15 @@ +steps: +# Build the container image +- name: 'gcr.io/cloud-builders/docker' + args: ['build', '-t', '$_GCR/$_PROJ_NAME/$_REPO_NAME/$_IMG_NAME', '.'] +# Push the container image to Container Registry +- name: 'gcr.io/cloud-builders/docker' + args: ['push', '$_GCR/$_PROJ_NAME/$_REPO_NAME/$_IMG_NAME'] +# Deploy container image to Cloud Run +- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' + entrypoint: 'gcloud' + args: ['run', 'deploy', '$_REPO_NAME', + '--image', '$_GCR/$_PROJ_NAME/$_REPO_NAME/$_IMG_NAME', + '--region', '$_REGION', + '--memory', '$_MEMORY', + '--cpu', '$_CPU'] \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 00000000..bc794545 --- /dev/null +++ b/config.py @@ -0,0 +1,100 @@ +from enum import Enum +import platform + + +class AppConfig: + """Stores application configuration.""" + + TITLE = "CBE Clima Tool" + DEBUG: bool = "macOS" in platform.platform() + HOST = "0.0.0.0" + PORT = 8080 + PROCESSES = 1 + THREADED = True + + +class UnitSystem(str, Enum): + """Unit systems.""" + + SI = "si" + IP = "ip" + + +class PageUrls(str, Enum): + """ + Stores application routes. + Inheriting from `str` allows the enum members to be used as strings directly. + """ + + SELECT = "/" + SUMMARY = "/summary" + T_RH = "/t-rh" + SUN = "/sun" + WIND = "/wind" + PSY_CHART = "/psy-chart" + NATURAL_VENTILATION = "/natural-ventilation" + OUTDOOR = "/outdoor" + EXPLORER = "/explorer" + CHANGELOG = "/changelog" + NOT_FOUND = "/404" + + +class FilePaths: + """Stores file paths used in the application.""" + + CHANGELOG = "CHANGELOG.md" + + +class Assets: + """Stores paths to assets.""" + + NOT_FOUND_ANIMATION = "/assets/animations/page_not_found.json" + + +class PageInfo: + """Stores page names and orders for registration.""" + + SELECT_NAME = "Select Weather File" + SELECT_ORDER = 0 + SUMMARY_NAME = "Climate Summary" + SUMMARY_ORDER = 1 + TEMP_RH_NAME = "Temperature and Humidity" + TEMP_RH_ORDER = 2 + SOLAR_RADIATION_NAME = "Solar Radiation" + SOLAR_RADIATION_ORDER = 3 + SUN_NAME = "Sun and Clouds" + SUN_ORDER = 4 + WIND_NAME = "Wind" + WIND_ORDER = 5 + PSYCHROMETRIC_NAME = "Psychrometric Chart" + PSYCHROMETRIC_ORDER = 6 + NATURAL_VENTILATION_NAME = "Natural Ventilation" + NATURAL_VENTILATION_ORDER = 7 + UTCI_NAME = "Outdoor Comfort" + UTCI_ORDER = 8 + EXPLORER_NAME = "Data Explorer" + EXPLORER_ORDER = 9 + CHANGELOG_NAME = "Changelog" + CHANGELOG_ORDER = 10 + NOT_FOUND_NAME = "404" + + +class DocLinks(str, Enum): + """Stores documentation links.""" + + MAIN = "https://cbe-berkeley.gitbook.io/clima" + TEMP_HUMIDITY_EXPLAINED = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/temperature-and-humidity/temperatures-explained" + SUN_PATH_DIAGRAM = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/how-to-read-a-sun-path-diagram" + WIND_ROSE = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/wind/how-to-read-a-wind-rose" + NATURAL_VENTILATION = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/natural-ventilation" + UTCI_CHART = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/outdoor-comfort/utci-explained" + PSYCHROMETRIC_CHART = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/psychrometric-chart" + CLOUD_COVER = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/cloud-coverage" + CUSTOM_HEATMAP = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps" + DEGREE_DAYS = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/tab-summary/degree-days-explained" + CLIMATE_PROFILES = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/tab-summary/climate-profiles-explained" + SOLAR_RADIATION = "https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation" + + +# You can also store other constants or settings here +DEFAULT_UNITS = UnitSystem.SI diff --git a/docs/.gitbook/assets/utci_image (1).png b/docs/.gitbook/assets/utci_image (1).png new file mode 100644 index 00000000..cda064ad Binary files /dev/null and b/docs/.gitbook/assets/utci_image (1).png differ diff --git a/docs/README.md b/docs/README.md index ea5f56db..05ce6e22 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,12 +6,20 @@ description: Web application for climate data analysis and visualization The CBE Clima Tool is a web-based application built to support climate analysis specifically designed to support the need of architects and engineers interested in climate-adapted design. It allows users to analyze the climate data of more than 27,500 locations worldwide from both [Energy Plus](https://energyplus.net/weather) and [Climate.One.Building.org](http://climate.onebuilding.org/). You can, however, also choose to upload your own EPW weather file. Our tool can be used to analyze and visualize data contained in EnergyPlus Weather (EPW) files. It furthermore calculates a number of climate-related values (i.e. solar azimuth and altitude, Universal Thermal Climate Index (UTCI), comfort indices, etc.) that are not contained in the EPW files but can be derived from the information therein contained. It can be freely accessed at [clima.cbe.berkeley.edu](http://clima.cbe.berkeley.edu) -> If you use this tool please consider citing us. Betti, G., Tartarini, F., Nguyen, C, Schiavon, S. CBE Clima Tool: -> A free and open-source web application for climate analysis tailored to sustainable building design. -> Build. Simul. (2023). [https://doi.org/10.1007/s12273-023-1090-5](https://doi.org/10.1007/s12273-023-1090-5). +> If you use this tool please consider citing us. Betti, G., Tartarini, F., Nguyen, C, Schiavon, S. CBE Clima Tool: A free and open-source web application for climate analysis tailored to sustainable building design. Build. Simul. (2023). [https://doi.org/10.1007/s12273-023-1090-5](https://doi.org/10.1007/s12273-023-1090-5). The CBE Clima Tool is open source. We have released the source code on a [public repository](https://github.com/CenterForTheBuiltEnvironment/clima). We welcome contributions from the community ([more info here](contributing/contributing.md)). +## Video Tutorial + +Learn more about the CBE Clima Tool by watching the following video. + +{% embed url="https://www.youtube.com/watch?v=VJ_wOHadVdw" %} +CBE Clima tool tutorial and overview +{% endembed %} + +[Watch on YouTube](https://www.youtube.com/watch?v=VJ_wOHadVdw) + ## Contributions This ongoing project results from the collaboration and contributions of the people listed below. diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index fa8fb538..48aa2860 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -16,7 +16,24 @@ Available [here](code_of_conduct.md) ## Code style -We use Black.exe to format the code. +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/). ## Submitting changes diff --git a/docs/contributing/run-project-locally.md b/docs/contributing/run-project-locally.md index ba9477fd..e125cd23 100644 --- a/docs/contributing/run-project-locally.md +++ b/docs/contributing/run-project-locally.md @@ -27,7 +27,7 @@ This guide is for Mac OSX, Linux, or Windows. 2. **Create a virtual environment using pipenv and install dependencies:** ```text - pipenv install --three + pipenv install --dev ``` 3. **Run tool locally** @@ -90,23 +90,19 @@ First make sure you that: * that you are logged in with the right account * you have updated the Pipfile.lock. -```bash -gcloud components update -gcloud auth list -pipenv lock -pipenv run pip3 freeze > requirements.txt +```text +gcloud components update --quiet +pipenv requirements > requirements.txt ``` -```bash -gcloud builds submit --tag us-docker.pkg.dev/clima-316917/gcr.io/clima --project=clima-316917 +### Deploy test version of the project -gcloud run deploy clima --image us-docker.pkg.dev/clima-316917/gcr.io/clima --platform managed --project=clima-316917 --allow-unauthenticated --region=us-central1 --memory=4Gi --concurrency=80 --cpu=2 +```text +gcloud builds submit --project=clima-316917 --substitutions=_REPO_NAME="clima-test",_PROJ_NAME="clima-316917",_IMG_NAME="test",_GCR="us.gcr.io",_REGION="us-central1",_MEMORY="4Gi",_CPU="2" ``` -### Test project +### Deploy main version of the project -```bash -gcloud builds submit --tag us-docker.pkg.dev/clima-316917/gcr.io/clima-test --project=clima-316917 - -gcloud run deploy clima-test --image us-docker.pkg.dev/clima-316917/gcr.io/clima-test --platform managed --project=clima-316917 --allow-unauthenticated --region=us-central1 --memory=4Gi --concurrency=80 --cpu=2 +```text +gcloud builds submit --project=clima-316917 --substitutions=_REPO_NAME="clima",_PROJ_NAME="clima-316917",_IMG_NAME="main",_GCR="us.gcr.io",_REGION="us-central1",_MEMORY="4Gi",_CPU="2" ``` \ No newline at end of file diff --git a/docs/documentation/tabs-explained/README.md b/docs/documentation/tabs-explained/README.md index 12b13e7a..b10d8a35 100644 --- a/docs/documentation/tabs-explained/README.md +++ b/docs/documentation/tabs-explained/README.md @@ -8,7 +8,7 @@ description: >- The Clima app is organized in a series of tabs that allow the exploration of various topics. All the tabs other than "Select Weather File" are active after a weather file has been selected. -Although there is a logical sequence in the organization of the tabs, thy can be accessed in any order. +Although there is a logical sequence in the organization of the tabs, thy can be accessed in any order. The Followin section will explain the content and the usage of each tab. diff --git a/docs/documentation/tabs-explained/data-explorer.md b/docs/documentation/tabs-explained/data-explorer.md index 56dfd797..f2ca916e 100644 --- a/docs/documentation/tabs-explained/data-explorer.md +++ b/docs/documentation/tabs-explained/data-explorer.md @@ -8,10 +8,15 @@ The tab is divided into three sections: * Single-variable + filter analysis * Triple-variable + filter analysis -The single-variable analysis allows data to be displayed in 4 outputs: a yearly chart, monthly daily charts, an annual heatmap chart, and a descriptive statistics table. +The single-variable analysis allows data to be displayed in 4 outputs: a yearly chart, monthly daily charts, an annual heatmap chart, and a descriptive statistics table. The single-variable + filter analysis allows data to be displayed in a customizable heatmap. The chart can be created starting from one variable inside the Clima dataframe and, eventually, filtered by another one. The triple-variable + filter analysis allows data to be displayed in two composite charts, a scatterplot, and a heat map. The baselines of the two graphs are driven by the choice of one variable to be placed on the x-axis and one on the y-axis. Then, data can be colored according to a third variable and filtered according to a fourth. -__ +### Video Tutorial + +Learn more about the Data Explorer tab by watching the following video. + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=1iXC2GvWPdPBGzMc&t=825" %} + diff --git a/docs/documentation/tabs-explained/natural-ventilation.md b/docs/documentation/tabs-explained/natural-ventilation.md index 0724145e..c1fbc341 100644 --- a/docs/documentation/tabs-explained/natural-ventilation.md +++ b/docs/documentation/tabs-explained/natural-ventilation.md @@ -26,4 +26,10 @@ The minimum temperature threshold is typically dictated by local discomfort near Natural ventilation can be used in combination with and in aid of radiant cooling systems. The most common risk is that condensation will form on cold surfaces, creating slippery floors or potential mold. **Clima** allows this control to be performed with the "surface temperature" filter, which is a function of the dew temperature. -_\[video as for psychometric chart]_ +### Video Tutorial + +Learn more about the Natural Ventilation tab by watching the following video. + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=_cUoFQGyxJD7V85a&t=703" %} + + diff --git a/docs/documentation/tabs-explained/outdoor-comfort/README.md b/docs/documentation/tabs-explained/outdoor-comfort/README.md index d958554a..f5f3b996 100644 --- a/docs/documentation/tabs-explained/outdoor-comfort/README.md +++ b/docs/documentation/tabs-explained/outdoor-comfort/README.md @@ -2,18 +2,18 @@ The **Outdoor Comfort** tab shows an overview of the perceived environmental condition based on the UTCI model. -The [Universal Thermal Climate Index](http://www.utci.org/index.php) (UTCI), introduced in 1994, aims to be the measure of human physiological reaction to the atmospheric environment. +The [Universal Thermal Climate Index](http://www.utci.org/index.php) (UTCI), introduced in 1994, aims to be the measure of human physiological reaction to the atmospheric environment. -

Graphical scheme of the UTCI calculation methodology

+

Graphical scheme of the UTCI calculation methodology

It considers: -* dry bulb temperature +* dry bulb temperature * mean radiant temperature * wind speed * relative humidity - to calculate a reference environmental temperature causing strain when compared to an individual's response to the real environment. It is based on [Fiala et al](https://link.springer.com/article/10.1007/s00484-011-0424-7).'s multi-node model of thermo-regulation. +to calculate a reference environmental temperature causing strain when compared to an individual's response to the real environment. It is based on [Fiala et al](https://link.springer.com/article/10.1007/s00484-011-0424-7).'s multi-node model of thermo-regulation. The [UTCI equivalent temperature](https://doi.org/10.1016/j.wace.2018.01.004) is a function of the above parameters, which are combined in a multinode thermo-physiological model that takes into account clothing insulation and metabolic rate. From this a[Universal Thermal Climate Index](http://www.utci.org/index.php) (UTCI) of perceived thermal stress is derived. @@ -24,3 +24,10 @@ The [UTCI equivalent temperature](https://doi.org/10.1016/j.wace.2018.01.004) is The UTCI temperature can be converted in a scale assessing thermal stress, displayed by **Clima** in a heatmap graph.

Example: UTCI heat stress index heatmap in 'Sun and Wind' conditions for Rome, ITA

+ +### Video Tutorial + +Learn more about the Outdoor Comfort tab by watching the following video. + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=hIa-95u3wpeP6eq0&t=781" %} + diff --git a/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md b/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md index 08adc0d6..3e1b5dc8 100644 --- a/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md +++ b/docs/documentation/tabs-explained/outdoor-comfort/utci-explained.md @@ -1,6 +1,6 @@ # UTCI explained -The UTCI tab allows users to analyze outdoor thermal comfort for a combination of different meteorological conditions based on the presence or absence of sun and wind. +The UTCI tab allows users to analyze outdoor thermal comfort for a combination of different meteorological conditions based on the presence or absence of sun and wind. ![Logos highlighting the different scenarios which can be displayed in Clima](<../../../.gitbook/assets/UTCI 01-01.jpg>) @@ -9,7 +9,7 @@ Clima leverages the several models implemented in [Pythermalcomfort](https://pyt * The "[Solar gain on people](https://pythermalcomfort.readthedocs.io/en/latest/reference/pythermalcomfort.html#solar-gain-on-people)" calculates the solar gain to the human body, so the mean radiant temperature. To simulate a sunless situation, Clima considers the person surrounded by surfaces that shade him, all of which tend toward dry bulb temperature; * Wind data is obtained directly from the weather file. The windless situation sets the value at 0.5 m/s, which is the minimum value allowed by the UTCI model. -The UTCI can then be visualized for the entire year for the scenario chosen. +The UTCI can then be visualized for the entire year for the scenario chosen.

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

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

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

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

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

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

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

@@ -17,3 +17,10 @@ Then, users can overlay another variable on the graphs, choosing from [Clima dat Moreover, data can be filtered by date, time, or one of the [Clima dataframe](../tab-summary/clima-dataframe.md) variables.

Psychrometric charts filters

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

Temperature line in the Psychrometric diagram

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

Relative humidity curves in the Psychrometric diagram

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

Cooling and heating process

diff --git a/docs/documentation/tabs-explained/sun-and-cloud/README.md b/docs/documentation/tabs-explained/sun-and-cloud/README.md index d0dd117a..9f354fd7 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/README.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/README.md @@ -1,8 +1,8 @@ # Sun and Clouds -The **Sun and Clouds** tab presents an overview of various climatic factors that relate to sun, solar position, intensity, and cloud cover, in particular: +The **Sun and Clouds** tab presents an overview of various climatic factors that relate to sun, solar position, intensity, and cloud cover, in particular: -* [Apparent sunpath for the location (spherical and cartesian projection)](broken-reference) +* [Apparent sunpath for the location (spherical and cartesian projection)](broken-reference/) * [Global and Diffuse Horizontal Solar Radiation](global-and-diffuse-horizontal-solar-radiation/) * [Cloud coverage](cloud-coverage.md) * [Customizable daily and hourly maps](customizable-daily-and-hourly-maps.md) @@ -11,12 +11,19 @@ The **Sun and Clouds** tab presents an overview of various climatic factors that **Clima** allows the user to visualize the sun path for the chosen location in spherical and cartesian projection -![Example: spherical sun path for Berlin, DEU ](../../../.gitbook/assets/cbeclima\_berlin\_deu\_spherical\_sun\_path\_sun\_tab.svg) +![Example: spherical sun path for Berlin, DEU](../../../.gitbook/assets/cbeclima\_berlin\_deu\_spherical\_sun\_path\_sun\_tab.svg) -![Example: cartesian sun path for Berlin, DEU ](../../../.gitbook/assets/cbeclima\_berlin\_deu\_cartesian\_sun\_path\_sun\_tab.svg) +![Example: cartesian sun path for Berlin, DEU](../../../.gitbook/assets/cbeclima\_berlin\_deu\_cartesian\_sun\_path\_sun\_tab.svg) Clima optionally allows a variety of variables to be overlayed on either sun path type. This allows the user to identify climatic patterns in relation to the apparent solar position. Data are plotted on the analemma. ![Spherical and carthesian sun paths for Berlin, DEU with various data overlays](../../../.gitbook/assets/sunpath+variables.png) + +### Video Tutorial + +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 3228741a..25755eca 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/cloud-coverage.md @@ -2,7 +2,7 @@ The cloud coverage diagram reports, for every month of the year the frequency of "clear", "cloudy" or "intermediate" conditions. -As the Cloud cover is reported in tenths of coverage (i.e. 0 is 0/10 covered. 10 is total coverage) for the purpose of this graph we have simplified the scale as per the table below. +As the Cloud cover is reported in tenths of coverage (i.e. 0 is 0/10 covered. 10 is total coverage) for the purpose of this graph we have simplified the scale as per the table below. | Categorization | Color | Tenth of coverage | | ----------------------- | ------------------------------------------------------------------------------- | ----------------- | diff --git a/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md b/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md index bdc56435..34481ad7 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps.md @@ -8,7 +8,7 @@ The chart above shows the [scatter plot](https://en.wikipedia.org/wiki/Scatter\_

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

-[Heat maps](https://en.wikipedia.org/wiki/Heat\_map) allow the intensity of values to be perceived through color palettes. These graphs are very helpful in seeing how magnitudes vary throughout the year. +[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 d9f65b5a..fcb336eb 100644 --- a/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/README.md +++ b/docs/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation/README.md @@ -11,4 +11,4 @@ Typical daily graphs showing the amount of energy gained from the sun have many * manage the **indirect solar gain** transfer into the building with a time shift, exploiting the thermal mass, heating thick walls or concrete floors, or designing special rooms adjacent to the main spaces that rely on convection to transfer the heat, such as sunroom or [Trombe wall](https://en.wikipedia.org/wiki/Trombe\_wall); * evaluating sustainable **renewable energy solutions** such as solar thermal or photovoltaic panels. - The integral of the curves in the graphs is the total energy (in Wh/m²), supplied by the sun. Be careful in considering the [different types of solar radiation.](global-diffuse-and-normal-solar-radiation-explained.md) +The integral of the curves in the graphs is the total energy (in Wh/m²), supplied by the sun. Be careful in considering the [different types of solar radiation.](global-diffuse-and-normal-solar-radiation-explained.md) diff --git a/docs/documentation/tabs-explained/tab-home.md b/docs/documentation/tabs-explained/tab-home.md index e4af9123..a7c1106a 100644 --- a/docs/documentation/tabs-explained/tab-home.md +++ b/docs/documentation/tabs-explained/tab-home.md @@ -1,6 +1,16 @@ +--- +description: This page explains how a user can load an EPW file in the Clima tool +--- + # Select Weather File Users can either choose to analyse the climate of the locations displayed on the map or upload a custom EPW file. After loading an EPW file the user can then access the other tabs to generate dynamic visualisations of the data. -![](../../.gitbook/assets/clima-home.png) +## Video Tutorial + +Learn more about how to analyse the climate of a specific location and uploading your custom EPW file by watching the following video. + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=SxvUzaI9rCNIFFs0&t=136" %} +How to select or upload an EPW file +{% endembed %} diff --git a/docs/documentation/tabs-explained/tab-summary/README.md b/docs/documentation/tabs-explained/tab-summary/README.md index 7281cc2e..06a3b3fd 100644 --- a/docs/documentation/tabs-explained/tab-summary/README.md +++ b/docs/documentation/tabs-explained/tab-summary/README.md @@ -7,3 +7,11 @@ The top section of the page provides information about the selected location suc The bottom section of the page comprises the heating and cooling degree day chart and four violin plots showing the distribution of the dry-bulb air temperature (Tdb), relative humidity (RH), Global Horizontal Irradiance (GHI), and wind speed (U). ![Tab summary top](../../../.gitbook/assets/clima-summary-bottom.png) + +## Video Tutorial + +Learn more about the Climate Summary tab by watching the following video. + + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=H-93XRhh5Neuby_b&t=220" %} + diff --git a/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md b/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md index 11cc1d82..2f985e61 100644 --- a/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md +++ b/docs/documentation/tabs-explained/tab-summary/clima-dataframe.md @@ -1,6 +1,6 @@ # Clima Dataframe -**Clima** calculates new variables and creates a new **dataframe** containing the variables already inside the original EPW files and other we calculate. Users can overlay all the variables on the [sun path](../sun-and-cloud/), on the [psychometric chart](../psychrometric-chart/), and on the customizable graphs in the [data explorer](../data-explorer.md). +**Clima** calculates new variables and creates a new **dataframe** containing the variables already inside the original EPW files and other we calculate. Users can overlay all the variables on the [sun path](../sun-and-cloud/), on the [psychrometric chart](../psychrometric-chart/), and on the customizable graphs in the [data explorer](../data-explorer.md). All the variables in the new Clima dataframe are listed below. @@ -13,15 +13,15 @@ All the variables in the new Clima dataframe are listed below. * [Horizontal Infrared Radiation ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-horizontal-infrared-radiation-intensity) * [Global Horizontal Radiation ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-global-horizontal-radiation) * [Direct Normal Radiation ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-direct-normal-radiation) -* [Diffuse Horizontal Radiation](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-diffuse-horizontal-radiation) -* [Global Horizontal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-global-horizontal-illuminance) -* [Direct Normal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-direct-normal-illuminance) +* [Diffuse Horizontal Radiation](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-diffuse-horizontal-radiation) +* [Global Horizontal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-global-horizontal-illuminance) +* [Direct Normal Illuminance](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-direct-normal-illuminance) * [Diffuse Horizontal Illuminance ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-diffuse-horizontal-illuminance) * [Zenith Luminance ](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-zenith-luminance) -* [Wind Direction](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-direction) -* [Wind Speed](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-speed) -* [Total Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-total-sky-cover) -* [Opaque Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-opaque-sky-cover) +* [Wind Direction](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-direction) +* [Wind Speed](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-wind-speed) +* [Total Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-total-sky-cover) +* [Opaque Sky Cover](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-opaque-sky-cover) * [Visibility](https://bigladdersoftware.com/epx/docs/22-2/auxiliary-programs/energyplus-weather-file-epw-data-dictionary.html#field-visibility) * [UTCI, Universal Thermal Climate Index](../outdoor-comfort/utci-explained.md) * [Vapor partial pressure](https://en.wikipedia.org/wiki/Vapor\_pressure) diff --git a/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md b/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md index 6eb090b7..1334a7dd 100644 --- a/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md +++ b/docs/documentation/tabs-explained/tab-summary/climate-profiles-explained.md @@ -2,7 +2,9 @@ The Climate Profiles graph gives the user the opportunity to observe at a glance the distribution of the data in the EPW file for four key variables and their variation between day and night. -The Climate Profiles graph are [Violin Plots](https://en.wikipedia.org/wiki/Violin\_plot). They show the [probability density](https://en.wikipedia.org/wiki/Probability\_density\_function) of the data at different values, usually smoothed by a [kernel density estimator](https://en.wikipedia.org/wiki/Kernel\_density\_estimator). Wider sections of the violin plot represent a higher probability that members of the population will take on the given value; the skinnier sections represent a lower probability. +The Climate Profiles graph are [Violin Plots](https://en.wikipedia.org/wiki/Violin\_plot). +They show the [probability density](https://en.wikipedia.org/wiki/Probability\_density\_function) of the data at different values, usually smoothed by a [kernel density estimator](https://en.wikipedia.org/wiki/Kernel\_density\_estimator). +Wider sections of the violin plot represent a higher probability that members of the population will take on the given value; the skinnier sections represent a lower probability. On mouse hover, they display various statistical properties of the data: diff --git a/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md b/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md index 21d70bc9..115d98d0 100644 --- a/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md +++ b/docs/documentation/tabs-explained/tab-summary/degree-days-explained.md @@ -4,7 +4,7 @@ you might want to start understanding what degree days are here: {% embed url="https://en.wikipedia.org/wiki/Heating_degree_day" %} -**Degree days** are calculated as the _integral of the difference between the outside air temperature and a base temperature over time_. +**Degree days** are calculated as the _integral of the difference between the outside air temperature and a base temperature over time_. If you can define a **base temperature** (the outside temperature above which a building needs no heating or cooling) then you can use this to estimate degree days. The building requires heating if the outside air temperature falls below the heating base temperature, and the heating degree days accrue; if the outside air temperature rises above the cooling base temperature, the structure requires cooling, and the cooling degree days accumulate. diff --git a/docs/documentation/tabs-explained/temperature-and-humidity/README.md b/docs/documentation/tabs-explained/temperature-and-humidity/README.md index 65527769..48fad2bf 100644 --- a/docs/documentation/tabs-explained/temperature-and-humidity/README.md +++ b/docs/documentation/tabs-explained/temperature-and-humidity/README.md @@ -2,7 +2,7 @@ The **Temperature and Humidity** tab presents an overview of [air dry bulb temperature](temperatures-explained.md) and [relative humidity](relative-humidity-explained.md) trends. -**Clima** allows the user to visualize the annual data trend through a customizable chart. +**Clima** allows the user to visualize the annual data trend through a customizable chart.

Example: annual dry bulb temperatures trend for Paris, FRA

@@ -19,3 +19,10 @@ Daily [scatter plot](https://en.wikipedia.org/wiki/Scatter\_plot) shows all hour

Example: annual dry bulb temperatures heatmap for Paris, FRA

Example: annual relative humidity heatmap for Paris, FRA

+ +### Video Tutorial + +Learn more about the Temperature and Relative Humidity tab by watching the following video. + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=a1lgX6Lpt8fUXiCr&t=433" %} + diff --git a/docs/documentation/tabs-explained/wind/README.md b/docs/documentation/tabs-explained/wind/README.md index b9118714..4d2abbe3 100644 --- a/docs/documentation/tabs-explained/wind/README.md +++ b/docs/documentation/tabs-explained/wind/README.md @@ -11,3 +11,10 @@ Moreover, **Clima** shows wind intensity and direction using [heat maps](https:/

Example: Heat map of the hourly wind intensity on all days of the year for Rome, ITA

Example: Heat map of the hourly wind direction on all days of the year for Rome, ITA

+ +### Video Tutorial + +Learn more about the Wind tab by watching the following video. + +{% embed url="https://youtu.be/VJ_wOHadVdw?si=W90QpOb4VoqUPcbA&t=524" %} + diff --git a/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md b/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md index 617e30e0..e7838698 100644 --- a/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md +++ b/docs/documentation/tabs-explained/wind/how-to-read-a-wind-rose.md @@ -14,7 +14,7 @@ The length of each radius around the circle shows how often the wind blew from t

Frequency of wind intensity recurrence in the wind rose

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

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

diff --git a/docs/documentation/weather-file-repositories.md b/docs/documentation/weather-file-repositories.md index 5725c61e..de63edaa 100644 --- a/docs/documentation/weather-file-repositories.md +++ b/docs/documentation/weather-file-repositories.md @@ -1,6 +1,6 @@ # Weather file repositories -In addition to the data from [Energy Plus](https://energyplus.net/weather) and [Climate.One.Building.org](http://climate.onebuilding.org/) CBE Clima Tool allows users to visualize any valid EPW file. Below we list some free sources from which climate files can be obtained. +In addition to the data from [Energy Plus](https://energyplus.net/weather) and [Climate.One.Building.org](http://climate.onebuilding.org/) CBE Clima Tool allows users to visualize any valid EPW file. Below we list some free sources from which climate files can be obtained. The following sources will be divided according to: @@ -9,16 +9,13 @@ The following sources will be divided according to: **TMY repositories:** -* ****[Climate.OneBuilding](https://climate.onebuilding.org/); +* [Climate.OneBuilding](https://climate.onebuilding.org/); * [EnergyPlus](https://energyplus.net/weather); * European commission [Photovoltaic Geographical Information System](https://re.jrc.ec.europa.eu/pvg\_tools/it/#TMY); * [CSIRO](https://agdatashop.csiro.au/future-climate-predictive-weather) projected weather files for future climate scenarios for Australian locations; * University of Exeter [Prometheus](https://engineering.exeter.ac.uk/research/cee/research/prometheus/termsandconditions/futureweatherfiles/), current and future weather files for British cities - - **AMY repositories:** * NASA [Power tool](https://power.larc.nasa.gov/data-access-viewer/); * Pacific Northwest National Laboratory [diyepw tool](https://github.com/IMMM-SFA/diyepw) (given set of WMOs station) - diff --git a/main.py b/main.py index 502eb7d5..d6fccbfa 100644 --- a/main.py +++ b/main.py @@ -1,86 +1,37 @@ -import dash import dash_bootstrap_components as dbc from dash import html, dcc -from dash.dependencies import Input, Output +from dash_extensions.enrich import Output, Input, callback from app import app -from my_project.layout import banner, build_tabs, footer -from my_project.page_changelog.app_changelog import changelog -from my_project.tab_data_explorer.app_data_explorer import layout_data_explorer -from my_project.tab_natural_ventilation.app_natural_ventilation import ( - layout_natural_ventilation, -) -from my_project.tab_outdoor_comfort.app_outdoor_comfort import layout_outdoor_comfort -from my_project.tab_psy_chart.app_psy_chart import layout_psy_chart -from my_project.tab_select.app_select import layout_select -from my_project.tab_summary.app_summary import layout_summary -from my_project.tab_sun.app_sun import layout_sun -from my_project.tab_t_rh.app_t_rh import layout_t_rh -from my_project.tab_wind.app_wind import layout_wind +from pages.lib.layout import banner, footer, build_tabs +from config import AppConfig server = app.server -app.title = "CBE Clima Tool" +app.title = AppConfig.TITLE app.layout = dbc.Container( fluid=True, style={"padding": "0"}, children=[ - dcc.Location(id="url", refresh=False), + dcc.Location(id="url", refresh=False), # connected to callback below banner(), - html.Div(id="page-content"), + html.Div(id="page-content", children=build_tabs()), footer(), ], ) -@app.callback( - dash.dependencies.Output("page-content", "children"), - [dash.dependencies.Input("url", "pathname")], -) -def display_page(pathname): - if pathname == "/": - return build_tabs() - elif pathname == "/changelog": - return html.Div(children=[changelog()]) - - -# Handle tab selection -@app.callback( - Output("tabs-content", "children"), - [ - Input("tabs", "value"), - Input("si-ip-radio-input", "value"), - ], -) -def render_content(tab, si_ip): - """Update the contents of the page depending on what tab the user selects.""" - if tab == "tab-select": - return layout_select() - elif tab == "tab-summary": - return layout_summary(si_ip) - elif tab == "tab-t-rh": - return layout_t_rh() - elif tab == "tab-sun": - return layout_sun(si_ip) - elif tab == "tab-wind": - return layout_wind() - elif tab == "tab-data-explorer": - return layout_data_explorer() - elif tab == "tab-outdoor-comfort": - return layout_outdoor_comfort() - elif tab == "tab-natural-ventilation": - return layout_natural_ventilation(si_ip) - elif tab == "tab-psy-chart": - return layout_psy_chart() - else: - return "404" +# callback for survey alert (dbc.Toast) +@callback(Output("alert-auto", "is_open"), Input("interval-component", "n_intervals")) +def display_alert(n): + return n == 1 if __name__ == "__main__": app.run_server( - debug=False, - host="0.0.0.0", - port=8080, - processes=1, - threaded=True, + debug=AppConfig.DEBUG, + host=AppConfig.HOST, + port=AppConfig.PORT, + processes=AppConfig.PROCESSES, + threaded=AppConfig.THREADED, ) diff --git a/my_project/layout.py b/my_project/layout.py deleted file mode 100644 index 9bfd5f5a..00000000 --- a/my_project/layout.py +++ /dev/null @@ -1,244 +0,0 @@ -import dash_bootstrap_components as dbc -from dash import dcc, html - - -def footer(): - return dbc.Row( - align="center", - justify="between", - id="footer-container", - children=[ - dbc.Col( - children=[ - dbc.Row( - html.A( - children=[ - html.Img( - src="assets/img/cbe-logo.png", - ) - ], - href="https://cbe.berkeley.edu/", - ) - ), - ], - width=12, - md=4, - style={"padding": "15px"}, - ), - dbc.Col( - children=[ - dbc.Row( - [ - dcc.Markdown( - """ - Please cite us: - Betti, G., Tartarini, F., Nguyen, C, Schiavon, S. CBE Clima Tool: - A free and open-source web application for climate analysis tailored to sustainable building design. - Build. Simul. (2023). [https://doi.org/10.1007/s12273-023-1090-5](https://doi.org/10.1007/s12273-023-1090-5). - [Version: 0.8.17](https://center-for-the-built-environment.gitbook.io/clima/version/changelog) - """ - ), - dcc.Markdown( - """ - [Contributors](https://cbe-berkeley.gitbook.io/clima/#contributions), - [Report issues on GitHub](https://github.com/CenterForTheBuiltEnvironment/clima/issues), - [Contact us page](https://forms.gle/LRUq3vsFnE1QCLiA6), - [Documentation page](https://center-for-the-built-environment.gitbook.io/clima/), - [License](https://center-for-the-built-environment.gitbook.io/clima/#license) - """, - style={"marginTop": "1rem"}, - ), - ], - style={"marginTop": "1rem"}, - ), - ], - width=12, - md=8, - ), - ], - ) - - -def banner(): - """Build the banner at the top of the page.""" - return html.Div( - id="banner", - children=[ - dbc.Row( - align="center", - children=[ - dbc.Col( - html.A( - href="/", - children=[ - html.Img( - src="assets/img/cbe-logo-small.png", - ), - ], - ), - width="auto", - ), - dbc.Col( - children=[ - html.H1(id="banner-title", children=["CBE Clima Tool"]), - html.H5( - id="banner-subtitle", - children=["Current Location:"], - ), - ], - ), - dbc.Col( - style={"fontWeight": "400", "padding": "1rem"}, - align="end", - children=[ - dbc.Row( - style={"text-align": "right"}, - children=[ - dbc.RadioItems( - options=[ - { - "label": "Global Value Ranges", - "value": "global", - }, - { - "label": "Local Value Ranges", - "value": "local", - }, - ], - value="local", - id="global-local-radio-input", - inline=True, - ), - ], - ), - dbc.Row( - align="end", - style={"text-align": "right"}, - children=[ - dbc.RadioItems( - options=[ - {"label": "SI", "value": "si"}, - {"label": "IP", "value": "ip"}, - ], - value="si", - id="si-ip-radio-input", - inline=True, - ), - ], - ), - ], - width="auto", - ), - ], - ), - ], - ) - - -def build_tabs(): - """Build the seven different tabs.""" - return html.Div( - id="tabs-container", - children=[ - dcc.Tabs( - id="tabs", - parent_className="custom-tabs", - value="tab-select", - children=[ - dcc.Tab( - label="Select Weather File", - value="tab-select", - className="custom-tab", - selected_className="custom-tab--selected", - ), - dcc.Tab( - id="tab-summary", - label="Climate Summary", - value="tab-summary", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - dcc.Tab( - id="tab-t-rh", - label="Temperature and Humidity", - value="tab-t-rh", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - dcc.Tab( - id="tab-sun", - label="Sun and Clouds", - value="tab-sun", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - dcc.Tab( - id="tab-wind", - label="Wind", - value="tab-wind", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - dcc.Tab( - id="tab-psy-chart", - label="Psychrometric Chart", - value="tab-psy-chart", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - dcc.Tab( - id="tab-natural-ventilation", - label="Natural Ventilation", - value="tab-natural-ventilation", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - dcc.Tab( - id="tab-outdoor-comfort", - label="Outdoor Comfort", - value="tab-outdoor-comfort", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - dcc.Tab( - id="tab-data-explorer", - label="Data Explorer", - value="tab-data-explorer", - className="custom-tab", - selected_className="custom-tab--selected", - disabled=True, - ), - ], - ), - html.Div( - id="store-container", - children=[store(), html.Div(id="tabs-content")], - ), - ], - ) - - -def store(): - return html.Div( - id="store", - children=[ - dcc.Loading( - [ - 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"), - ], - fullscreen=True, - type="dot", - ) - ], - ) diff --git a/my_project/page_changelog/app_changelog.py b/my_project/page_changelog/app_changelog.py deleted file mode 100644 index e6b81ee7..00000000 --- a/my_project/page_changelog/app_changelog.py +++ /dev/null @@ -1,12 +0,0 @@ -import dash_bootstrap_components as dbc -from dash import dcc - - -def changelog(): - """changelog page""" - f = open("CHANGELOG.md", "r") - - return dbc.Container( - className="container p-4", - children=[dcc.Markdown(f.read())], - ) diff --git a/my_project/tab_data_explorer/__init__.py b/my_project/tab_data_explorer/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_natural_ventilation/__init__.py b/my_project/tab_natural_ventilation/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_outdoor_comfort/__init__.py b/my_project/tab_outdoor_comfort/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_psy_chart/__init__.py b/my_project/tab_psy_chart/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_select/__init__.py b/my_project/tab_select/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_summary/__init__.py b/my_project/tab_summary/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_sun/__init__.py b/my_project/tab_sun/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_t_rh/__init__.py b/my_project/tab_t_rh/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_under_construction/__init__.py b/my_project/tab_under_construction/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/tab_under_construction/construction.py b/my_project/tab_under_construction/construction.py deleted file mode 100644 index a9f724ff..00000000 --- a/my_project/tab_under_construction/construction.py +++ /dev/null @@ -1,12 +0,0 @@ -from dash import html - - -def construction(): - return html.Div( - className="container-col", - id="construction-container", - children=[ - html.H4("This tab is under construction."), - html.P("Come back soon to check it out!"), - ], - ) diff --git a/my_project/tab_wind/__init__.py b/my_project/tab_wind/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/my_project/__init__.py b/pages/__init__.py similarity index 100% rename from my_project/__init__.py rename to pages/__init__.py diff --git a/pages/changelog.py b/pages/changelog.py new file mode 100644 index 00000000..be4578f6 --- /dev/null +++ b/pages/changelog.py @@ -0,0 +1,23 @@ +import dash +import dash_bootstrap_components as dbc +from dash import dcc + +from config import PageUrls, FilePaths, PageInfo + + +dash.register_page( + __name__, + name=PageInfo.CHANGELOG_NAME, + path=PageUrls.CHANGELOG.value, +) + + +def layout(): + """changelog page""" + with open(FilePaths.CHANGELOG, "r") as f: + changelog_content = f.read() + + return dbc.Container( + className="container p-4", + children=[dcc.Markdown(changelog_content)], + ) diff --git a/my_project/tab_data_explorer/app_data_explorer.py b/pages/explorer.py similarity index 96% rename from my_project/tab_data_explorer/app_data_explorer.py rename to pages/explorer.py index 9090c523..f2c899bd 100644 --- a/my_project/tab_data_explorer/app_data_explorer.py +++ b/pages/explorer.py @@ -1,22 +1,18 @@ +import dash +from dash import dcc, html import dash_bootstrap_components as dbc -from copy import deepcopy -from dash import dcc -from dash import html +from dash_extensions.enrich import Output, Input, State, callback from dash.exceptions import PreventUpdate -from my_project.utils import ( - generate_chart_name, - generate_custom_inputs, - generate_custom_inputs_explorer, - generate_units, - title_with_tooltip, - summary_table_tmp_rh_tab, - code_timer, - title_with_link, - determine_month_and_hour_filter, - dropdown, -) -from my_project.global_scheme import ( +from copy import deepcopy + +from config import PageUrls, PageInfo, DocLinks, UnitSystem +from pages.lib.charts_data_explorer import ( + custom_heatmap, + two_var_graph, + three_var_graph, +) +from pages.lib.global_scheme import ( fig_config, dropdown_names, sun_cloud_tab_dropdown_names, @@ -24,16 +20,8 @@ sun_cloud_tab_explore_dropdown_names, container_row_center_full, container_col_center_one_of_three, - mapping_dictionary, -) -from dash.dependencies import Input, Output, State - -from my_project.tab_data_explorer.charts_data_explorer import ( - custom_heatmap, - two_var_graph, - three_var_graph, ) -from my_project.template_graphs import ( +from pages.lib.template_graphs import ( heatmap, yearly_profile, daily_profile, @@ -41,7 +29,26 @@ filter_df_by_month_and_hour, ) -from app import app +from pages.lib.utils import ( + generate_chart_name, + generate_custom_inputs, + generate_custom_inputs_explorer, + generate_units, + title_with_tooltip, + summary_table_tmp_rh_tab, + title_with_link, + determine_month_and_hour_filter, + dropdown, +) + + +dash.register_page( + __name__, + name=PageInfo.EXPLORER_NAME, + path=PageUrls.EXPLORER.value, + order=PageInfo.EXPLORER_ORDER, +) + explore_dropdown_names = {} explore_dropdown_names.update(deepcopy(dropdown_names)) @@ -76,7 +83,7 @@ def section_one(): children=title_with_link( text="Yearly chart", id_button="explore-yearly-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/temperature-and-humidity/temperatures-explained", + doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( @@ -87,7 +94,7 @@ def section_one(): children=title_with_link( text="Daily chart", id_button="explore-daily-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/temperature-and-humidity/temperatures-explained", + doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( @@ -98,7 +105,7 @@ def section_one(): children=title_with_link( text="Heatmap chart", id_button="explore-heatmap-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/temperature-and-humidity/temperatures-explained", + doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( @@ -604,15 +611,15 @@ def section_three(): ) -def layout_data_explorer(): - """Return the contents of tab six." """ +def layout(): + """Return the contents of tab six.""" return html.Div( - className="continer-col justify-center", + className="justify-center", children=[section_one(), section_two(), section_three()], ) -@app.callback( +@callback( Output("yearly-explore", "children"), # Section One [ @@ -626,7 +633,7 @@ def layout_data_explorer(): State("si-ip-unit-store", "data"), ], ) -def update_tab_yearly(ts, var, global_local, df, meta, si_ip): +def update_tab_yearly(_, var, global_local, df, meta, si_ip): """Update the contents of tab size. Passing in the info from the dropdown and the general info.""" if df[var].mean() == 99990.0: @@ -645,7 +652,7 @@ def update_tab_yearly(ts, var, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("query-daily", "children"), [ Input("df-store", "modified_timestamp"), @@ -658,7 +665,7 @@ def update_tab_yearly(ts, var, global_local, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def update_tab_daily(ts, var, global_local, df, meta, si_ip): +def update_tab_daily(_, var, global_local, df, meta, si_ip): """Update the contents of tab size. Passing in the info from the dropdown and the general info.""" custom_inputs = generate_custom_inputs(var) units = generate_units(si_ip) @@ -670,7 +677,7 @@ def update_tab_daily(ts, var, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("query-heatmap", "children"), [ Input("df-store", "modified_timestamp"), @@ -683,7 +690,7 @@ def update_tab_daily(ts, var, global_local, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def update_tab_heatmap(ts, var, global_local, df, meta, si_ip): +def update_tab_heatmap(_, var, global_local, df, meta, si_ip): """Update the contents of tab size. Passing in the info from the dropdown and the general info.""" custom_inputs = generate_custom_inputs(var) units = generate_units(si_ip) @@ -695,7 +702,7 @@ def update_tab_heatmap(ts, var, global_local, df, meta, si_ip): ) -@app.callback( +@callback( [ Output("custom-heatmap", "children"), Output("custom-summary", "style"), @@ -725,7 +732,7 @@ def update_tab_heatmap(ts, var, global_local, df, meta, si_ip): ], ) def update_heatmap( - ts, + _, var, time_filter, data_filter, @@ -796,7 +803,11 @@ def update_heatmap( {}, ) custom_inputs = f"{var}" - units = "SI" if si_ip == "si" else "IP" if si_ip == "ip" else None + + units = UnitSystem.SI.upper() + if si_ip == UnitSystem.IP: + units = UnitSystem.IP.upper() + return ( dcc.Graph( config=generate_chart_name("heatmap", meta, custom_inputs, units), @@ -808,7 +819,7 @@ def update_heatmap( ) -@app.callback( +@callback( [Output("three-var", "children"), Output("two-var", "children")], [ Input("df-store", "modified_timestamp"), @@ -833,7 +844,7 @@ def update_heatmap( ], ) def update_more_charts( - ts, + _, var_x, var_y, color_by, @@ -900,7 +911,7 @@ def update_more_charts( ) -@app.callback( +@callback( Output("table-data-explorer", "children"), [ Input("df-store", "modified_timestamp"), @@ -917,9 +928,9 @@ def update_more_charts( ], ) def update_table( - ts, + _, dd_value, - n_clicks, + __, df, si_ip, month_range, diff --git a/my_project/page_changelog/__init__.py b/pages/lib/__init__.py similarity index 100% rename from my_project/page_changelog/__init__.py rename to pages/lib/__init__.py diff --git a/my_project/tab_data_explorer/charts_data_explorer.py b/pages/lib/charts_data_explorer.py similarity index 98% rename from my_project/tab_data_explorer/charts_data_explorer.py rename to pages/lib/charts_data_explorer.py index 9211e8c2..2a085bd3 100644 --- a/my_project/tab_data_explorer/charts_data_explorer.py +++ b/pages/lib/charts_data_explorer.py @@ -1,11 +1,10 @@ from math import ceil, floor -import json import numpy as np import math import plotly.express as px import plotly.graph_objects as go -from my_project.global_scheme import template, mapping_dictionary, month_lst +from pages.lib.global_scheme import template, mapping_dictionary, month_lst def custom_heatmap(df, global_local, var, time_filter_info, data_filter_info, si_ip): diff --git a/my_project/tab_summary/charts_summary.py b/pages/lib/charts_summary.py similarity index 100% rename from my_project/tab_summary/charts_summary.py rename to pages/lib/charts_summary.py diff --git a/my_project/tab_sun/charts_sun.py b/pages/lib/charts_sun.py similarity index 98% rename from my_project/tab_sun/charts_sun.py rename to pages/lib/charts_sun.py index ab5c5984..73d43a24 100644 --- a/my_project/tab_sun/charts_sun.py +++ b/pages/lib/charts_sun.py @@ -1,11 +1,12 @@ from datetime import timedelta from math import ceil, cos, floor, radians -import json import numpy as np import pandas as pd import plotly.graph_objects as go -from my_project.global_scheme import ( + +from config import UnitSystem +from pages.lib.global_scheme import ( template, mapping_dictionary, degrees_unit, @@ -27,6 +28,7 @@ def monthly_solar(epw_df, si_ip): rows=1, cols=12, subplot_titles=month_lst, + shared_yaxes=True, ) for i in range(12): @@ -92,10 +94,11 @@ def monthly_solar(epw_df, si_ip): ) fig.update_xaxes(range=[0, 25], row=1, col=i + 1) - if si_ip == "si": - fig.update_yaxes(range=[0, 1000], row=1, col=i + 1) - if si_ip == "ip": - fig.update_yaxes(range=[0, 400], row=1, col=i + 1) + + if si_ip == UnitSystem.SI: + fig.update_yaxes(range=[0, 1000]) + if si_ip == UnitSystem.IP: + fig.update_yaxes(range=[0, 400]) fig.update_layout( template=template, diff --git a/my_project/extract_df.py b/pages/lib/extract_df.py similarity index 97% rename from my_project/extract_df.py rename to pages/lib/extract_df.py index e8cc8cb0..b030a3eb 100644 --- a/my_project/extract_df.py +++ b/pages/lib/extract_df.py @@ -1,11 +1,12 @@ import io import json +import math import re import zipfile from datetime import timedelta from urllib.request import Request, urlopen -import math +import logging import numpy as np import pandas as pd import requests @@ -16,9 +17,11 @@ from pythermalcomfort.models import utci from pythermalcomfort.utilities import running_mean_outdoor_temperature -from my_project.global_scheme import month_lst +from pages.lib.global_scheme import month_lst +from pages.lib.utils import code_timer +@code_timer def get_data(source_url): """Return a list of the data from api call.""" if source_url[-3:] == "zip" or source_url[-3:] == "all": @@ -40,10 +43,12 @@ def get_data(source_url): req = Request(source_url, headers=headers) epw = urlopen(req).read().decode() return epw.split("\n") - except: + except Exception as e: + logging.error(f"Failed to fetch EPW data: {e}") return None +@code_timer def get_location_info(lst, file_name): """Extract and clean the data. Return a pandas data from a url.""" meta = lst[0].strip().replace("\\r", "").split(",") @@ -69,6 +74,7 @@ def get_location_info(lst, file_name): return location_info +@code_timer def create_df(lst, file_name): """Extract and clean the data. Return a pandas data from a url.""" meta = lst[0].strip().replace("\\r", "").split(",") @@ -147,7 +153,7 @@ def create_df(lst, file_name): years = epw_df["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["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 @@ -375,7 +381,7 @@ def convert_data(df, 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 conversion_function_name != None: + if conversion_function_name is not None: conversion_function = globals()[conversion_function_name] conversion_function(df, key) return json.dumps(mapping_dict) diff --git a/my_project/global_scheme.py b/pages/lib/global_scheme.py similarity index 91% rename from my_project/global_scheme.py rename to pages/lib/global_scheme.py index ddc4e884..b71d008c 100644 --- a/my_project/global_scheme.py +++ b/pages/lib/global_scheme.py @@ -1,3 +1,7 @@ +import plotly.io as pio + +from config import UnitSystem + # Colors Dictionary blue_red_yellow = ["#00b3ff", "#000082", "#ff0000", "#ffff00"] dry_humid = ["#ffe600", "#00c8ff", "#0000ff"] @@ -76,8 +80,6 @@ "Dec", ] -import plotly.io as pio - clima_template = "plotly_white" pio.templates.default = clima_template @@ -104,8 +106,8 @@ tight_margins = dict(l=20, r=20, t=33, b=20) # Units Dictionary -degrees_unit = "\u00B0deg" -temperature_unit = "\u00B0C" +degrees_unit = "\u00b0deg" +temperature_unit = "\u00b0C" thermal_stress_label = "Thermal stress" mapping_dictionary = { @@ -132,11 +134,11 @@ "DBT": { "name": "Dry bulb temperature", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-40, 50], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-40, 122], }, @@ -145,11 +147,11 @@ "DPT": { "name": "Dew point temperature", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-50, 35], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-58, 95], }, @@ -158,11 +160,11 @@ "RH": { "name": "Relative humidity", "color": ["#ffe600", "#00c8ff", "#0000ff"], - "si": { + UnitSystem.SI: { "unit": "%", "range": [0, 100], }, - "ip": { + UnitSystem.IP: { "unit": "%", "range": [0, 100], }, @@ -182,11 +184,11 @@ "#cc0000", "#ffaa00", ], - "si": { + UnitSystem.SI: { "unit": "Pa", "range": [95000, 105000], }, - "ip": { + UnitSystem.IP: { "unit": "Psi", "range": [95000 * 0.000145038, 1050000.000145038], }, @@ -203,11 +205,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "Wh/m2", "range": [0, 1200], }, - "ip": { + UnitSystem.IP: { "unit": "Btu/ft2", "range": [0, 1200 * 0.3169983306], }, @@ -224,11 +226,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "Wh/m2", "range": [0, 500], }, - "ip": { + UnitSystem.IP: { "unit": "Btu/ft2", "range": [0, 500 * 0.3169983306], }, @@ -245,11 +247,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "Wh/m2", "range": [0, 1200], }, - "ip": { + UnitSystem.IP: { "unit": "Btu/ft2", "range": [0, 1200 * 0.3169983306], }, @@ -266,11 +268,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "Wh/m2", "range": [0, 1200], }, - "ip": { + UnitSystem.IP: { "unit": "Btu/ft2", "range": [0, 1200 * 0.3169983306], }, @@ -287,11 +289,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "Wh/m2", "range": [0, 1200], }, - "ip": { + UnitSystem.IP: { "unit": "Btu/ft2", "range": [0, 1200 * 0.3169983306], }, @@ -300,11 +302,11 @@ "glob_hor_ill": { "name": "Global horizontal illuminance", "color": ["#4d6daa", "#a0beed", "#f1e969", "#eb7d05", "#d81600"], - "si": { + UnitSystem.SI: { "unit": "lux", "range": [0, 120000], }, - "ip": { + UnitSystem.IP: { "unit": "fc", "range": [0, 120000 * 0.0929], }, @@ -313,11 +315,11 @@ "dir_nor_ill": { "name": "Direct normal illuminance", "color": ["#4d6daa", "#a0beed", "#f1e969", "#eb7d05", "#d81600"], - "si": { + UnitSystem.SI: { "unit": "lux", "range": [0, 120000], }, - "ip": { + UnitSystem.IP: { "unit": "fc", "range": [0, 120000 * 0.0929], }, @@ -326,11 +328,11 @@ "dif_hor_ill": { "name": "Diffuse horizontal illuminance", "color": ["#4d6daa", "#a0beed", "#f1e969", "#eb7d05", "#d81600"], - "si": { + UnitSystem.SI: { "unit": "lux", "range": [0, 120000], }, - "ip": { + UnitSystem.IP: { "unit": "fc", "range": [0, 120000 * 0.0929], }, @@ -339,11 +341,11 @@ "Zlumi": { "name": "Zenith luminance", "color": ["#730a8c", "#0d0db3", "#0f85be", "#0f85be", "#b11421", "#fdf130"], - "si": { + UnitSystem.SI: { "unit": "cd/m2", "range": [0, 60000], }, - "ip": { + UnitSystem.IP: { "unit": "cd/ft2", "range": [0, 60000 * 0.0929], }, @@ -352,11 +354,11 @@ "wind_dir": { "name": "Wind direction", "color": ["#0072dd", "#00c420", "#eded00", "#be00d5", "#0072dd"], - "si": { + UnitSystem.SI: { "unit": "°deg", "range": [0, 360], }, - "ip": { + UnitSystem.IP: { "unit": "°deg", "range": [0, 360], }, @@ -365,7 +367,7 @@ "wind_speed": { "name": "Wind speed", "color": [ - "#ffffff", + "#D3D3D3", "#b2f2ff", "#33ddff", "#00aaff", @@ -376,11 +378,11 @@ "#cc0000", "#ffaa00", ], - "si": { + UnitSystem.SI: { "unit": "m/s", "range": [0, 20], }, - "ip": { + UnitSystem.IP: { "unit": "fpm", "range": [0, 20 * 196.85039370078738], }, @@ -389,11 +391,11 @@ "tot_sky_cover": { "name": "Total sky cover", "color": cloud_colors, - "si": { + UnitSystem.SI: { "unit": "tenths", "range": [0, 10], }, - "ip": { + UnitSystem.IP: { "unit": "tenths", "range": [0, 10], }, @@ -402,11 +404,11 @@ "Oskycover": { "name": "Opaque sky cover", "color": cloud_colors, - "si": { + UnitSystem.SI: { "unit": "tenths", "range": [0, 10], }, - "ip": { + UnitSystem.IP: { "unit": "tenths", "range": [0, 10], }, @@ -415,11 +417,11 @@ "Vis": { "name": "Visibility", "color": cloud_colors, - "si": { + UnitSystem.SI: { "unit": "km", "range": [0, 100], }, - "ip": { + UnitSystem.IP: { "unit": "miles", "range": [0, 100 * 0.6215], }, @@ -436,11 +438,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "°deg", "range": [0, 180], }, - "ip": { + UnitSystem.IP: { "unit": "°deg", "range": [0, 180], }, @@ -457,11 +459,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "°deg", "range": [0, 180], }, - "ip": { + UnitSystem.IP: { "unit": "°deg", "range": [0, 180], }, @@ -478,11 +480,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "°deg", "range": [-90, 90], }, - "ip": { + UnitSystem.IP: { "unit": "°deg", "range": [-90, 90], }, @@ -499,11 +501,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "°deg", "range": [-90, 90], }, - "ip": { + UnitSystem.IP: { "unit": "°deg", "range": [-90, 90], }, @@ -520,11 +522,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "°deg", "range": [0, 360], }, - "ip": { + UnitSystem.IP: { "unit": "°deg", "range": [0, 360], }, @@ -541,11 +543,11 @@ "#ffff7b", "#ffffff", ], - "si": { + UnitSystem.SI: { "unit": "°deg", "range": [-20, 20], }, - "ip": { + UnitSystem.IP: { "unit": "°deg", "range": [-20, 20], }, @@ -554,11 +556,11 @@ "utci_Sun_Wind": { "name": "UTCI: Sun & Wind", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-70, 70], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-94, 158], }, @@ -567,11 +569,11 @@ "utci_noSun_Wind": { "name": "UTCI: no Sun & Wind", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-70, 70], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-94, 158], }, @@ -580,11 +582,11 @@ "utci_Sun_noWind": { "name": "UTCI: Sun & no Wind", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-70, 70], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-94, 158], }, @@ -593,11 +595,11 @@ "utci_noSun_noWind": { "name": "UTCI: no Sun & no Wind", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-70, 70], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-94, 158], }, @@ -627,11 +629,11 @@ [0.9435, "#751613"], [1.0, "#751613"], ], - "si": { + UnitSystem.SI: { "unit": thermal_stress_label, "range": [-5, 4], }, - "ip": { + UnitSystem.IP: { "unit": thermal_stress_label, "range": [-5, 4], }, @@ -661,11 +663,11 @@ [0.9435, "#751613"], [1.0, "#751613"], ], - "si": { + UnitSystem.SI: { "unit": thermal_stress_label, "range": [-5, 4], }, - "ip": { + UnitSystem.IP: { "unit": thermal_stress_label, "range": [-5, 4], }, @@ -695,11 +697,11 @@ [0.9435, "#751613"], [1.0, "#751613"], ], - "si": { + UnitSystem.SI: { "unit": thermal_stress_label, "range": [-5, 4], }, - "ip": { + UnitSystem.IP: { "unit": thermal_stress_label, "range": [-5, 4], }, @@ -729,11 +731,11 @@ [0.9435, "#751613"], [1.0, "#751613"], ], - "si": { + UnitSystem.SI: { "unit": thermal_stress_label, "range": [-5, 4], }, - "ip": { + UnitSystem.IP: { "unit": thermal_stress_label, "range": [-5, 4], }, @@ -742,11 +744,11 @@ "p_vap": { "name": "Vapor partial pressure", "color": ["#ffe600", "#00c8ff", "#0000ff"], - "si": { + UnitSystem.SI: { "unit": "Pa", "range": [0, 5000], }, - "ip": { + UnitSystem.IP: { "unit": "Psi", "range": [0, 5000 * 0.000145038], }, @@ -754,11 +756,11 @@ }, "p_sat": { "name": "Saturation pressure", - "si": { + UnitSystem.SI: { "unit": "Pa", "range": [0, 5000], }, - "ip": { + UnitSystem.IP: { "unit": "Psi", "range": [0, 5000 * 0.000145038], }, @@ -767,11 +769,11 @@ "hr": { "name": "Absolute humidity", "color": ["#ffe600", "#00c8ff", "#0000ff"], - "si": { + UnitSystem.SI: { "unit": "g water/kg dry air", "range": [0, 0.03 * 1000], }, - "ip": { + UnitSystem.IP: { "unit": "lb water/klb dry air", "range": [0, 0.03 * 1000], }, @@ -780,11 +782,11 @@ "t_wb": { "name": "Wet bulb temperature", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-40, 50], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-40, 122], }, @@ -793,11 +795,11 @@ "t_dp": { "name": "Dew point temperature", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "°C", "range": [-40, 50], }, - "ip": { + UnitSystem.IP: { "unit": "°F", "range": [-40, 122], }, @@ -806,11 +808,11 @@ "h": { "name": "Enthalpy", "color": ["#00b3ff", "#000082", "#ff0000", "#ffff00"], - "si": { + UnitSystem.SI: { "unit": "J/kg dry air", "range": [0, 110000], }, - "ip": { + UnitSystem.IP: { "unit": "Btu/lb dry air", "range": [0, 110000 * 0.000429923], }, diff --git a/my_project/import_one_building_files.py b/pages/lib/import_one_building_files.py similarity index 89% rename from my_project/import_one_building_files.py rename to pages/lib/import_one_building_files.py index 415f77d0..c9760733 100644 --- a/my_project/import_one_building_files.py +++ b/pages/lib/import_one_building_files.py @@ -1,8 +1,9 @@ -import re - import pandas as pd +from pages.lib.utils import code_timer +import re +@code_timer def import_kml_files(file_name): with open(f"./assets/data/{file_name}", encoding="utf8", errors="ignore") as data: text = data.read() @@ -76,13 +77,11 @@ def import_kml_files(file_name): ) try: - df_old = pd.read_csv(f"./assets/data/one_building.csv", compression="gzip") + df_old = pd.read_csv("./assets/data/one_building.csv", compression="gzip") df_old = pd.concat([df_old, df]).drop_duplicates() - df_old.to_csv( - f"./assets/data/one_building.csv", index=False, compression="gzip" - ) + df_old.to_csv("./assets/data/one_building.csv", index=False, compression="gzip") except FileNotFoundError: - df.to_csv(f"./assets/data/one_building.csv", index=False, compression="gzip") + df.to_csv("./assets/data/one_building.csv", index=False, compression="gzip") if __name__ == "__main__": diff --git a/pages/lib/layout.py b/pages/lib/layout.py new file mode 100644 index 00000000..558e3745 --- /dev/null +++ b/pages/lib/layout.py @@ -0,0 +1,278 @@ +import dash_bootstrap_components as dbc +import dash +from dash import dcc, html +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from config import DocLinks, UnitSystem + + +def alert(): + """Alert for survey.""" + return html.Div( + id="alert-container", + children=[ + dbc.Toast( + [ + "If you have a moment, help us improve Clima and take a ", + html.A( + "quick user survey", + href="https://forms.gle/k289zP3R92jdu14M7", + className="alert-link", + target="_blank", + ), + "! ☀️", + ], + id="alert-auto", + header="CBE Clima User Survey", + icon="info", + is_open=False, + dismissable=True, + className="survey-alert", + style={"position": "fixed", "top": 25, "right": 10, "width": 400}, + ), + dcc.Interval(id="interval-component", interval=12 * 1000, n_intervals=0), + ], + ) + + +def footer(): + """Build the footer at the bottom of the page.""" + return dbc.Row( + align="center", + justify="between", + id="footer-container", + children=[ + dbc.Col( + children=[ + dbc.Row( + html.A( + children=[ + html.Img( + src="assets/img/cbe-logo.png", + ) + ], + href="https://cbe.berkeley.edu/", + ) + ), + ], + width=12, + md=4, + style={"padding": "15px"}, + ), + dbc.Col( + children=[ + dbc.Row( + [ + dcc.Markdown( + """ + Please cite us: + Betti, G., Tartarini, F., Nguyen, C, Schiavon, S. CBE Clima Tool: + A free and open-source web application for climate analysis tailored to sustainable building design. + Build. Simul. (2023). [https://doi.org/10.1007/s12273-023-1090-5](https://doi.org/10.1007/s12273-023-1090-5). + """ + ), + dmc.Group( + [ + dmc.Anchor( + "Version: 0.9.0", + href="https://center-for-the-built-environment.gitbook.io/clima/version/changelog", + underline=True, + c="white", + target="_blank", + ), + dmc.Anchor( + "Contributors", + href="https://cbe-berkeley.gitbook.io/clima/#contributions", + underline=True, + c="white", + target="_blank", + ), + dmc.Anchor( + "Report issues on GitHub", + href="https://github.com/CenterForTheBuiltEnvironment/clima/issues", + underline=True, + c="white", + target="_blank", + ), + dmc.Anchor( + "Contact us", + href="https://github.com/CenterForTheBuiltEnvironment/clima/discussions", + underline=True, + c="white", + target="_blank", + ), + dmc.Anchor( + "Documentation", + href="https://center-for-the-built-environment.gitbook.io/clima/", + underline=True, + c="white", + target="_blank", + ), + dmc.Anchor( + "License", + href="https://center-for-the-built-environment.gitbook.io/clima/#license", + underline=True, + c="white", + target="_blank", + ), + ], + spacing="sm", + style={"marginTop": "1rem"}, + ), + ], + style={"marginTop": "1rem"}, + ), + ], + width=12, + md=8, + ), + ], + ) + + +def banner(): + """Build the banner at the top of the page.""" + return html.Div( + id="banner", + children=[ + dmc.Group( + position="apart", + align="center", + children=[ + dmc.Group( + align="center", + children=[ + html.A( + href="/", + children=[ + dmc.Image( + src="assets/img/cbe-logo-small.png", + height=40, + width="auto", + ) + ], + ), + dmc.Stack( + spacing=0, + children=[ + dmc.Title( + "CBE Clima Tool", + order=1, + id="banner-title", + style={"fontSize": "2rem"}, + ), + dmc.Text( + "Current Location:", + id="banner-subtitle", + size="sm", + ), + ], + ), + ], + ), + dmc.Group( + align="center", + children=[ + html.A( + dmc.Button( + "Documentation", + leftIcon=DashIconify(icon="bi:book-half", width=20), + variant="filled", + color="#5c7cfa", + ), + href=DocLinks.MAIN.value, + target="_blank", + style={"textDecoration": "none"}, + ), + dmc.SegmentedControl( + id="global-local-radio-input", + value="local", + radius="md", + data=[ + {"label": "Global Value Ranges", "value": "global"}, + {"label": "Local Value Ranges", "value": "local"}, + ], + ), + dmc.SegmentedControl( + id="si-ip-radio-input", + value=UnitSystem.SI, + radius="md", + data=[ + { + "label": UnitSystem.SI.upper(), + "value": UnitSystem.SI, + }, + { + "label": UnitSystem.IP.upper(), + "value": UnitSystem.IP, + }, + ], + ), + ], + ), + ], + ) + ], + ) + + +def store(): + return html.Div( + id="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"), + ], + ) + + +def build_tabs(): + return html.Div( + id="tabs-container", + children=[ + html.Div( + id="tabs-parent", + className="custom-tabs", + children=[ + dbc.Nav( + [ + dbc.NavItem( + dbc.NavLink( + page["name"], + id=page["path"], + href=page["path"], + active="exact", + className="nav-link", + disabled=True, + ), + className="custom-tab", + ) + for page in dash.page_registry.values() + if page["name"] not in ["404", "changelog"] + ], + id="tabs", + class_name="tab-container", + pills=True, + justified=True, + ) + ], + ), + html.Div( + id="store-container", + children=[ + store(), + html.Div( + id="tabs-content", + children=[ + alert(), # alert can be removed after survey is done + dash.page_container, + ], + ), + ], + ), + ], + ) diff --git a/my_project/template_graphs.py b/pages/lib/template_graphs.py similarity index 94% rename from my_project/template_graphs.py rename to pages/lib/template_graphs.py index 6c07e9df..514b0ad4 100644 --- a/my_project/template_graphs.py +++ b/pages/lib/template_graphs.py @@ -1,13 +1,16 @@ -import dash_bootstrap_components as dbc +from math import ceil, floor + import numpy as np import pandas as pd import plotly.graph_objects as go -from math import ceil, floor from plotly.subplots import make_subplots -from my_project.global_scheme import mapping_dictionary +from config import UnitSystem +from pages.lib.global_scheme import mapping_dictionary +import dash_bootstrap_components as dbc from .global_scheme import month_lst, template, tight_margins -from .utils import determine_month_and_hour_filter + +from .utils import code_timer, determine_month_and_hour_filter def violin(df, var, global_local, si_ip): @@ -79,6 +82,7 @@ def violin(df, var, global_local, si_ip): return fig +@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"] @@ -231,6 +235,7 @@ def yearly_profile(df, var, global_local, si_ip): return fig +# @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"] @@ -252,6 +257,7 @@ def daily_profile(df, var, global_local, si_ip): rows=1, cols=12, subplot_titles=month_lst, + shared_yaxes=True, ) for i in range(12): @@ -311,6 +317,7 @@ def daily_profile(df, var, global_local, si_ip): return fig +# @code_timer def heatmap_with_filter( df, var, @@ -357,7 +364,7 @@ def heatmap_with_filter( range_z = [data_min, data_max] fig = go.Figure( data=go.Heatmap( - y=df["hour"], + y=df["hour"] - 0.5, # Offset by 0.5 to center the hour labels x=df["UTC_time"].dt.date, z=df[var], colorscale=var_color, @@ -448,7 +455,6 @@ def heatmap(df, var, global_local, si_ip): return fig -### WIND ROSE TEMPLATE def speed_labels(bins, units): """Return nice labels for a wind speed range.""" labels = [] @@ -456,9 +462,9 @@ def speed_labels(bins, units): if left == bins[0]: labels.append("calm") elif np.isinf(right): - labels.append(">{} {}".format(left, units)) + labels.append(f">{left} {units}") else: - labels.append("{} - {} {}".format(left, right, units)) + labels.append(f"{left} - {right} {units}") return labels @@ -476,14 +482,14 @@ def wind_rose(df, title, month, hour, labels, si_ip): else: df = df.loc[(df["month"] <= end_month) | (df["month"] >= start_month)] if start_hour <= end_hour: - df = df.loc[(df["hour"] >= start_hour) & (df["hour"] <= end_hour)] + df = df.loc[(df["hour"] > start_hour) & (df["hour"] <= end_hour)] else: df = df.loc[(df["hour"] <= end_hour) | (df["hour"] >= start_hour)] spd_colors = mapping_dictionary["wind_speed"]["color"] spd_unit = mapping_dictionary["wind_speed"][si_ip]["unit"] spd_bins = [-1, 0.5, 1.5, 3.3, 5.5, 7.9, 10.7, 13.8, 17.1, 20.7, np.inf] - if si_ip == "ip": + if si_ip == UnitSystem.IP: spd_bins = convert_bins(spd_bins) spd_labels = speed_labels(spd_bins, spd_unit) @@ -491,26 +497,30 @@ def wind_rose(df, title, month, hour, labels, si_ip): dir_labels = (dir_bins[:-1] + dir_bins[1:]) / 2 total_count = df.shape[0] calm_count = df.query("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 + ), + WindDir_bins=lambda d: pd.cut( + d["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}) + rose = ( - df.assign( - WindSpd_bins=lambda df: pd.cut( - df["wind_speed"], bins=spd_bins, labels=spd_labels, right=True - ) - ) - .assign( - WindDir_bins=lambda df: pd.cut( - df["wind_dir"], bins=dir_bins, labels=dir_labels, right=False - ) - ) - .replace({"WindDir_bins": {360: 0}}) - .groupby(by=["WindSpd_bins", "WindDir_bins"]) + df_binned.groupby(by=["WindSpd_bins", "WindDir_bins"], observed=False) .size() .unstack(level="WindSpd_bins") .fillna(0) - .assign(calm=lambda df: calm_count / df.shape[0]) + .assign(calm=lambda d: calm_count / d.shape[0]) .sort_index(axis=1) - .applymap(lambda x: x / total_count * 100) + .map(lambda x: x / total_count * 100) ) + fig = go.Figure() for i, col in enumerate(rose.columns): fig.add_trace( @@ -522,7 +532,7 @@ def wind_rose(df, title, month, hour, labels, si_ip): hovertemplate="frequency: %{r:.2f}%" + "
" + "direction: %{theta:.2f}" - + "\u00B0 deg" + + "\u00b0 deg" + "
", ) ) @@ -556,6 +566,7 @@ def wind_rose(df, title, month, hour, labels, si_ip): showlegend=labels, dragmode=False, margin=tight_margins, + legend_title_text=f"Wind Speed ({spd_unit})", ) fig.update_xaxes(showline=True, linewidth=1, linecolor="black", mirror=True) fig.update_yaxes(showline=True, linewidth=1, linecolor="black", mirror=True) @@ -826,10 +837,10 @@ def filter_df_by_month_and_hour( df.loc[mask, var] = None if start_hour <= end_hour: - mask = (df["hour"] < start_hour) | (df["hour"] > end_hour) + mask = (df["hour"] <= start_hour) | (df["hour"] > end_hour) df.loc[mask, var] = None else: - mask = (df["hour"] >= end_hour) & (df["hour"] <= start_hour) + mask = (df["hour"] > end_hour) & (df["hour"] <= start_hour) df.loc[mask, var] = None return df @@ -839,5 +850,5 @@ def catch(func, handle=lambda e: e, *args, **kwargs): # Handle category not in dictionary try: return func(*args, **kwargs) - except Exception as e: + except (KeyError, IndexError, TypeError): return 0 diff --git a/my_project/utils.py b/pages/lib/utils.py similarity index 74% rename from my_project/utils.py rename to pages/lib/utils.py index f528c511..860a0409 100644 --- a/my_project/utils.py +++ b/pages/lib/utils.py @@ -1,16 +1,13 @@ import copy import functools -import json import time import dash_bootstrap_components as dbc import pandas as pd -import plotly.express as px -import requests from dash import html, dash_table, dcc -from pandas import json_normalize -from my_project.global_scheme import fig_config, mapping_dictionary +from config import UnitSystem +from pages.lib.global_scheme import fig_config, mapping_dictionary, month_lst def code_timer(func): @@ -36,20 +33,23 @@ def generate_chart_name(tab_name, meta=None, custom_inputs=None, units=None): if units: custom_str += f"_{units}" if meta: - figure_config["toImageButtonOptions"][ - "filename" - ] = f"{meta['city']}_{meta['country']}_{tab_name}{custom_str}" + file_name = f"{meta['city']}_{meta['country']}_{tab_name}{custom_str}" + figure_config["toImageButtonOptions"]["filename"] = file_name else: figure_config["toImageButtonOptions"]["filename"] = f"{tab_name}{custom_str}" return figure_config def generate_units(si_ip): - return "SI" if si_ip == "si" else "IP" if si_ip == "ip" else None + """Generate units for the chart titles.""" + if si_ip == UnitSystem.SI: + return UnitSystem.SI + else: + return UnitSystem.IP def generate_units_degree(si_ip): - return "C" if si_ip == "si" else "F" if si_ip == "ip" else None + return "C" if si_ip == UnitSystem.SI else "F" if si_ip == UnitSystem.IP else None def generate_custom_inputs(var): @@ -62,21 +62,7 @@ def generate_custom_inputs(var): def generate_custom_inputs_time(start_month, end_month, start_hour, end_hour): - month_names = [ - "", - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ] + month_names = [""] + month_lst start_month_abbr = month_names[int(start_month)] end_month_abbr = month_names[int(end_month)] custom_inputs = ( @@ -88,21 +74,7 @@ def generate_custom_inputs_time(start_month, end_month, start_hour, end_hour): def generate_custom_inputs_nv( start_month, end_month, start_hour, end_hour, min_dbt_val, max_dbt_val ): - month_names = [ - "", - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ] + month_names = [""] + month_lst start_month_abbr = month_names[int(start_month)] end_month_abbr = month_names[int(end_month)] custom_inputs = f"{min_dbt_val:02d}-{max_dbt_val:02d}_{start_month_abbr}-{end_month_abbr}_{start_hour:02d}-{end_hour:02d}" @@ -112,21 +84,7 @@ def generate_custom_inputs_nv( def generate_custom_inputs_explorer( var, start_month, end_month, start_hour, end_hour, filter_var, min_val, max_val ): - month_names = [ - "", - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ] + month_names = [""] + month_lst start_month_abbr = month_names[int(start_month)] end_month_abbr = month_names[int(end_month)] if var in mapping_dictionary: @@ -156,21 +114,7 @@ def generate_custom_inputs_psy( min_val, max_val, ): - month_names = [ - "", - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ] + month_names = [""] + month_lst start_month_abbr = month_names[int(start_month)] end_month_abbr = month_names[int(end_month)] if colorby_var in mapping_dictionary: @@ -195,59 +139,6 @@ def generate_custom_inputs_psy( return custom_inputs -def plot_location_epw_files(): - 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.005 - df["lat"] += 0.005 - df = df.rename(columns={"properties.epw": "Source"}) - - fig = px.scatter_mapbox( - df.head(2585), - lat="lat", - lon="lon", - hover_name="properties.title", - color_discrete_sequence=["#3a0ca3"], - hover_data=["Source"], - zoom=2, - height=500, - ) - try: - print(requests.get("https://climate.onebuilding.org", timeout=6)) - - df_one_building = pd.read_csv( - "./assets/data/one_building.csv", compression="gzip" - ) - - fig2 = px.scatter_mapbox( - df_one_building, - lat="lat", - lon="lon", - hover_name=df_one_building["name"], - color_discrete_sequence=["#4895ef"], - hover_data=[ - "period", - "elevation (m)", - "time zone (GMT)", - "99% Heating DB", - "1% Cooling DB ", - "Source", - ], - zoom=2, - height=500, - ) - fig.add_trace(fig2.data[0]) - except requests.exceptions.ConnectTimeout: - pass - fig.update_layout(mapbox_style="carto-positron") - fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0}) - - return fig - - def title_with_tooltip(text, tooltip_text, id_button): display_tooltip = "none" if tooltip_text: @@ -287,7 +178,7 @@ def title_with_link( text, tooltip_text="Click to access the official documentation", id_button=None, - doc_link=None, + doc_link: str = "", ): return html.Div( className="container-row", @@ -377,12 +268,14 @@ def determine_month_and_hour_filter(month, hour, invert_month, invert_hour): return start_month, end_month, start_hour, end_hour -def dropdown(options={}, **kwargs): +def dropdown(options=None, **kwargs): """ Wrapper for dcc.Dropdown which - makes "clearable=False" the default, so we don't need to handle None - accepts dicts, and preserves order. """ + if options is None: + options = {} return dcc.Dropdown( options=[{"label": k, "value": v} for k, v in options.items()], clearable=False, diff --git a/my_project/tab_natural_ventilation/app_natural_ventilation.py b/pages/natural_ventilation.py similarity index 88% rename from my_project/tab_natural_ventilation/app_natural_ventilation.py rename to pages/natural_ventilation.py index 70f3d280..32c62cdb 100644 --- a/my_project/tab_natural_ventilation/app_natural_ventilation.py +++ b/pages/natural_ventilation.py @@ -1,10 +1,15 @@ import math -import json -from dash import dcc + +import dash +from dash import dcc, html import dash_bootstrap_components as dbc -from dash import html +from dash_extensions.enrich import Output, Input, State, callback + +import numpy as np import plotly.graph_objects as go -from my_project.global_scheme import ( + +from config import PageUrls, DocLinks, PageInfo, UnitSystem +from pages.lib.global_scheme import ( template, mapping_dictionary, tight_margins, @@ -12,24 +17,39 @@ container_row_center_full, container_col_center_one_of_three, ) -from dash.dependencies import Input, Output, State -import numpy as np - -from my_project.template_graphs import filter_df_by_month_and_hour -from my_project.utils import ( +from pages.lib.template_graphs import filter_df_by_month_and_hour +from pages.lib.utils import ( title_with_tooltip, generate_chart_name, generate_units_degree, generate_units, generate_custom_inputs_nv, determine_month_and_hour_filter, + title_with_link, ) -from app import app + +dash.register_page( + __name__, + name=PageInfo.NATURAL_VENTILATION_NAME, + path=PageUrls.NATURAL_VENTILATION.value, + order=PageInfo.NATURAL_VENTILATION_ORDER, +) -def layout_natural_ventilation(si_ip): - if si_ip == "ip": +def layout(): + return html.Div( + className="container-col", + id="main-nv-section", + children=[ + # + ], + ) + + +@callback(Output("main-nv-section", "children"), [Input("si-ip-radio-input", "value")]) +def update_layout(si_ip): + if si_ip == UnitSystem.IP: tdb_set_min = 50 tdb_set_max = 75 dpt_set = 61 @@ -38,54 +58,58 @@ def layout_natural_ventilation(si_ip): tdb_set_max = 24 dpt_set = 16 - return html.Div( - className="container-col", - children=[ - inputs_tab(tdb_set_min, tdb_set_max, dpt_set), - dcc.Loading( - html.Div( - id="nv-heatmap-chart", - style={"marginTop": "1rem"}, - ), - type="circle", + return [ + html.Div( + children=title_with_link( + text="Natural Ventilation Potential", + id_button="natural-ventilation-label", + doc_link=DocLinks.NATURAL_VENTILATION, ), + ), + inputs_tab(tdb_set_min, tdb_set_max, dpt_set), + dcc.Loading( html.Div( - className="container-row align-center justify-center", - children=[ - dbc.Checklist( - options=[ - {"label": "", "value": 1}, - ], - value=[1], - id="switches-input", - switch=True, - style={ - "padding": "1rem", - "marginTop": "1rem", - "marginRight": "-2rem", - }, - ), - html.Div( - children=title_with_tooltip( - text="Normalize data", - tooltip_text=( - "If normalized is enabled it calculates the % " - "time otherwise it calculates the total number of hours" - ), - id_button="nv_normalize", - ), - ), - ], + id="nv-heatmap-chart", + style={"marginTop": "1rem"}, ), - dcc.Loading( + type="circle", + ), + html.Div( + className="container-row align-center justify-center", + children=[ + dbc.Checklist( + options=[ + {"label": "", "value": 1}, + ], + value=[1], + id="switches-input", + switch=True, + style={ + "padding": "1rem", + "marginTop": "1rem", + "marginRight": "-2rem", + }, + ), html.Div( - id="nv-bar-chart", - style={"marginTop": "1rem"}, + children=title_with_tooltip( + text="Normalize data", + tooltip_text=( + "If normalized is enabled it calculates the % " + "time otherwise it calculates the total number of hours" + ), + id_button="nv_normalize", + ), ), - type="circle", + ], + ), + dcc.Loading( + html.Div( + id="nv-bar-chart", + style={"marginTop": "1rem"}, ), - ], - ) + type="circle", + ), + ] def inputs_tab(t_min, t_max, d_set): @@ -254,7 +278,7 @@ def inputs_tab(t_min, t_max, d_set): ) -@app.callback( +@callback( Output("nv-heatmap-chart", "children"), [ Input("df-store", "modified_timestamp"), @@ -362,7 +386,7 @@ def nv_heatmap( fig = go.Figure( data=go.Heatmap( - y=df["hour"], + y=df["hour"] - 0.5, # Offset by 0.5 to center the hour labels x=df["UTC_time"].dt.date, z=df[var], colorscale=var_color, @@ -390,7 +414,7 @@ def nv_heatmap( template=template, title=title, yaxis_nticks=13, - yaxis=dict(range=(1, 24)), + yaxis=dict(range=(0, 24)), margin=tight_margins.copy().update({"t": 55}), ) @@ -421,7 +445,7 @@ def nv_heatmap( ) -@app.callback( +@callback( Output("nv-bar-chart", "children"), [ Input("df-store", "modified_timestamp"), @@ -585,9 +609,7 @@ def nv_bar_chart( ) -@app.callback( - Output("nv-dpt-filter", "disabled"), Input("enable-condensation", "value") -) +@callback(Output("nv-dpt-filter", "disabled"), Input("enable-condensation", "value")) def enable_disable_button_data_filter(state_checklist): if len(state_checklist) == 1: return False diff --git a/my_project/not_found_404.py b/pages/not_found_404.py similarity index 64% rename from my_project/not_found_404.py rename to pages/not_found_404.py index 9e077fec..ff3b389c 100644 --- a/my_project/not_found_404.py +++ b/pages/not_found_404.py @@ -3,12 +3,25 @@ from dash_iconify import DashIconify from dash_extensions import Lottie -dash.register_page(__name__) +from config import PageUrls, Assets, PageInfo + + +dash.register_page( + __name__, name=PageInfo.NOT_FOUND_NAME, path=PageUrls.NOT_FOUND.value +) + layout = [ dmc.Title("I could not find the page you are currently looking for", order=4), - dmc.Text( - "Please navigate the the home page by using the button below", className="mb-2" + dmc.Text("Use the button below to return to the home page.", className="mb-2"), + Lottie( + options=dict( + loop=True, + autoplay=True, + rendererSettings=dict(preserveAspectRatio="xMidYMid slice"), + ), + width="20%", + url=Assets.NOT_FOUND_ANIMATION, ), dmc.Anchor( dmc.Button( @@ -16,15 +29,6 @@ fullWidth=True, leftIcon=DashIconify(icon="material-symbols:home-outline-rounded"), ), - href="/", - ), - Lottie( - options=dict( - loop=True, - autoplay=True, - rendererSettings=dict(preserveAspectRatio="xMidYMid slice"), - ), - width="100%", - url="../assets/animations/page_not_found.json", + href=PageUrls.SELECT.value, ), ] diff --git a/my_project/tab_outdoor_comfort/app_outdoor_comfort.py b/pages/outdoor.py similarity index 88% rename from my_project/tab_outdoor_comfort/app_outdoor_comfort.py rename to pages/outdoor.py index aab6edfa..ab8d53e1 100644 --- a/my_project/tab_outdoor_comfort/app_outdoor_comfort.py +++ b/pages/outdoor.py @@ -1,21 +1,20 @@ -from dash import dcc -from dash import html +import dash +from dash import dcc, html import dash_bootstrap_components as dbc -from my_project.global_scheme import ( +from dash_extensions.enrich import Output, Input, State, callback + +import numpy as np + +from config import PageUrls, DocLinks, PageInfo +from pages.lib.global_scheme import ( outdoor_dropdown_names, - tight_margins, - month_lst, - container_row_center_full, - container_col_center_one_of_three, ) -from my_project.utils import dropdown -from dash.dependencies import Input, Output, State - -from my_project.template_graphs import ( +from pages.lib.template_graphs import ( heatmap_with_filter, thermal_stress_stacked_barchart, ) -from my_project.utils import ( +from pages.lib.utils import ( + dropdown, generate_chart_name, generate_units_degree, generate_units, @@ -24,8 +23,12 @@ ) -import numpy as np -from app import app +dash.register_page( + __name__, + name=PageInfo.UTCI_NAME, + path=PageUrls.OUTDOOR.value, + order=PageInfo.UTCI_ORDER, +) def inputs_outdoor_comfort(): @@ -142,7 +145,7 @@ def outdoor_comfort_chart(): children=title_with_link( text="UTCI heatmap chart", id_button="utci-charts-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/outdoor-comfort/utci-explained", + doc_link=DocLinks.UTCI_CHART, ) ), dcc.Loading( @@ -153,7 +156,7 @@ def outdoor_comfort_chart(): children=title_with_link( text="UTCI thermal stress chart", id_button="utci-charts-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/outdoor-comfort/utci-explained", + doc_link=DocLinks.UTCI_CHART, ) ), dcc.Loading( @@ -196,7 +199,7 @@ def outdoor_comfort_chart(): ) -def layout_outdoor_comfort(): +def layout(): return ( dcc.Loading( type="circle", @@ -208,7 +211,7 @@ def layout_outdoor_comfort(): ) -@app.callback( +@callback( Output("outdoor-comfort-output", "children"), [ Input("df-store", "modified_timestamp"), @@ -217,30 +220,43 @@ def layout_outdoor_comfort(): State("df-store", "data"), ], ) -def update_outdoor_comfort_output(ts, df): +def update_outdoor_comfort_output(_, df): + """ + Find the column(s) with the highest number of zero values. + + Args: + _: Unused callback input. + df: DataFrame-like object containing UTCI category columns. + + Returns + ------- + str + Description of the best weather condition(s). + """ cols = [ "utci_noSun_Wind_categories", "utci_noSun_noWind_categories", "utci_Sun_Wind_categories", "utci_Sun_noWind_categories", ] - colsWithTheHighestNumberOfZero = [] - highestCount = 0 + cols_with_the_highest_number_of_zero = [] + highest_count = 0 for col in cols: try: - count = df[col].value_counts()[0] # this can cause error if there is no 0 - if count > highestCount: - highestCount = count - colsWithTheHighestNumberOfZero.clear() - colsWithTheHighestNumberOfZero.append(col) - elif count == highestCount: - colsWithTheHighestNumberOfZero.append(col) - except: + count = df[col].value_counts()[0] + except (KeyError, TypeError): + # KeyError: 0 not in value_counts; TypeError: df[col] is not valid continue - return f"The Best Weather Condition is: {', '.join(colsWithTheHighestNumberOfZero)}" + if count > highest_count: + highest_count = count + cols_with_the_highest_number_of_zero.clear() + cols_with_the_highest_number_of_zero.append(col) + elif count == highest_count: + cols_with_the_highest_number_of_zero.append(col) + return f"The Best Weather Condition is: {', '.join(cols_with_the_highest_number_of_zero)}" -@app.callback( +@callback( Output("utci-heatmap", "children"), [ Input("df-store", "modified_timestamp"), @@ -259,7 +275,7 @@ def update_outdoor_comfort_output(ts, df): ], ) def update_tab_utci_value( - ts, + _, var, global_local, time_filter, @@ -290,7 +306,7 @@ def update_tab_utci_value( ) -@app.callback( +@callback( Output("image-selection", "children"), Input("tab7-dropdown", "value"), ) @@ -307,7 +323,7 @@ def change_image_based_on_selection(value): return html.Img(src=source, height=50) -@app.callback( +@callback( Output("utci-category-heatmap", "children"), [ Input("df-store", "modified_timestamp"), @@ -326,7 +342,7 @@ def change_image_based_on_selection(value): ], ) def update_tab_utci_category( - ts, + _, var, global_local, time_filter, @@ -377,7 +393,7 @@ def update_tab_utci_category( ) -@app.callback( +@callback( Output("utci-summary-chart", "children"), [ Input("tab7-dropdown", "value"), diff --git a/my_project/tab_psy_chart/app_psy_chart.py b/pages/psy-chart.py similarity index 96% rename from my_project/tab_psy_chart/app_psy_chart.py rename to pages/psy-chart.py index f3a5b56c..2c46b885 100644 --- a/my_project/tab_psy_chart/app_psy_chart.py +++ b/pages/psy-chart.py @@ -1,18 +1,30 @@ -import numpy as np -import plotly.graph_objects as go -import json -from pythermalcomfort import psychrometrics as psy from math import ceil, floor + +import dash +from dash import dcc, html import dash_bootstrap_components as dbc +from dash_extensions.enrich import Output, Input, State, callback + from copy import deepcopy -from dash import dcc -from dash import html -from my_project.global_scheme import ( +import numpy as np +import pandas as pd +import plotly.graph_objects as go +from pythermalcomfort import psychrometrics as psy + +from config import PageUrls, DocLinks, PageInfo, UnitSystem +from pages.lib.global_scheme import ( container_row_center_full, container_col_center_one_of_three, + dropdown_names, + sun_cloud_tab_dropdown_names, + more_variables_dropdown, + sun_cloud_tab_explore_dropdown_names, + template, + mapping_dictionary, + tight_margins, ) -from my_project.template_graphs import filter_df_by_month_and_hour -from my_project.utils import ( +from pages.lib.template_graphs import filter_df_by_month_and_hour +from pages.lib.utils import ( generate_chart_name, generate_units, generate_custom_inputs_psy, @@ -20,23 +32,16 @@ title_with_link, dropdown, ) -from my_project.global_scheme import ( - dropdown_names, - sun_cloud_tab_dropdown_names, - more_variables_dropdown, - sun_cloud_tab_explore_dropdown_names, -) -from dash.dependencies import Input, Output, State -import pandas as pd -from app import app -from my_project.global_scheme import ( - template, - mapping_dictionary, - tight_margins, +dash.register_page( + __name__, + name=PageInfo.PSYCHROMETRIC_NAME, + path=PageUrls.PSY_CHART.value, + order=PageInfo.PSYCHROMETRIC_ORDER, ) + psy_dropdown_names = { "None": "None", "Frequency": "Frequency", @@ -207,13 +212,13 @@ def inputs(): ) -def layout_psy_chart(): +def layout(): return ( html.Div( children=title_with_link( text="Psychrometric Chart", id_button="Psychrometric-Chart-chart", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/psychrometric-chart", + doc_link=DocLinks.PSYCHROMETRIC_CHART, ), ), dcc.Loading( @@ -227,7 +232,7 @@ def layout_psy_chart(): # psychrometric chart -@app.callback( +@callback( Output("psych-chart", "children"), [ Input("df-store", "modified_timestamp"), @@ -347,7 +352,7 @@ def update_psych_chart( for k in range(len(rh_df[name])): rh_multiply[k] = rh_multiply[k] * 1000 - if si_ip == "ip": + if si_ip == UnitSystem.IP: for j in range(len(dbt_list)): dbt_list_convert[j] = dbt_list_convert[j] * 1.8 + 32 diff --git a/my_project/tab_select/app_select.py b/pages/select.py similarity index 69% rename from my_project/tab_select/app_select.py rename to pages/select.py index a7d38672..d4e4af13 100644 --- a/my_project/tab_select/app_select.py +++ b/pages/select.py @@ -4,14 +4,26 @@ import dash import dash_bootstrap_components as dbc +import dash_mantine_components as dmc +import pandas as pd +import plotly.express as px from dash.exceptions import PreventUpdate -from dash_extensions.enrich import Serverside, Output, Input, State, html, dcc +from dash_extensions.enrich import Serverside, Output, Input, State, html, dcc, callback +from pandas import json_normalize + +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_scheme import mapping_dictionary +from config import PageUrls, PageInfo, UnitSystem +from pages.lib.utils import generate_chart_name + +dash.register_page( + __name__, + name=PageInfo.SELECT_NAME, + path=PageUrls.SELECT.value, + order=PageInfo.SELECT_ORDER, +) -from app import app -from my_project.extract_df import convert_data -from my_project.extract_df import create_df, get_data, get_location_info -from my_project.global_scheme import mapping_dictionary -from my_project.utils import plot_location_epw_files, generate_chart_name messages_alert = { "start": "To start, upload an EPW file or click on a point on the map!", @@ -22,7 +34,7 @@ } -def layout_select(): +def layout(): """Contents in the first tab 'Select Weather File'""" return html.Div( className="container-col tab-container", @@ -50,14 +62,14 @@ def layout_select(): multiple=True, className="d-grid", ), - dcc.Graph( - id="tab-one-map", - figure=plot_location_epw_files(), - config=generate_chart_name("epw_location_select"), + dmc.Skeleton( + visible=False, + id="skeleton-graph-container", + height=500, + children=html.Div(id="tab-one-map"), ), dbc.Modal( [ - # dbc.ModalHeader("Header"), dbc.ModalHeader(id="modal-header"), dbc.ModalFooter( children=[ @@ -85,22 +97,18 @@ def layout_select(): def alert(): """Alert layout for the submit button.""" - return html.Div( - [ - dbc.Alert( - messages_alert["start"], - color="primary", - id="alert", - dismissable=False, - is_open=True, - style={"maxHeight": "66px"}, - ) - ] + return dbc.Alert( + messages_alert["start"], + color="primary", + id="alert", + dismissable=False, + is_open=True, + style={"maxHeight": "66px"}, ) # add si-ip and map dictionary in the output -@app.callback( +@callback( [ Output("meta-store", "data"), Output("lines-store", "data"), @@ -121,8 +129,8 @@ def alert(): ) # @code_timer def submitted_data( - use_epw_click, - upload_click, + _, + __, list_of_contents, list_of_names, url_store, @@ -182,8 +190,8 @@ def submitted_data( messages_alert["invalid_format"], "warning", ) - except Exception as e: - # print(e) + except (ValueError, IndexError, KeyError) as e: + print(f"Error parsing EPW file: {e}") return ( None, None, @@ -195,7 +203,7 @@ def submitted_data( # add switch_si_ip function and convert the data-store -@app.callback( +@callback( [ Output("df-store", "data"), Output("si-ip-unit-store", "data"), @@ -206,11 +214,11 @@ def submitted_data( ], [State("url-store", "data"), State("lines-store", "data")], ) -def switch_si_ip(ts, si_ip_input, url_store, lines): +def switch_si_ip(_, si_ip_input, url_store, lines): if lines is not None: df, _ = create_df(lines, url_store) map_json = json.dumps(mapping_dictionary) - if si_ip_input == "ip": + if si_ip_input == UnitSystem.IP: map_json = convert_data(df, map_json) return Serverside(df), si_ip_input else: @@ -220,16 +228,17 @@ def switch_si_ip(ts, si_ip_input, url_store, lines): ) -@app.callback( +@callback( [ - Output("tab-summary", "disabled"), - Output("tab-t-rh", "disabled"), - Output("tab-sun", "disabled"), - Output("tab-wind", "disabled"), - Output("tab-psy-chart", "disabled"), - Output("tab-data-explorer", "disabled"), - Output("tab-outdoor-comfort", "disabled"), - Output("tab-natural-ventilation", "disabled"), + Output("/", "disabled"), + Output("/summary", "disabled"), + Output("/t-rh", "disabled"), + Output("/sun", "disabled"), + Output("/wind", "disabled"), + Output("/psy-chart", "disabled"), + Output("/explorer", "disabled"), + Output("/outdoor", "disabled"), + Output("/natural-ventilation", "disabled"), Output("banner-subtitle", "children"), ], [ @@ -250,6 +259,7 @@ def enable_tabs_when_data_is_loaded(meta, data): True, True, True, + True, default, ) else: @@ -262,11 +272,12 @@ def enable_tabs_when_data_is_loaded(meta, data): False, False, False, + False, "Current Location: " + meta["city"] + ", " + meta["country"], ) -@app.callback( +@callback( [ Output("modal", "is_open"), Output("url-store", "data"), @@ -279,7 +290,7 @@ def enable_tabs_when_data_is_loaded(meta, data): [State("modal", "is_open")], prevent_initial_call=True, ) -def display_modal_when_data_clicked(clicks_use_epw, click_map, close_clicks, is_open): +def display_modal_when_data_clicked(_, click_map, __, is_open): """display the modal to the user and check if he wants to use that file""" if click_map: url = re.search( @@ -289,7 +300,7 @@ def display_modal_when_data_clicked(clicks_use_epw, click_map, close_clicks, is_ return is_open, "" -@app.callback( +@callback( [ Output("modal-header", "children"), ], @@ -298,8 +309,67 @@ def display_modal_when_data_clicked(clicks_use_epw, click_map, close_clicks, is_ ], prevent_initial_call=True, ) -def display_modal_when_data_clicked(click_map): +def change_text_modal(click_map): """change the text of the modal header""" if click_map: return [f"Analyse data from {click_map['points'][0]['hovertext']}?"] return ["Analyse data from this location?"] + + +@callback( + Output("skeleton-graph-container", "children"), + Input("url", "pathname"), +) +def plot_location_epw_files(pathname): + # print(pathname) + if pathname != "/": + raise PreventUpdate + + 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 = df.rename(columns={"properties.epw": "Source"}) + + fig = px.scatter_mapbox( + df.head(2585), + lat="lat", + lon="lon", + hover_name="properties.title", + color_discrete_sequence=["#3a0ca3"], + hover_data=["Source"], + zoom=2, + height=500, + ) + df_one_building = pd.read_csv("./assets/data/one_building.csv", compression="gzip") + + fig2 = px.scatter_mapbox( + df_one_building, + lat="lat", + lon="lon", + hover_name=df_one_building["name"], + color_discrete_sequence=["#4895ef"], + hover_data=[ + "period", + "elevation (m)", + "time zone (GMT)", + "99% Heating DB", + "1% Cooling DB ", + "Source", + ], + zoom=2, + height=500, + ) + fig.add_trace(fig2.data[0]) + fig.update_layout(mapbox_style="carto-positron") + fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0}) + + return ( + dcc.Graph( + id="tab-one-map", + figure=fig, + config=generate_chart_name("epw_location_select"), + ), + ) diff --git a/my_project/tab_summary/app_summary.py b/pages/summary.py similarity index 65% rename from my_project/tab_summary/app_summary.py rename to pages/summary.py index d0413724..733056ad 100644 --- a/my_project/tab_summary/app_summary.py +++ b/pages/summary.py @@ -1,16 +1,17 @@ import dash import dash_bootstrap_components as dbc +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import dcc, html, Output, Input, State, callback + import plotly.graph_objects as go import requests -from dash.exceptions import PreventUpdate -from dash_extensions.enrich import dcc, html, Output, Input, State - -from app import app -from my_project.extract_df import get_data -from my_project.global_scheme import template, tight_margins, mapping_dictionary -from my_project.tab_summary.charts_summary import world_map -from my_project.template_graphs import violin -from my_project.utils import ( + +from config import PageUrls, DocLinks, PageInfo, UnitSystem +from pages.lib.charts_summary import world_map +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.utils import ( generate_chart_name, generate_units, generate_units_degree, @@ -19,9 +20,31 @@ ) -def layout_summary(si_ip): +dash.register_page( + __name__, + name=PageInfo.SUMMARY_NAME, + path=PageUrls.SUMMARY.value, + order=PageInfo.SUMMARY_ORDER, +) + + +def layout(): """Contents in the second tab 'Climate Summary'.""" - if si_ip == "si": + + return html.Div( + className="container-col", + id="tab-two-container", + children=[ + # + ], + ) + + +@callback( + Output("tab-two-container", "children"), [Input("si-ip-radio-input", "value")] +) +def update_layout(si_ip): + if si_ip == UnitSystem.SI: heating_setpoint = 10 cooling_setpoint = 18 else: @@ -30,145 +53,150 @@ def layout_summary(si_ip): return html.Div( className="container-col", - id="tab-two-container", + id="tab2-sec1-container", children=[ + dcc.Loading( + type="circle", + children=html.Div( + className="container-col", + id="location-info", + style={"padding": "12px"}, + ), + ), + dcc.Loading( + type="circle", + children=html.Div(className="tab-two-section", id="world-map"), + ), html.Div( - className="container-col", - id="tab2-sec1-container", - children=[ - dcc.Loading( - type="circle", - children=html.Div( - className="container-col", - id="location-info", - style={"padding": "12px"}, + children=title_with_tooltip( + text="Download", + id_button="download-button-label", + tooltip_text="Use the following buttons to download either the Clima sourcefile or the EPW file", + ), + ), + dcc.Loading( + type="circle", + children=dbc.Row( + [ + dbc.Col( + dbc.Button( + "Download EPW", + color="primary", + id="download-epw-button", + ), + width="auto", ), - ), - dcc.Loading( - type="circle", - children=html.Div(className="tab-two-section", id="world-map"), - ), - html.Div( - children=title_with_tooltip( - text="Download", - id_button="download-button-label", - tooltip_text="Use the following buttons to download either the Clima sourcefile or the EPW file", + dbc.Col( + dbc.Button( + "Download Clima dataframe", + color="primary", + id="download-button", + ), + width="auto", ), - ), - dcc.Loading( - type="circle", - children=dbc.Row( + dbc.Col( [ - dbc.Col( - dbc.Button( - "Download EPW", - color="primary", - id="download-epw-button", - ), - width="auto", - ), - dbc.Col( - dbc.Button( - "Download Clima dataframe", - color="primary", - id="download-button", - ), - width="auto", - ), - dbc.Col( - [ - dcc.Download(id="download-dataframe-csv"), - dcc.Download(id="download-epw"), - ], - width=1, - ), + dcc.Download(id="download-dataframe-csv"), + dcc.Download(id="download-epw"), ], + width=1, ), - ), - html.Div( - children=title_with_link( - text="Heating and Cooling Degree Days", - id_button="hdd-cdd-chart", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/tab-summary/degree-days-explained", + ], + ), + ), + html.Div( + children=title_with_link( + text="Heating and Cooling Degree Days", + id_button="hdd-cdd-chart", + doc_link=DocLinks.DEGREE_DAYS, + ), + ), + dbc.Alert( + "WARNING: Invalid Results! The CDD setpoint should be higher than the HDD setpoint!", + color="warning", + is_open=False, + id="warning-cdd-higher-hdd", + ), + dbc.Row( + [ + dbc.Col( + html.Label( + "Heating degree day (HDD) setpoint", ), + width="auto", ), - dbc.Alert( - "WARNING: Invalid Results! The CDD setpoint should be higher than the HDD setpoint!", - color="warning", - is_open=False, - id="warning-cdd-higher-hdd", - ), - dbc.Row( - [ - dbc.Col( - html.Label( - "Heating degree day (HDD) setpoint", - ), - width="auto", - ), - dbc.Col( - dbc.Input( - id="input-hdd-set-point", - type="number", - value=heating_setpoint, - style={"width": "4rem"}, - ), - width="auto", - ), - dbc.Col( - html.Label( - "Cooling degree day (CDD) setpoint", - ), - width="auto", - ), - dbc.Col( - dbc.Input( - id="input-cdd-set-point", - type="number", - value=cooling_setpoint, - style={"width": "4rem"}, - ), - width="auto", - ), - dbc.Col( - dbc.Button( - id="submit-set-points", - children="Submit", - color="primary", - ), - width="auto", - ), - ], - align="center", - justify="center", + dbc.Col( + dbc.Input( + id="input-hdd-set-point", + type="number", + value=heating_setpoint, + style={"width": "4rem"}, + ), + width="auto", ), - dcc.Loading( - type="circle", - children=html.Div(id="degree-days-chart-wrapper"), + dbc.Col( + html.Label( + "Cooling degree day (CDD) setpoint", + ), + width="auto", ), - html.Div( - children=title_with_link( - text="Climate Profiles", - id_button="climate-profiles-chart", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/tab-summary/climate-profiles-explained", + dbc.Col( + dbc.Input( + id="input-cdd-set-point", + type="number", + value=cooling_setpoint, + style={"width": "4rem"}, ), + width="auto", ), - dbc.Row( - id="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( + dbc.Button( + id="submit-set-points", + children="Submit", + color="primary", + ), + width="auto", ), ], + align="center", + justify="center", + ), + dcc.Loading( + type="circle", + children=html.Div(id="degree-days-chart-wrapper"), + ), + html.Div( + children=title_with_link( + text="Climate Profiles", + id_button="climate-profiles-chart", + doc_link=DocLinks.CLIMATE_PROFILES, + ), + ), + dbc.Row( + id="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), + ], ), ], ) -@app.callback( +# @callback( +# [Output('input-hdd-set-point', 'value'), Output('input-cdd-set-point', 'value')], +# [Input('si-ip-radio-input', 'value')] +# ) +# def update_setpoints(si_ip_unit_store_data): +# if si_ip_unit_store_data == 'si': +# return 10, 18 +# else: +# return 50, 64 + + +@callback( Output("world-map", "children"), Input("meta-store", "data"), ) @@ -183,7 +211,7 @@ def update_map(meta): return map_world -@app.callback( +@callback( Output("location-info", "children"), Input("df-store", "modified_timestamp"), [ @@ -200,7 +228,7 @@ def update_location_info(ts, df, meta, si_ip): site_elevation = float(meta["site_elevation"]) site_elevation = round(site_elevation, 2) - if si_ip != "si": + 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" @@ -233,7 +261,14 @@ def update_location_info(ts, df, meta, si_ip): total_solar_rad_unit = "k" + mapping_dictionary["glob_hor_rad"][si_ip]["unit"] total_solar_rad = f"Annual cumulative horizontal solar radiation: {total_solar_rad_value} {total_solar_rad_unit}" - total_diffuse_rad = f"Percentage of diffuse horizontal solar radiation: {round(df['dif_hor_rad'].sum()/df['glob_hor_rad'].sum()*100, 1)} %" + glob_sum = df["glob_hor_rad"].sum() + if glob_sum > 0: + diffuse_percentage = round(df["dif_hor_rad"].sum() / glob_sum * 100, 1) + else: + diffuse_percentage = 0 + total_diffuse_rad = ( + f"Percentage of diffuse horizontal solar radiation: {diffuse_percentage} %" + ) tmp_unit = mapping_dictionary["DBT"][si_ip]["unit"] average_yearly_tmp = ( f"Average yearly temperature: {df['DBT'].mean().round(1)} " + tmp_unit @@ -272,7 +307,7 @@ def update_location_info(ts, df, meta, si_ip): return location_info -@app.callback( +@callback( [ Output("degree-days-chart-wrapper", "children"), Output("warning-cdd-higher-hdd", "is_open"), @@ -383,7 +418,7 @@ def degree_day_chart(ts, ts_click, df, meta, hdd_value, cdd_value, n_clicks, si_ return chart, warning_setpoint -@app.callback( +@callback( Output("temp-profile-graph", "children"), [ Input("df-store", "modified_timestamp"), @@ -405,7 +440,7 @@ def update_violin_tdb(ts, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("wind-speed-graph", "children"), [ Input("df-store", "modified_timestamp"), @@ -428,7 +463,7 @@ def update_tab_wind(ts, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("humidity-profile-graph", "children"), [ Input("df-store", "modified_timestamp"), @@ -451,7 +486,7 @@ def update_tab_rh(ts, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("solar-radiation-graph", "children"), [ Input("df-store", "modified_timestamp"), @@ -474,7 +509,7 @@ def update_tab_gh_rad(ts, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("download-dataframe-csv", "data"), [Input("download-button", "n_clicks")], [ @@ -488,7 +523,7 @@ def download_clima_dataframe(n_clicks, df, meta, si_ip): if n_clicks is None: raise PreventUpdate elif df is not None: - if si_ip == "si": + if si_ip == UnitSystem.SI: return dcc.send_data_frame( df.to_csv, f"df_{meta['city']}_{meta['country']}_Clima_SIunit.csv" ) @@ -500,13 +535,13 @@ def download_clima_dataframe(n_clicks, df, meta, si_ip): print("df not loaded yet") -@app.callback( +@callback( Output("download-epw", "data"), [Input("download-epw-button", "n_clicks")], [State("meta-store", "data")], prevent_initial_call=True, ) -def download_clima_dataframe(n_clicks, meta): +def download_epw(n_clicks, meta): if n_clicks is None: raise PreventUpdate elif meta is not None: diff --git a/my_project/tab_sun/app_sun.py b/pages/sun.py similarity index 78% rename from my_project/tab_sun/app_sun.py rename to pages/sun.py index 4cf9b333..a77c4c7f 100644 --- a/my_project/tab_sun/app_sun.py +++ b/pages/sun.py @@ -1,36 +1,37 @@ -import numpy as np from copy import deepcopy -from dash import dcc -from dash import html + +import dash import dash_bootstrap_components as dbc -from my_project.global_scheme import ( +import numpy as np +from dash import html, dcc +from dash_extensions.enrich import Output, Input, State, callback + +from config import PageUrls, DocLinks, PageInfo, UnitSystem +from pages.lib.charts_sun import ( + monthly_solar, + polar_graph, + custom_cartesian_solar, +) +from pages.lib.global_scheme import ( sun_cloud_tab_dropdown_names, sun_cloud_tab_explore_dropdown_names, dropdown_names, - more_variables_dropdown, tight_margins, month_lst, - mapping_dictionary, -) -from my_project.utils import dropdown -from dash.dependencies import Input, Output, State - -from my_project.tab_sun.charts_sun import ( - monthly_solar, - polar_graph, - custom_cartesian_solar, ) -from my_project.template_graphs import heatmap, barchart, daily_profile -from my_project.utils import code_timer -from my_project.utils import ( - title_with_tooltip, +from pages.lib.template_graphs import heatmap, barchart, daily_profile +from pages.lib.utils import ( + dropdown, generate_chart_name, generate_units, generate_custom_inputs, title_with_link, ) -from app import app +dash.register_page( + __name__, name=PageInfo.SUN_NAME, path=PageUrls.SUN.value, order=PageInfo.SUN_ORDER +) + sc_dropdown_names = { "None": "None", @@ -57,7 +58,7 @@ def sun_path(): children=title_with_link( text="Sun path chart", id_button="sun-path-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/how-to-read-a-sun-path-diagram", + doc_link=DocLinks.SUN_PATH_DIAGRAM, ), ), dbc.Row( @@ -116,7 +117,7 @@ def explore_daily_heatmap(): children=title_with_link( text="Daily charts", id_button="daily-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/customizable-daily-and-hourly-maps", + doc_link=DocLinks.CUSTOM_HEATMAP, ), ), html.Div( @@ -144,52 +145,57 @@ def explore_daily_heatmap(): ) -def static_section(si_ip): - if si_ip == "si": - hor_unit = "Wh/m²" - if si_ip == "ip": - hor_unit = "Btu/ft²" +def static_section(): return html.Div( + id="static-section", className="container-col full-width", children=[ - html.Div( - children=title_with_link( - text="Global and Diffuse Horizontal Solar Radiation (" - + hor_unit - + ")", - id_button="monthly-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/global-and-diffuse-horizontal-solar-radiation", - ), - ), - dcc.Loading( - type="circle", - children=html.Div(id="monthly-solar"), - ), - html.Div( - children=title_with_link( - text="Cloud coverage", - id_button="cloud-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/sun-and-cloud/cloud-coverage", - ), - ), - dcc.Loading( - type="circle", - children=html.Div(id="cloud-cover"), - ), + # ... ], ) -def layout_sun(si_ip): +def layout(): """Contents of tab four.""" return html.Div( className="container-col", id="tab-four-container", - children=[sun_path(), static_section(si_ip), explore_daily_heatmap()], + children=[sun_path(), static_section(), explore_daily_heatmap()], ) -@app.callback( +@callback(Output("static-section", "children"), [Input("si-ip-radio-input", "value")]) +def update_static_section(si_ip): + hor_unit = "Wh/m²" + if si_ip == UnitSystem.IP: + hor_unit = "Btu/ft²" + return [ + html.Div( + children=title_with_link( + text="Global and Diffuse Horizontal Solar Radiation (" + hor_unit + ")", + id_button="monthly-chart-label", + doc_link=DocLinks.SOLAR_RADIATION, + ), + ), + dcc.Loading( + type="circle", + children=html.Div(id="monthly-solar"), + ), + html.Div( + children=title_with_link( + text="Cloud coverage", + id_button="cloud-chart-label", + doc_link=DocLinks.CLOUD_COVER, + ), + ), + dcc.Loading( + type="circle", + children=html.Div(id="cloud-cover"), + ), + ] + + +@callback( [ Output("monthly-solar", "children"), Output("cloud-cover", "children"), @@ -203,7 +209,7 @@ def layout_sun(si_ip): State("si-ip-unit-store", "data"), ], ) -def monthly_and_cloud_chart(ts, df, meta, si_ip): +def monthly_and_cloud_chart(_, df, meta, si_ip): """Update the contents of tab four. Passing in the polar selection and the general info (df, meta).""" # Sun Radiation @@ -232,7 +238,7 @@ def monthly_and_cloud_chart(ts, df, meta, si_ip): ) -@app.callback( +@callback( Output("custom-sunpath", "children"), [ Input("df-store", "modified_timestamp"), @@ -246,7 +252,7 @@ def monthly_and_cloud_chart(ts, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def sun_path_chart(ts, view, var, global_local, df, meta, si_ip): +def sun_path_chart(_, view, var, global_local, df, meta, si_ip): """Update the contents of tab four. Passing in the polar selection and the general info (df, meta).""" custom_inputs = "" if var == "None" else f"{var}" units = "" if var == "None" else generate_units(si_ip) @@ -262,7 +268,7 @@ def sun_path_chart(ts, view, var, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("tab4-daily", "children"), [ Input("df-store", "modified_timestamp"), @@ -275,7 +281,7 @@ def sun_path_chart(ts, view, var, global_local, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def daily(ts, var, global_local, df, meta, si_ip): +def daily(_, var, global_local, df, meta, si_ip): """Update the contents of tab four section two. Passing in the general info (df, meta).""" custom_inputs = generate_custom_inputs(var) units = generate_units(si_ip) @@ -285,7 +291,7 @@ def daily(ts, var, global_local, df, meta, si_ip): ) -@app.callback( +@callback( Output("tab4-heatmap", "children"), [ Input("df-store", "modified_timestamp"), @@ -298,7 +304,7 @@ def daily(ts, var, global_local, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def update_heatmap(ts, var, global_local, df, meta, si_ip): +def update_heatmap(_, var, global_local, df, meta, si_ip): custom_inputs = generate_custom_inputs(var) units = generate_units(si_ip) return dcc.Graph( diff --git a/my_project/tab_t_rh/app_t_rh.py b/pages/t_rh.py similarity index 85% rename from my_project/tab_t_rh/app_t_rh.py rename to pages/t_rh.py index aa1a3b5f..c3d90f82 100644 --- a/my_project/tab_t_rh/app_t_rh.py +++ b/pages/t_rh.py @@ -1,10 +1,10 @@ -from dash import dcc, html -from dash_extensions.enrich import Output, Input, State +import dash +from dash_extensions.enrich import Output, Input, State, dcc, html, callback -from app import app -from my_project.global_scheme import dropdown_names -from my_project.template_graphs import heatmap, yearly_profile, daily_profile -from my_project.utils import ( +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.utils import ( generate_chart_name, generate_units, generate_units_degree, @@ -14,10 +14,19 @@ dropdown, ) + +dash.register_page( + __name__, + name=PageInfo.TEMP_RH_NAME, + path=PageUrls.T_RH.value, + order=PageInfo.TEMP_RH_ORDER, +) + + var_to_plot = ["Dry bulb temperature", "Relative humidity"] -def layout_t_rh(): +def layout(): return html.Div( className="container-col full-width", children=[ @@ -42,7 +51,7 @@ def layout_t_rh(): children=title_with_link( text="Yearly chart", id_button="yearly-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/temperature-and-humidity/temperatures-explained", + doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( @@ -53,7 +62,7 @@ def layout_t_rh(): children=title_with_link( text="Daily chart", id_button="daily-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/temperature-and-humidity/temperatures-explained", + doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( @@ -64,7 +73,7 @@ def layout_t_rh(): children=title_with_link( text="Heatmap chart", id_button="heatmap-chart-label", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/temperature-and-humidity/temperatures-explained", + doc_link=DocLinks.TEMP_HUMIDITY_EXPLAINED, ), ), dcc.Loading( @@ -87,7 +96,7 @@ def layout_t_rh(): ) -@app.callback( +@callback( Output("yearly-chart", "children"), [ Input("df-store", "modified_timestamp"), @@ -100,7 +109,7 @@ def layout_t_rh(): State("si-ip-unit-store", "data"), ], ) -def update_yearly_chart(ts, global_local, dd_value, df, meta, si_ip): +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.update_layout(xaxis=dict(rangeslider=dict(visible=True))) @@ -119,7 +128,7 @@ def update_yearly_chart(ts, global_local, dd_value, df, meta, si_ip): ) -@app.callback( +@callback( Output("daily", "children"), [ Input("df-store", "modified_timestamp"), @@ -132,7 +141,7 @@ def update_yearly_chart(ts, global_local, dd_value, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def update_daily(ts, global_local, dd_value, df, meta, si_ip): +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( @@ -157,7 +166,7 @@ def update_daily(ts, global_local, dd_value, df, meta, si_ip): ) -@app.callback( +@callback( [Output("heatmap", "children")], [ Input("df-store", "modified_timestamp"), @@ -170,7 +179,7 @@ def update_daily(ts, global_local, dd_value, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def update_heatmap(ts, global_local, dd_value, df, meta, si_ip): +def update_heatmap(_, global_local, dd_value, df, meta, si_ip): """Update the contents of tab three. Passing in general info (df, meta).""" if dd_value == dropdown_names[var_to_plot[0]]: units = generate_units_degree(si_ip) @@ -196,7 +205,7 @@ def update_heatmap(ts, global_local, dd_value, df, meta, si_ip): ) -@app.callback( +@callback( Output("table-tmp-hum", "children"), [ Input("df-store", "modified_timestamp"), @@ -204,7 +213,7 @@ def update_heatmap(ts, global_local, dd_value, df, meta, si_ip): ], [State("df-store", "data"), State("si-ip-unit-store", "data")], ) -def update_table(ts, dd_value, df, si_ip): +def update_table(_, dd_value, df, si_ip): """Update the contents of tab three. Passing in general info (df, meta).""" return summary_table_tmp_rh_tab( df[["month", "hour", dd_value, "month_names"]], dd_value, si_ip diff --git a/my_project/tab_wind/app_wind.py b/pages/wind.py similarity index 94% rename from my_project/tab_wind/app_wind.py rename to pages/wind.py index 8341d2e6..0e48d6d3 100644 --- a/my_project/tab_wind/app_wind.py +++ b/pages/wind.py @@ -1,8 +1,11 @@ +import dash from dash import dcc, html -from my_project.global_scheme import month_lst, container_row_center_full -from dash.dependencies import Input, Output, State -from my_project.template_graphs import heatmap, wind_rose -from my_project.utils import ( +from dash_extensions.enrich import Output, Input, State, callback + +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.utils import ( title_with_tooltip, generate_chart_name, generate_units, @@ -10,9 +13,14 @@ title_with_link, dropdown, ) -from my_project.utils import code_timer -from app import app + +dash.register_page( + __name__, + name=PageInfo.WIND_NAME, + path=PageUrls.WIND.value, + order=PageInfo.WIND_ORDER, +) def sliders(): @@ -66,7 +74,7 @@ def seasonal_wind_rose(): children=title_with_link( text="Seasonal Wind Rose", id_button="seasonal-rose-chart", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/wind/how-to-read-a-wind-rose", + doc_link=DocLinks.WIND_ROSE, ), ), html.Div( @@ -151,7 +159,7 @@ def daily_wind_rose(): children=title_with_link( text="Daily Wind Rose", id_button="daily-rose-chart", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/wind/how-to-read-a-wind-rose", + doc_link=DocLinks.WIND_ROSE, ), ), html.Div( @@ -254,9 +262,9 @@ def custom_wind_rose(): dropdown( id="tab5-custom-start-hour", options={ - str(i) + ":00": i for i in range(1, 25) + str(i) + ":00": i for i in range(0, 24) }, - value=1, + value=0, style={"width": "6rem"}, ), ], @@ -312,7 +320,7 @@ def custom_wind_rose(): ) -def layout_wind(): +def layout(): """Contents in the fifth tab 'Wind'.""" return html.Div( className="container-col justify-center", @@ -320,8 +328,8 @@ def layout_wind(): html.Div( children=title_with_link( text="Annual Wind Rose", - id_button="seasonal-rose-chart", - doc_link="https://cbe-berkeley.gitbook.io/clima/documentation/tabs-explained/wind/how-to-read-a-wind-rose", + id_button="wind-rose-label", + doc_link=DocLinks.WIND_ROSE, ), ), dcc.Loading( @@ -346,7 +354,7 @@ def layout_wind(): # wind rose -@app.callback( +@callback( Output("wind-rose", "children"), Input("df-store", "modified_timestamp"), [ @@ -355,7 +363,7 @@ def layout_wind(): State("si-ip-unit-store", "data"), ], ) -def update_annual_wind_rose(ts, df, meta, si_ip): +def update_annual_wind_rose(_, df, meta, si_ip): """Update the contents of tab five. Passing in the info from the sliders and the general info (df, meta).""" annual = wind_rose(df, "", [1, 12], [1, 24], True, si_ip) @@ -367,7 +375,7 @@ def update_annual_wind_rose(ts, df, meta, si_ip): # wind speed -@app.callback( +@callback( Output("wind-speed", "children"), # General [ @@ -380,7 +388,7 @@ def update_annual_wind_rose(ts, df, meta, si_ip): State("si-ip-unit-store", "data"), ], ) -def update_tab_wind_speed(ts, global_local, df, meta, si_ip): +def update_tab_wind_speed(_, global_local, df, meta, si_ip): """Update the contents of tab five. Passing in the info from the sliders and the general info (df, meta).""" speed = heatmap(df, "wind_speed", global_local, si_ip) @@ -392,7 +400,7 @@ def update_tab_wind_speed(ts, global_local, df, meta, si_ip): # wind direction -@app.callback( +@callback( Output("wind-direction", "children"), # General [ @@ -416,7 +424,7 @@ def update_tab_wind_direction(global_local, df, meta, si_ip): # Custom Wind rose -@app.callback( +@callback( Output("custom-wind-rose", "children"), # Custom Graph Input [ @@ -433,7 +441,7 @@ def update_tab_wind_direction(global_local, df, meta, si_ip): ], ) def update_custom_wind_rose( - ts, start_month, start_hour, end_month, end_hour, df, meta, si_ip + _, start_month, start_hour, end_month, end_hour, df, meta, si_ip ): """Update the contents of tab five. Passing in the info from the sliders and the general info (df, meta).""" @@ -464,8 +472,7 @@ def update_custom_wind_rose( ) -### Seasonal Graphs ### -@app.callback( +@callback( [ Output("winter-wind-rose", "children"), Output("spring-wind-rose", "children"), @@ -485,7 +492,7 @@ def update_custom_wind_rose( State("si-ip-unit-store", "data"), ], ) -def update_seasonal_graphs(ts, df, meta, si_ip): +def update_seasonal_graphs(_, df, meta, si_ip): hours = [1, 24] winter_months = [12, 2] spring_months = [3, 5] @@ -580,8 +587,7 @@ def seasonal_chart_caption(month_start, month_end, count, n_calm): ) -### Daily Graphs ### -@app.callback( +@callback( # Daily Graphs [ Output("morning-wind-rose", "children"), @@ -599,7 +605,7 @@ def seasonal_chart_caption(month_start, month_end, count, n_calm): State("si-ip-unit-store", "data"), ], ) -def update_daily_graphs(ts, df, meta, si_ip): +def update_daily_graphs(_, df, meta, si_ip): """Update the contents of tab five. Passing in the info from the sliders and the general info (df, meta).""" months = [1, 12] diff --git a/requirements.txt b/requirements.txt index f2a5c358..d2d7baaf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,52 +1,59 @@ -ansi2html==1.9.1 -blinker==1.7.0 +ansi2html==1.9.2 +black==25.1.0 +blinker==1.9.0 bump2version==1.0.1 cachelib==0.9.0 -certifi==2023.11.17 -charset-normalizer==3.3.2 -cleanpy==0.4.0 -click==8.1.7 -dash==2.14.2 +certifi==2025.7.14 +charset-normalizer==3.4.2 +cleanpy==0.5.1 +click==8.2.1 +dash==2.15.0 dash-bootstrap-components==1.2.0 dash-core-components==2.0.0 dash-extensions==1.0.7 dash-html-components==2.0.0 +dash-iconify==0.1.2 dash-mantine-components==0.12.1 dash-table==5.0.0 -dataclass-wizard==0.22.2 -EditorConfig==0.12.3 +dataclass-wizard==0.22.3 +EditorConfig==0.17.1 Flask==2.3.3 Flask-Caching==2.0.2 -gunicorn==20.1.0 -h5py==3.10.0 -idna==3.6 -importlib-metadata==7.0.1 -iniconfig==2.0.0 -itsdangerous==2.1.2 -Jinja2==3.1.3 -jsbeautifier==1.14.11 -llvmlite==0.41.1 -MarkupSafe==2.1.4 +h5py==3.14.0 +idna==3.10 +importlib_metadata==8.7.0 +iniconfig==2.1.0 +itsdangerous==2.2.0 +Jinja2==3.1.6 +jsbeautifier==1.15.4 +llvmlite==0.44.0 +MarkupSafe==3.0.2 more-itertools==9.1.0 +mypy_extensions==1.1.0 +narwhals==2.0.1 nest-asyncio==1.6.0 -numba==0.58.1 +numba==0.61.2 numpy==1.26.3 -packaging==23.2 +packaging==25.0 pandas==2.2.0 +pathspec==0.12.1 +platformdirs==4.3.8 plotly==5.18.0 -pluggy==1.3.0 +pluggy==1.6.0 pvlib==0.9.1 -pytest==7.4.4 +Pygments==2.19.2 +pytest==8.4.1 pythermalcomfort==2.9.1 -python-dateutil==2.8.2 -pytz==2023.3.post1 -requests==2.31.0 -retrying==1.3.4 +python-dateutil==2.9.0.post0 +pytz==2025.2 +requests==2.32.4 +retrying==1.4.1 +ruff==0.12.7 scipy==1.12.0 -six==1.16.0 -tenacity==8.2.3 -typing_extensions==4.9.0 -tzdata==2023.4 -urllib3==2.1.0 -Werkzeug==3.0.1 -zipp==3.17.0 +six==1.17.0 +tenacity==9.1.2 +typing_extensions==4.14.1 +tzdata==2025.2 +urllib3==2.5.0 +Werkzeug==3.0.6 +zipp==3.23.0 diff --git a/tests/node/cypress/e2e/spec.cy.js b/tests/node/cypress/e2e/spec.cy.js index 753c43d7..dcc84470 100644 --- a/tests/node/cypress/e2e/spec.cy.js +++ b/tests/node/cypress/e2e/spec.cy.js @@ -1,6 +1,5 @@ function click_tab(name) { - cy.get('.custom-tab') - .not('.tab--disabled') + cy.get('.nav-item') .contains(name) .click(); } diff --git a/tests/node/package-lock.json b/tests/node/package-lock.json index 08476797..37f5fdc1 100644 --- a/tests/node/package-lock.json +++ b/tests/node/package-lock.json @@ -8,7 +8,7 @@ "name": "clima", "version": "0.0.0", "devDependencies": { - "cypress": "^13.5.1" + "cypress": "^13.8.1" } }, "node_modules/@colors/colors": { @@ -22,10 +22,11 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", + "integrity": "sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -33,16 +34,16 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", + "form-data": "~4.0.4", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.10.4", + "qs": "6.14.0", "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -74,6 +75,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.10.tgz", "integrity": "sha512-luANqZxPmjTll8bduz4ACs/lNTCLuWssCyjqTY9yLdsv1xnViQp3ISKwsEWOIecO13JWUqjVdig/Vjjc09o8uA==", "dev": true, + "optional": true, "dependencies": { "undici-types": "~5.26.4" } @@ -186,6 +188,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" } @@ -195,6 +198,7 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -218,7 +222,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -275,6 +280,7 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tweetnacl": "^0.14.3" } @@ -292,10 +298,11 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -343,15 +350,32 @@ "node": ">=6" } }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -496,6 +520,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -531,13 +556,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -548,21 +575,20 @@ } }, "node_modules/cypress": { - "version": "13.5.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.5.1.tgz", - "integrity": "sha512-yqLViT0D/lPI8Kkm7ciF/x/DCK/H/DnogdGyiTnQgX4OVR2aM30PtK+kvklTOD1u3TuItiD9wUQAF8EYWtyZug==", + "version": "13.8.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.8.1.tgz", + "integrity": "sha512-Uk6ovhRbTg6FmXjeZW/TkbRM07KPtvM5gah1BIMp4Y2s+i/NMxgaLw0+PbYTOdw1+egE0FP3mWRiGcRkjjmhzA==", "dev": true, "hasInstallScript": true, "dependencies": { "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", - "@types/node": "^18.17.5", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -580,7 +606,7 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -610,6 +636,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" }, @@ -646,34 +673,37 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, + "license": "MIT", "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -707,6 +737,55 @@ "node": ">=8.6" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -790,7 +869,8 @@ "dev": true, "engines": [ "node >=0.6.0" - ] + ], + "license": "MIT" }, "node_modules/fd-slicer": { "version": "1.1.0", @@ -826,17 +906,20 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/fs-extra": { @@ -874,25 +957,50 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -922,6 +1030,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" } @@ -962,12 +1071,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -988,23 +1098,12 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1012,11 +1111,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { "node": ">= 0.4" }, @@ -1025,10 +1128,11 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1037,14 +1141,15 @@ } }, "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "sshpk": "^1.18.0" }, "engines": { "node": ">=0.10" @@ -1205,13 +1310,15 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -1248,6 +1355,7 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -1380,6 +1488,16 @@ "node": ">=10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -1391,6 +1509,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1400,6 +1519,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -1456,10 +1576,14 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1575,12 +1699,6 @@ "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "dev": true }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -1591,22 +1709,14 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -1615,12 +1725,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", @@ -1630,12 +1734,6 @@ "throttleit": "^1.0.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -1703,7 +1801,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { "version": "7.5.4", @@ -1720,21 +1819,6 @@ "node": ">=10" } }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1757,14 +1841,76 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1795,6 +1941,7 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, + "license": "MIT", "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -1880,6 +2027,26 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -1893,18 +2060,16 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" + "node": ">=16" } }, "node_modules/tslib": { @@ -1929,7 +2094,8 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true + "dev": true, + "license": "Unlicense" }, "node_modules/type-fest": { "version": "0.21.3", @@ -1947,16 +2113,8 @@ "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, - "engines": { - "node": ">= 4.0.0" - } + "optional": true }, "node_modules/untildify": { "version": "4.0.0", @@ -1967,16 +2125,6 @@ "node": ">=8" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -1994,6 +2142,7 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/tests/node/package.json b/tests/node/package.json index ceecd8f0..cb40d108 100644 --- a/tests/node/package.json +++ b/tests/node/package.json @@ -7,6 +7,6 @@ "cy:open": "cypress open" }, "devDependencies": { - "cypress": "^13.5.1" + "cypress": "^13.8.1" } } diff --git a/tests/python/test_utils.py b/tests/python/test_utils.py index 1fbebf66..ab085c42 100644 --- a/tests/python/test_utils.py +++ b/tests/python/test_utils.py @@ -1,9 +1,13 @@ -import requests +import os -from my_project.utils import summary_table_tmp_rh_tab -from my_project.extract_df import get_data, create_df import pandas as pd -import os + +from config import UnitSystem + +import requests + +from pages.lib.utils import summary_table_tmp_rh_tab +from pages.lib.extract_df import get_data, create_df def save_epw_test(path_file): @@ -29,7 +33,7 @@ def test_summary_table_tmp_rh_tab(): # check tha the climate.onebuilding website is on print(requests.get("https://climate.onebuilding.org", timeout=2)) df = import_epw_test() - data_table = summary_table_tmp_rh_tab(df, "RH", "si") + data_table = summary_table_tmp_rh_tab(df, "RH", UnitSystem.SI) assert data_table.data[0]["month"] == "Jan" except requests.exceptions.ConnectionError: