buildstock-fetch 1.3.1__py3-none-any.whl → 1.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of buildstock-fetch might be problematic. Click here for more details.

@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from typing import Union, cast
7
7
 
8
8
  import questionary
9
+ import tomli
9
10
  import typer
10
11
  from rich.console import Console
11
12
  from rich.panel import Panel
@@ -29,7 +30,10 @@ app = typer.Typer(
29
30
  BUILDSTOCK_RELEASES_FILE = str(files("buildstock_fetch").joinpath("data").joinpath("buildstock_releases.json"))
30
31
 
31
32
  # File types that haven't been implemented yet
32
- UNAVAILABLE_FILE_TYPES = ["load_curve_daily"]
33
+ UNAVAILABLE_FILE_TYPES: list[str] = []
34
+
35
+ # Upgrade scenario description lookup file
36
+ UPGRADES_LOOKUP_FILE = str(files("buildstock_fetch").joinpath("data").joinpath("buildstock_upgrades_lookup.json"))
33
37
 
34
38
 
35
39
  class InvalidProductError(Exception):
@@ -75,7 +79,7 @@ def _get_release_years_options(available_releases: list[str], product_type: str)
75
79
  parsed_releases = _parse_buildstock_releases(available_releases)
76
80
  available_releases = _filter_available_releases(list(parsed_releases.keys()), product_type=product_type)
77
81
  available_release_years = list({parsed_releases[release]["release_year"] for release in available_releases})
78
- available_release_years.sort()
82
+ available_release_years.sort(reverse=True) # Sort in descending order (latest first)
79
83
 
80
84
  return available_releases, available_release_years
81
85
 
@@ -152,6 +156,14 @@ def _get_upgrade_ids_options(release_name: str) -> list[str]:
152
156
  available_upgrade_ids.sort()
153
157
  available_upgrade_ids = [str(upgrade_id) for upgrade_id in available_upgrade_ids]
154
158
 
159
+ if release_name in json.loads(Path(UPGRADES_LOOKUP_FILE).read_text(encoding="utf-8")):
160
+ upgrade_descriptions = json.loads(Path(UPGRADES_LOOKUP_FILE).read_text(encoding="utf-8"))[release_name][
161
+ "upgrade_descriptions"
162
+ ]
163
+ available_upgrade_ids = [
164
+ f"{upgrade_id}: {upgrade_descriptions[upgrade_id]}" for upgrade_id in available_upgrade_ids
165
+ ]
166
+
155
167
  return cast(list[str], available_upgrade_ids)
156
168
 
157
169
 
@@ -218,10 +230,26 @@ def _get_file_type_options(release_name: str) -> list[str]:
218
230
  return cast(list[str], available_releases[release_name]["available_data"])
219
231
 
220
232
 
221
- def _get_file_type_options_grouped(release_name: str) -> list[dict]:
233
+ def _get_file_type_options_grouped(release_name: str, selected_states: list[str]) -> list[dict]:
222
234
  """Get file type options grouped by category for questionary checkbox."""
223
235
  file_types = _get_file_type_options(release_name)
224
236
 
237
+ # TODO: If a trip_schedule table was built for any of the states in this release, "trip_schedules" will be included
238
+ # in the file_types list above. However, the state that the user wants to download files for may not have the trip_schedule tables built for it yet.
239
+ # So, we need to check if the release_name + state combo is in the available trip_schedules_states list.
240
+ # If not, we need to remove "trip_schedules" from the file_types list, so it doesn't show up in the questionary checkbox.
241
+ # Remember that users can select multiple states, so as long as one release_name + state combo is in the available trip_schedules_states list,
242
+ # we should include "trip_schedules" in the file_types list. and then later on, we need to handle different states differently.
243
+
244
+ trip_schedule_availabile = False
245
+ available_releases = _get_all_available_releases()
246
+ for state in selected_states:
247
+ if (
248
+ "trip_schedules" in available_releases[release_name]["available_data"]
249
+ and state in available_releases[release_name]["trip_schedule_states"]
250
+ ):
251
+ trip_schedule_availabile = True
252
+
225
253
  # Define categories
226
254
  categories = {
227
255
  "Simulation Files": ["hpxml", "schedule"],
@@ -233,21 +261,39 @@ def _get_file_type_options_grouped(release_name: str) -> list[dict]:
233
261
  "load_curve_annual",
234
262
  ],
235
263
  "Metadata": ["metadata"],
264
+ "EV": ["trip_schedules"],
236
265
  "Weather": ["weather"],
237
266
  }
238
267
 
239
- choices = []
268
+ choices: list[dict] = []
240
269
  for category, types in categories.items():
241
- # Filter available types but maintain the defined order
242
- available_in_category = [ft for ft in types if ft in file_types]
243
- if available_in_category:
244
- choices.append({"name": f"--- {category} ---", "value": None, "disabled": True})
245
- for file_type in available_in_category:
246
- choices.append({"name": f" {file_type}", "value": file_type, "style": "bold"})
270
+ if category == "EV":
271
+ _add_ev_category_choices(choices, category, types, trip_schedule_availabile)
272
+ else:
273
+ _add_standard_category_choices(choices, category, types, file_types)
247
274
 
248
275
  return choices
249
276
 
250
277
 
278
+ def _add_ev_category_choices(
279
+ choices: list[dict], category: str, types: list[str], trip_schedule_available: bool
280
+ ) -> None:
281
+ """Add EV category choices to the choices list."""
282
+ for file_type in types:
283
+ if file_type == "trip_schedules" and trip_schedule_available:
284
+ choices.append({"name": f"--- {category} ---", "value": None, "disabled": True})
285
+ choices.append({"name": f" {file_type}", "value": file_type, "style": "bold"})
286
+
287
+
288
+ def _add_standard_category_choices(choices: list[dict], category: str, types: list[str], file_types: list[str]) -> None:
289
+ """Add standard category choices to the choices list."""
290
+ available_in_category = [ft for ft in types if ft in file_types]
291
+ if available_in_category:
292
+ choices.append({"name": f"--- {category} ---", "value": None, "disabled": True})
293
+ for file_type in available_in_category:
294
+ choices.append({"name": f" {file_type}", "value": file_type, "style": "bold"})
295
+
296
+
251
297
  def _get_available_releases_names() -> list[str]:
252
298
  # Read the buildstock releases JSON file
253
299
  buildstock_releases = json.loads(Path(BUILDSTOCK_RELEASES_FILE).read_text(encoding="utf-8"))
@@ -363,20 +409,23 @@ def _run_interactive_mode() -> dict[str, Union[str, list[str]]]:
363
409
  )
364
410
 
365
411
  # Retrieve state
366
- selected_states = _handle_cancellation(
367
- questionary.checkbox(
368
- "Select states:",
369
- choices=_get_state_options(),
370
- instruction="Use spacebar to select/deselect options, enter to confirm",
371
- validate=lambda answer: "You must select at least one state" if len(answer) == 0 else True,
372
- ).ask()
412
+ selected_states: list[str] = cast(
413
+ list[str],
414
+ _handle_cancellation(
415
+ questionary.checkbox(
416
+ "Select states:",
417
+ choices=_get_state_options(),
418
+ instruction="Use spacebar to select/deselect options, enter to confirm",
419
+ validate=lambda answer: "You must select at least one state" if len(answer) == 0 else True,
420
+ ).ask()
421
+ ),
373
422
  )
374
423
 
375
424
  # Retrieve requested file type
376
425
  requested_file_types = _handle_cancellation(
377
426
  questionary.checkbox(
378
427
  "Select file type:",
379
- choices=_get_file_type_options_grouped(selected_release_name),
428
+ choices=_get_file_type_options_grouped(selected_release_name, selected_states),
380
429
  instruction="Use spacebar to select/deselect options, enter to confirm",
381
430
  validate=lambda answer: "You must select at least one file type" if len(answer) == 0 else True,
382
431
  ).ask()
@@ -512,6 +561,18 @@ def _check_unavailable_file_types(inputs: Mapping[str, Union[str, list[str]]]) -
512
561
  if "weather" in selected_file_types:
513
562
  _check_weather_file_availability(inputs, available_file_types, selected_unavailable_file_types)
514
563
 
564
+ if "trip_schedules" in selected_file_types:
565
+ product_short_name = "res" if inputs["product"] == "resstock" else "com"
566
+ input_release_name = (
567
+ f"{product_short_name}_{inputs['release_year']}_{inputs['weather_file']}_{inputs['release_version']}"
568
+ )
569
+ available_releases = _get_all_available_releases()
570
+ availble_trip_schedule_states = available_releases[input_release_name]["trip_schedule_states"]
571
+ for state in inputs["states"]:
572
+ if state not in availble_trip_schedule_states:
573
+ console.print(f"[yellow]The following state is not available for trip schedules: {state}[/yellow]")
574
+ selected_unavailable_file_types.append(f"trip_schedules_{state}")
575
+
515
576
  _print_unavailable_file_types_warning(selected_unavailable_file_types)
516
577
 
517
578
  return available_file_types, selected_unavailable_file_types
@@ -645,6 +706,7 @@ def _validate_file_types(inputs: dict[str, Union[str, list[str]]], release_name:
645
706
  """Validate file types."""
646
707
  available_releases = _get_all_available_releases()
647
708
  for file_type in inputs["file_type"]:
709
+ # TODO: Validate EV related files
648
710
  if file_type not in available_releases[release_name]["available_data"]:
649
711
  return f"Invalid file type: {file_type}"
650
712
  return True
@@ -715,6 +777,29 @@ UPGRADE_ID_OPTION = typer.Option(
715
777
  None, "--upgrade_id", "-u", help="Upgrade IDs (multiple can be provided, inside quotes and separated by spaces)"
716
778
  )
717
779
  OUTPUT_DIRECTORY_OPTION = typer.Option(None, "--output_directory", "-o", help='e.g., "data" or "../output"')
780
+ VERSION_OPTION = typer.Option(False, "--version", "-v", help="Show version information and exit")
781
+
782
+
783
+ def _get_version() -> str:
784
+ """Get the version from pyproject.toml."""
785
+ try:
786
+ # Get the path to pyproject.toml (assuming it's in the project root)
787
+ project_root = Path(__file__).parent.parent
788
+ pyproject_path = project_root / "pyproject.toml"
789
+
790
+ with open(pyproject_path, "rb") as f:
791
+ data = tomli.load(f)
792
+ version = data["project"]["version"]
793
+ return str(version)
794
+ except (FileNotFoundError, KeyError, Exception):
795
+ return "unknown"
796
+
797
+
798
+ def _show_version() -> None:
799
+ """Display version information and exit."""
800
+ version = _get_version()
801
+ console.print(f"buildstock-fetch version {version}")
802
+ raise typer.Exit(0) from None
718
803
 
719
804
 
720
805
  def _run_interactive_mode_wrapper() -> dict[str, Union[str, list[str]]]:
@@ -792,7 +877,6 @@ def _process_data_download(inputs: dict[str, Union[str, list[str]]]) -> None:
792
877
  output_dir = inputs["output_directory"]
793
878
  if isinstance(output_dir, list):
794
879
  output_dir = output_dir[0] if output_dir else "."
795
-
796
880
  fetch_bldg_data(
797
881
  selected_bldg_ids, file_type_tuple, Path(output_dir), weather_states=available_weather_states
798
882
  )
@@ -811,11 +895,16 @@ def main_callback(
811
895
  file_type: str = FILE_TYPE_OPTION,
812
896
  upgrade_id: str = UPGRADE_ID_OPTION,
813
897
  output_directory: str = OUTPUT_DIRECTORY_OPTION,
898
+ version: bool = VERSION_OPTION,
814
899
  ) -> None:
815
900
  """
816
901
  Buildstock Fetch CLI tool. Run without arguments for interactive mode.
817
902
  """
818
903
 
904
+ # Handle version option first
905
+ if version:
906
+ _show_version()
907
+
819
908
  # If no arguments provided, run interactive mode
820
909
  if not any([product, release_year, weather_file, release_version, states, file_type]):
821
910
  inputs = _run_interactive_mode_wrapper()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: buildstock-fetch
3
- Version: 1.3.1
3
+ Version: 1.4.3
4
4
  Summary: This library simplifies downloading building characteristics and load curve data from NREL's ResStock and ComStock projects.
5
5
  Author-email: Switchbox <hello@switch.box>
6
6
  Project-URL: Homepage, https://switchbox-data.github.io/buildstock-fetch/
@@ -24,6 +24,9 @@ Requires-Dist: typer>=0.9.0
24
24
  Requires-Dist: rich>=13.9.5
25
25
  Requires-Dist: questionary>=1.11.0
26
26
  Requires-Dist: polars>=0.20.0
27
+ Requires-Dist: tomli>=2.0.0
28
+ Requires-Dist: boto3>=1.33.0
29
+ Requires-Dist: botocore>=1.33.0
27
30
  Provides-Extra: test
28
31
  Requires-Dist: pytest>=7.2.0; extra == "test"
29
32
  Provides-Extra: docs
@@ -44,6 +47,7 @@ Requires-Dist: botocore>=1.33.0; extra == "dev-utils"
44
47
  Requires-Dist: pyarrow>=14.0.1; extra == "dev-utils"
45
48
  Requires-Dist: numpy>=1.26.4; extra == "dev-utils"
46
49
  Requires-Dist: scikit-learn>=1.6.1; extra == "dev-utils"
50
+ Requires-Dist: awscli>=1.32.0; extra == "dev-utils"
47
51
  Requires-Dist: xmltodict>=0.14.2; extra == "dev-utils"
48
52
  Provides-Extra: dev
49
53
  Requires-Dist: buildstock-fetch[dev-utils,docs,lint,test]; extra == "dev"
@@ -57,15 +61,36 @@ Dynamic: license-file
57
61
  [![Commit activity](https://img.shields.io/github/commit-activity/m/switchbox-data/buildstock-fetch)](https://img.shields.io/github/commit-activity/m/switchbox-data/buildstock-fetch)
58
62
  [![License](https://img.shields.io/github/license/switchbox-data/buildstock-fetch)](https://img.shields.io/github/license/switchbox-data/buildstock-fetch)
59
63
 
60
- This library simplifies downloading building characteristics and load curve data from NREL's ResStock and ComStock projects.
64
+ A CLI tool, and python library, to simplify downloading building characteristics and load curve data from NREL's ResStock and ComStock projects.
61
65
 
62
66
  - **Github repository**: <https://github.com/switchbox-data/buildstock-fetch/>
63
67
  - **Documentation**: <https://switchbox-data.github.io/buildstock-fetch/>
64
- - **PyPI page***: <https://pypi.org/project/buildstock-fetch/>
68
+ - **PyPI page**: <https://pypi.org/project/buildstock-fetch/>
69
+
70
+ ## Installing the CLI tool
71
+
72
+ We recommend using [uv](https://docs.astral.sh/uv/getting-started/installation/) or [pipx](https://pipx.pypa.io/stable/installation/) to install the buildstock-fetch CLI:
73
+
74
+ ```bash
75
+ uv install buildstock-fetch
76
+ ```
77
+ or
78
+
79
+ ```bash
80
+ pipx install buildstock-fetch
81
+ ```
82
+
83
+ You'll then be able to access the `bsf` command system-wide:
84
+
85
+ ```bash
86
+ bsf --help
87
+ ```
65
88
 
66
89
  ## Installing the library
67
90
 
68
- `buildstock-fetch` is available on [PyPI](https://pypi.org/project/buildstock-fetch/) and can be installed via:
91
+ buildstock-fetch is implemented in Python, and we expose our internal functions via a library.
92
+
93
+ If you're using Python, and you want to install the CLI only for a particular project (rather than system-wide), or you want to user the underlying library, install our [PyPI](https://pypi.org/project/buildstock-fetch/) package via:
69
94
 
70
95
  ```bash
71
96
  pip install buildstock-fetch
@@ -77,26 +102,20 @@ or
77
102
  uv add buildstock-fetch
78
103
  ```
79
104
 
80
- ## Using the library
105
+ ## Using the CLI
81
106
 
82
- To use the CLI tool in `buildstock-fetch` run:
107
+ To make it easy to download what you want from NREL's S3 bucket, `bsf` has an interactive mode. Activate it with:
83
108
 
84
109
  ```bash
85
110
  bsf
86
111
  ```
87
112
 
88
- to activate the interactive mode. Alternatively, provide the inputs directly. For example,
113
+ Alternatively, you can tell `bsf` exactly what to download via CLI args:
89
114
 
90
115
  ```bash
91
116
  bsf --product resstock --release_year 2022 --weather_file tmy3 --release_version 1 --states CA --file_type "hpxml metadata" --upgrade_id "0 1 2" --output_directory ./CA_data
92
117
  ```
93
118
 
94
- If the above options don't work, try:
95
-
96
- ```bash
97
- python -m buildstock_fetch
98
- ```
99
-
100
119
  For more details about the usage, see [Usage](https://switchbox-data.github.io/buildstock-fetch/usage/)
101
120
 
102
121
  ## Developing the library
@@ -1,8 +1,9 @@
1
1
  buildstock_fetch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  buildstock_fetch/__main__.py,sha256=wEPNJ-s7F6KcRQ_4B5Xh08uqvYZHno5F2R8DU0BMBtk,80
3
- buildstock_fetch/main.py,sha256=6NAYxs_5yW0PpNgPdCgmaJJUqgSF-InrMO8xZJe4npQ,73839
4
- buildstock_fetch/main_cli.py,sha256=3zKJ5sXG2QGmAs--kZPQy1qhLEnaw5gvi4RoH_q65dI,31891
5
- buildstock_fetch/data/buildstock_releases.json,sha256=rHbNkWtTi6VXFim7xtvhwFWQUvtqvcbCfjtvRjkWV-4,10853
3
+ buildstock_fetch/main.py,sha256=yZ8xoYWnoyeTIDMXzVnbQt57a2NAZS7bEPRDtj_SuiU,90887
4
+ buildstock_fetch/main_cli.py,sha256=ZZ6Yn7rBfXguCiUSszKlB0L-FY88ZJM_HXLC8o1EGNc,36311
5
+ buildstock_fetch/data/buildstock_releases.json,sha256=XL-D1Xdix59sFGbS6vKFiGhQakhhPrwn14N-Tgt9iuk,11620
6
+ buildstock_fetch/data/buildstock_upgrades_lookup.json,sha256=CCc2dV6voD8r6WpBBq9zoxhVQGAPcUGmAoFyKoUcUpo,1486
6
7
  buildstock_fetch/data/building_data/combined_metadata.parquet/product=comstock/release_year=2021/weather_file=amy2018/release_version=1/state=AK/d1454abff0d94c8090af7b3e923c473b-0.parquet,sha256=nG6H3oGyDzPk1B15YmFd_U81PsA14uABqqwK7P30nKE,7059
7
8
  buildstock_fetch/data/building_data/combined_metadata.parquet/product=comstock/release_year=2021/weather_file=amy2018/release_version=1/state=AK/dcd864cc169b4695be2b9775b1a054ae-0.parquet,sha256=fsREWV9g4fsA3cIWqyPnXVwd7FsJxED2btYpmyOvnbQ,7163
8
9
  buildstock_fetch/data/building_data/combined_metadata.parquet/product=comstock/release_year=2021/weather_file=amy2018/release_version=1/state=AL/d1454abff0d94c8090af7b3e923c473b-0.parquet,sha256=jiTG48ly-LZwfwAL3THdfRzHDG1qEogoxO7P78U4WOU,41201
@@ -1602,9 +1603,9 @@ buildstock_fetch/data/building_data/combined_metadata.parquet/product=resstock/r
1602
1603
  buildstock_fetch/data/load_curve_column_map/2022_resstock_load_curve_columns.csv,sha256=9UoTPlvxR3RqsxH8i4tI75lLtdSAT-HbAFxwlzvAYNY,7339
1603
1604
  buildstock_fetch/data/load_curve_column_map/2024_resstock_load_curve_columns.csv,sha256=TIxqsE-iVHE5tYq9oPSgBxIVbd5qfyDcJG_eTqds9aY,7471
1604
1605
  buildstock_fetch/data/weather_station_map/weather_station_map.parquet,sha256=igNrx-UGH20CqPcjANTDrrMyj6Z4_JcXIg2aaCNhFRg,346990
1605
- buildstock_fetch-1.3.1.dist-info/licenses/LICENSE,sha256=TJeh2yvO8__8Rbamd8r48-zvlFCINAsu9nOo5QdMRX8,1066
1606
- buildstock_fetch-1.3.1.dist-info/METADATA,sha256=hdj6JpUKIqsyAS_2CIQL-CHruvKiau28by_d6MIrebY,7607
1607
- buildstock_fetch-1.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1608
- buildstock_fetch-1.3.1.dist-info/entry_points.txt,sha256=C7zPk3BSLcI47ymvYKI05nvfRJMEXz4BPIIDKsjePn8,54
1609
- buildstock_fetch-1.3.1.dist-info/top_level.txt,sha256=-PGb2C-Tb3O-wPqUHSOBrvJqRzNHgY_KTbTsXaHIo5M,17
1610
- buildstock_fetch-1.3.1.dist-info/RECORD,,
1606
+ buildstock_fetch-1.4.3.dist-info/licenses/LICENSE,sha256=TJeh2yvO8__8Rbamd8r48-zvlFCINAsu9nOo5QdMRX8,1066
1607
+ buildstock_fetch-1.4.3.dist-info/METADATA,sha256=7TaxdMpRmwemxFqaneiJ09sXgPzlcuc_xBUIPGdzTn4,8323
1608
+ buildstock_fetch-1.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1609
+ buildstock_fetch-1.4.3.dist-info/entry_points.txt,sha256=C7zPk3BSLcI47ymvYKI05nvfRJMEXz4BPIIDKsjePn8,54
1610
+ buildstock_fetch-1.4.3.dist-info/top_level.txt,sha256=-PGb2C-Tb3O-wPqUHSOBrvJqRzNHgY_KTbTsXaHIo5M,17
1611
+ buildstock_fetch-1.4.3.dist-info/RECORD,,