diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a10256..831c317 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,16 +6,22 @@ on: jobs: test: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + python-version: [ "3.9", "3.12" ] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: ${{ matrix.python-version }} - name: Install poetry - uses: abatilo/actions-poetry@v2.0.0 + uses: abatilo/actions-poetry@v3 with: - poetry-version: 1.4.0 + poetry-version: 1.8.3 - name: Setup Python run: | pip install --upgrade pip @@ -28,19 +34,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.12" - name: Run pre-commit - uses: pre-commit/action@v2.0.0 + uses: pre-commit/action@v3.0.1 with: extra_args: --all-files - name: Install poetry - uses: abatilo/actions-poetry@v2.0.0 + uses: abatilo/actions-poetry@v3 with: - poetry-version: 1.4.0 + poetry-version: 1.8.3 - name: type check run: | poetry install diff --git a/.gitignore b/.gitignore index f865902..ee02239 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Project files +.idea/ +.vscode/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b5369e..81f6ce1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.2.3 + rev: v4.6.0 hooks: - id: trailing-whitespace - id: check-added-large-files - args: ['--maxkb=2000'] + args: [ '--maxkb=2000' ] - id: check-ast - id: check-json - id: check-merge-conflict @@ -12,21 +12,19 @@ repos: - id: check-yaml - id: debug-statements - id: end-of-file-fixer - # - id: requirements-txt-fixer - - id: flake8 - args: - - '--max-line-length=140' # default of Black - - '--per-file-ignores=buildingsync_asset_extractor/lighting_processing/building_space_type_to_lpd.py:E501 buildingsync_asset_extractor/lighting_processing/building_type_to_lpd.py:E501' - id: mixed-line-ending - id: pretty-format-json - args: ['--autofix', '--no-sort-keys'] - - repo: https://github.com/pre-commit/mirrors-isort - rev: v5.10.1 - hooks: - - id: isort - args: ['-m=VERTICAL_HANGING_INDENT'] # vertical hanging + args: [ '--autofix', '--no-sort-keys' ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.1.1 + rev: v1.11.2 + hooks: + - id: mypy + args: [ "--install-types", "--non-interactive", "--ignore-missing-imports" ] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.7 hooks: - - id: mypy - args: ["--install-types", "--non-interactive", "--ignore-missing-imports"] + # Run the linter + - id: ruff + args: [ --fix, --exit-non-zero-on-fix, --output-format=full ] + # Run the formatter + - id: ruff-format diff --git a/README.md b/README.md index f1077ad..813b4da 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,11 @@ This package processes a BuildingSync file to extract asset information that can ```bash pip install buildingsync-asset-extractor ``` + ### Install from source + [Poetry](https://python-poetry.org/) is required to install buildingsync-asset-extractor. + ```bash # Copy repo git clone https://github.com/BuildingSync/BuildingSync-asset-extractor.git @@ -29,26 +32,35 @@ poetry run buildingsync_asset_extractor BuildingSync version 2.4.0. -The pre-importer will identify assets defined in the `asset_definitions.json` file stored in the `config` directory. There are various methods of calculating assets: +The pre-importer will identify assets defined in the `asset_definitions.json` file stored in the `config` directory. +There are various methods of calculating assets: -1. `sqft`. The sqft method will calculate a 'primary' and 'secondary' value for the asset based on the area it serves. This is calculated from the floor areas defined in each `Section` element. `Conditioned` floor area values will be used if present; `Gross` otherwise. +1. `sqft`. The sqft method will calculate a 'primary' and 'secondary' value for the asset based on the area it serves. + This is calculated from the floor areas defined in each `Section` element. `Conditioned` floor area values will be + used if present; `Gross` otherwise. 1. `num`. The num method will sum up all assets of the specified type and return a single overall number. 1. `avg`. The avg method will return an average value for all assets of the specified type found. -1. `avg_sqft`. The avg_sqft method will return a weighted average value for all assets of the specified type found based on the area they serve. +1. `avg_sqft`. The avg_sqft method will return a weighted average value for all assets of the specified type found based + on the area they serve. -1. `age_oldest`, `age_newest`, `age_average`. The age method will retrieve the 'YearOfManufacture' (or 'YearInstalled' if not present) element of a specified equipment type and return either the oldest or newest, or average age (year) as specified. Average age is calculated by a weighted average using the following (in order): capacity, served space area, regular average. +1. `age_oldest`, `age_newest`, `age_average`. The age method will retrieve the 'YearOfManufacture' (or 'YearInstalled' + if not present) element of a specified equipment type and return either the oldest or newest, or average age (year) + as specified. Average age is calculated by a weighted average using the following (in order): capacity, served space + area, regular average. -1. `custom`. Use this method for particular asset that do not fit in the other categories; i.e. Heating Efficiency. Note that a dedicated method may need to be written to support this type of asset. +1. `custom`. Use this method for particular asset that do not fit in the other categories; i.e. Heating Efficiency. Note + that a dedicated method may need to be written to support this type of asset. -When an asset has a unit associated with it, a separate asset will be generated to store the unit information. That asset will be named the same as the original asset, with ' Units' appended at the end. +When an asset has a unit associated with it, a separate asset will be generated to store the unit information. That +asset will be named the same as the original asset, with ' Units' appended at the end. To test usage: ```bash - python buildingsync_asset_extractor/main.py +python buildingsync_asset_extractor/main.py ``` This will extract assets from `tests/files/testfile.xml` and save the results to `assets_output.json` @@ -58,6 +70,7 @@ There are 2 methods of initializing the Processor: with either a filename or dat ```bash bp = BSyncProcessor(filename=filename) ``` + or ```bash @@ -65,33 +78,43 @@ bp = BSyncProcessor(data=file_data) ``` #### Assumptions + 1. Assuming 1 building per file 1. Assuming sqft method uses "Conditioned" floor area for calculations. If not present, uses "Gross" -1. Assuming averages that use served space area must be defined in Sections (LinkedSectionIDs). LinkedBuildingID is not used. +1. Assuming averages that use served space area must be defined in Sections (LinkedSectionIDs). LinkedBuildingID is not + used. #### TODO -1. thermal zones: when spaces are listed within them with spaces (or multiple thermal zones), this would change the average setpoint calculations. Is this an exception or a normal case to handle? + +1. thermal zones: when spaces are listed within them with spaces (or multiple thermal zones), this would change the + average setpoint calculations. Is this an exception or a normal case to handle? #### Assets Definitions File -This file is used to specify what assets to extract from a BuildingSync XML file. By default, the file found in `config/asset_definitions.json` is used, but a custom file can be specified with the `set_asset_defs_file` method in the `BSyncProcessor` class. +This file is used to specify what assets to extract from a BuildingSync XML file. By default, the file found in +`config/asset_definitions.json` is used, but a custom file can be specified with the `set_asset_defs_file` method in the +`BSyncProcessor` class. There are currently 5 types of assets that can be extracted: -1. sqft: Sqft assets take into account the floor area served by a specific asset and returns 'Primary' and 'Secondary' values. For example: Primary HVAC System and Secondary HVAC System. +1. sqft: Sqft assets take into account the floor area served by a specific asset and returns 'Primary' and 'Secondary' + values. For example: Primary HVAC System and Secondary HVAC System. -1. avg_sqft: Avg_sqft assets compute a weighted average to get the an average asset value. For example: Average Heating Setpoint. +1. avg_sqft: Avg_sqft assets compute a weighted average to get the an average asset value. For example: Average Heating + Setpoint. -1. num: Num assets count the total number of the specified asset found. For example, Total number of lighting systems. +1. num: Num assets count the total number of the specified asset found. For example, Total number of lighting systems. -1. age_oldest, age_newest, and age_average: These types return the oldest or newest asset, or average age of a specific type. For example: Oldest Boiler. +1. age_oldest, age_newest, and age_average: These types return the oldest or newest asset, or average age of a specific + type. For example: Oldest Boiler. -1. custom: For asset that need particular handling, such as Heating Efficiency. The current assets that have custom methods are: - - Heating System Efficiency - - Cooling System Efficiency - - Lighting System Efficiency - - Water Heater Efficiency - - Heating Fuel Type +1. custom: For asset that need particular handling, such as Heating Efficiency. The current assets that have custom + methods are: + - Heating System Efficiency + - Cooling System Efficiency + - Lighting System Efficiency + - Water Heater Efficiency + - Heating Fuel Type The schema for the assets definition JSON file is in `schemas/asset_definitions_schema.json`. @@ -99,61 +122,76 @@ The schema for the assets definition JSON file is in `schemas/asset_definitions_ The schema for the extracted assets JSON file is in `schemas/extracted_assets_schema.json`. -This file lists the extracted assets information in name, value, units triples. Names will match the `export_name` listed in the asset_definitions JSON file, except for assets of type 'sqft', which will be prepended by 'Primary' and 'Secondary'. +This file lists the extracted assets information in name, value, units triples. Names will match the `export_name` +listed in the asset_definitions JSON file, except for assets of type 'sqft', which will be prepended by 'Primary' and ' +Secondary'. ### BUILDINGSYNC to CTS Spreadsheet Processor -This functionality takes in a list of buildingsync filepaths, runs the process to extract relevant information, aggregate at the Facility level, and generate an XLSX file in the expected CTS format. This is the `CTS Comprehensive Evaluation Upload Template`. +This functionality takes in a list of buildingsync filepaths, runs the process to extract relevant information, +aggregate at the Facility level, and generate an XLSX file in the expected CTS format. This is the +`CTS Comprehensive Evaluation Upload Template`. To test usage: ```bash - python buildingsync_asset_extractor/cts_main.py +python buildingsync_asset_extractor/cts_main.py ``` -This will process the 2 primary schools XML files (that will be aggregated into 1 facility) and the office XML file (which will be another facility) found in `tests/files/` and generate a XLSX containing 2 facilities. The resulting file will be saved to `tests\output\cts_output.xlsx`. +This will process the 2 primary schools XML files (that will be aggregated into 1 facility) and the office XML file ( +which will be another facility) found in `tests/files/` and generate a XLSX containing 2 facilities. The resulting file +will be saved to `tests\output\cts_output.xlsx`. ### -- BuildingSync files are aggregated by facility based on the Facility ID in the file. This ID can be found in the `` section, within the `` subsection: - - ``` - - - Agency Designated Covered Facility ID - ABC 123 - - - Sub-agency Acronym - ABC - - - Facility Name - Test Facility - - + +- BuildingSync files are aggregated by facility based on the Facility ID in the file. This ID can be found in the + `` section, within the `` subsection: + + ```xml + + + Agency Designated Covered Facility ID + ABC 123 + + + Sub-agency Acronym + ABC + + + Facility Name + Test Facility + + ``` -- The process will extract the measures that are part of the `cheapest` scenario within each file. The measures will be aggregated at the Facility level and the number of measures in each category will be added to the spreadsheet. If there is no cost, no measures will be counted. -- More information about the BuildingSync to CTS mappings can be found in the [cts_map page](buildingsync_asset_extractor/cts/cts_map.md). + +- The process will extract the measures that are part of the `cheapest` scenario within each file. The measures will be + aggregated at the Facility level and the number of measures in each category will be added to the spreadsheet. If + there is no cost, no measures will be counted. +- More information about the BuildingSync to CTS mappings can be found in + the [cts_map page](buildingsync_asset_extractor/cts/cts_map.md). ## Developing ### Pre-commit This project uses `pre-commit `_ to ensure code consistency. -To enable pre-commit on every commit run the following from the command line from within the git checkout of the BuildingSync-asset-extractor +To enable pre-commit on every commit run the following from the command line from within the git checkout of the +BuildingSync-asset-extractor ```bash - pre-commit install +pre-commit install ``` -To run pre-commit against the files without calling git commit, then run the following. This is useful when cleaning up the repo before committing. +To run pre-commit against the files without calling git commit, then run the following. This is useful when cleaning up +the repo before committing. ```bash - pre-commit run --all-files +pre-commit run --all-files ``` + ### Testing - poetry run pytest + poetry run pytest ## Releasing @@ -167,7 +205,9 @@ poetry publish -r testpypi # install from testpypi pip install --index-url https://test.pypi.org/simple/ buildingsync-asset-extractor ``` + If everything looks good, publish to pypi: + ```bash poetry publish ``` diff --git a/buildingsync_asset_extractor/converter.py b/buildingsync_asset_extractor/converter.py index 850c260..f16d12b 100644 --- a/buildingsync_asset_extractor/converter.py +++ b/buildingsync_asset_extractor/converter.py @@ -21,14 +21,15 @@ def unify_units( - system_datas: list[SystemData], to_units: Optional[str] = None + system_datas: list[SystemData], + to_units: Optional[str] = None, ) -> list[SystemData]: if to_units is None: to_units = system_datas[0].cap_units for sd in system_datas: if sd.cap is not None: try: - sd.cap = convert(float(sd.cap), sd.cap_units, to_units) # type: ignore + sd.cap = convert(float(sd.cap), sd.cap_units, to_units) # type: ignore[arg-type, assignment] sd.cap_units = to_units except BSyncProcessorError: pass diff --git a/buildingsync_asset_extractor/cts/__init__.py b/buildingsync_asset_extractor/cts/__init__.py index 4e84012..b7bf4fa 100644 --- a/buildingsync_asset_extractor/cts/__init__.py +++ b/buildingsync_asset_extractor/cts/__init__.py @@ -1 +1 @@ -from buildingsync_asset_extractor.cts.cts import building_sync_to_cts # NOQA +from buildingsync_asset_extractor.cts.cts import building_sync_to_cts diff --git a/buildingsync_asset_extractor/cts/classes.py b/buildingsync_asset_extractor/cts/classes.py index 6aeb1e5..a19d40e 100644 --- a/buildingsync_asset_extractor/cts/classes.py +++ b/buildingsync_asset_extractor/cts/classes.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import Iterable, Optional -from lxml import etree as ETree +from lxml import etree as ETree # noqa: N812 # Gets or creates a logger logging.basicConfig() @@ -38,10 +38,7 @@ class FacilityAppearance: @functools.cached_property def cheapest_package_of_measures_scenario(self) -> Optional[PackageOfMeasuresScenario]: # get the measures for reference - measures_by_id = { - m.get("ID"): Measure(m) - for m in self.etree.findall("./Measures/Measure", self.etree.nsmap) - } + measures_by_id = {m.get("ID"): Measure(m) for m in self.etree.findall("./Measures/Measure", self.etree.nsmap)} cheapest_package_of_measures_scenario = None cheapest_cost = None @@ -72,6 +69,7 @@ def __init__(self, etree: ETree, path: Path) -> None: @dataclass class Facility: """Name of the facility and it's appearances in each file""" + name: str appearances: list[FacilityAppearance] = field(default_factory=list) diff --git a/buildingsync_asset_extractor/cts/cts.py b/buildingsync_asset_extractor/cts/cts.py index 2b2359a..25b20b4 100644 --- a/buildingsync_asset_extractor/cts/cts.py +++ b/buildingsync_asset_extractor/cts/cts.py @@ -1,4 +1,3 @@ - import logging from pathlib import Path @@ -6,16 +5,13 @@ from lxml import etree from styleframe import StyleFrame -from buildingsync_asset_extractor.cts.classes import ( - Facility, - FacilityAppearance -) +from buildingsync_asset_extractor.cts.classes import Facility, FacilityAppearance from buildingsync_asset_extractor.cts.parsers import ( get_aggregated_findings_of_comprehensive_evaluations_estimated_annual_data, get_aggregated_findings_of_comprehensive_evaluations_estimated_life_cycle_data, get_covered_facility_identification_information, get_potential_conservation_measures_per_technology_category, - parse_user_defined_fields + parse_user_defined_fields, ) # Gets or creates a logger @@ -36,7 +32,9 @@ def log_facilities(facility_by_id: dict[str, Facility]) -> None: logger.info(f"\t\t-{appearance.path}") cpoms = appearance.cheapest_package_of_measures_scenario if cpoms: - logger.info(f"\t\t\tusing cheapest package of measures: {cpoms.id} (measures: {list(cpoms.measures_by_id.keys())})") + logger.info( + f"\t\t\tusing cheapest package of measures: {cpoms.id} (measures: {list(cpoms.measures_by_id.keys())})", + ) def aggregate_facilities(files: list[Path]) -> dict[str, Facility]: @@ -44,7 +42,7 @@ def aggregate_facilities(files: list[Path]) -> dict[str, Facility]: # for each file, get the facilities in the file for f in files: - file_etree = etree.parse(f) + file_etree = etree.parse(f) # noqa: S320 facility_etrees = file_etree.findall("/Facilities/Facility", namespaces=file_etree.getroot().nsmap) # for each facility in the file, add it to facility_by_id @@ -63,23 +61,25 @@ def aggregate_facilities(files: list[Path]) -> dict[str, Facility]: def building_sync_to_cts(files: list[Path], out_file: Path) -> None: # import blank template - df = pd.read_excel(BLANK_CTS_FILE_PATH, sheet_name="Evaluation Upload Template") + template_df = pd.read_excel(BLANK_CTS_FILE_PATH, sheet_name="Evaluation Upload Template") # for each facility, fill in a row facility_by_id = aggregate_facilities(files) for i, (facility_name, facility) in enumerate(facility_by_id.items()): - df.loc[3 + i] = to_cts_row(facility) + template_df.loc[3 + i] = to_cts_row(facility) # write back out - sf = StyleFrame.read_excel_as_template(BLANK_CTS_FILE_PATH, df=df, sheet_name="Evaluation Upload Template") + sf = StyleFrame.read_excel_as_template(BLANK_CTS_FILE_PATH, df=template_df, sheet_name="Evaluation Upload Template") writer = sf.to_excel(out_file, row_to_add_filters=0) writer.close() def to_cts_row(facility: Facility) -> pd.Series: - return pd.concat([ - get_covered_facility_identification_information(facility), - get_aggregated_findings_of_comprehensive_evaluations_estimated_annual_data(facility), - get_aggregated_findings_of_comprehensive_evaluations_estimated_life_cycle_data(facility), - get_potential_conservation_measures_per_technology_category(facility), - ]) + return pd.concat( + [ + get_covered_facility_identification_information(facility), + get_aggregated_findings_of_comprehensive_evaluations_estimated_annual_data(facility), + get_aggregated_findings_of_comprehensive_evaluations_estimated_life_cycle_data(facility), + get_potential_conservation_measures_per_technology_category(facility), + ], + ) diff --git a/buildingsync_asset_extractor/cts/energy_and_water_conservation_measures.py b/buildingsync_asset_extractor/cts/energy_and_water_conservation_measures.py index e194a84..1bfc1a2 100644 --- a/buildingsync_asset_extractor/cts/energy_and_water_conservation_measures.py +++ b/buildingsync_asset_extractor/cts/energy_and_water_conservation_measures.py @@ -44,7 +44,6 @@ "technology_category": "BoilerPlantImprovements", "measure_name": "Convert to Cleaner Fuels", }, - # ChillerPlantImprovements "B_Add energy recovery": { "technology_category": "ChillerPlantImprovements", @@ -82,758 +81,739 @@ "technology_category": "ChillerPlantImprovements", "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # BuildingAutomationSystems "C_Add or Upgrade BAS/EMS/EMCS": { "technology_category": "BuildingAutomationSystems", - "measure_name": "Add or upgrade BAS/EMS/EMCS" + "measure_name": "Add or upgrade BAS/EMS/EMCS", }, "C_Add or Upgrade Controls": { "technology_category": "BuildingAutomationSystems", - "measure_name": "Add or upgrade controls" + "measure_name": "Add or upgrade controls", }, "C_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "BuildingAutomationSystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, "C_Convert pneumatic controls to DDC": { "technology_category": "BuildingAutomationSystems", - "measure_name": "Convert pneumatic controls to DDC" + "measure_name": "Convert pneumatic controls to DDC", }, - # OtherHVAC "D_Add Duct Insulation": { "technology_category": "OtherHVAC", - "measure_name": "Add duct insulation" + "measure_name": "Add duct insulation", }, "D_Add or replace Cooling Tower": { "technology_category": "OtherHVAC", - "measure_name": "Add or replace cooling tower" + "measure_name": "Add or replace cooling tower", }, "D_Convert CV System to VAV System": { "technology_category": "OtherHVAC", - "measure_name": "Convert CV system to VAV system" + "measure_name": "Convert CV system to VAV system", }, "D_Add energy recovery": { "technology_category": "OtherHVAC", - "measure_name": "Add energy recovery" + "measure_name": "Add energy recovery", }, "D_Other cooling": { "technology_category": "OtherHVAC", - "measure_name": "Other cooling" + "measure_name": "Other cooling", }, "D_Balance ventilation/distribution system": { "technology_category": "OtherHVAC", - "measure_name": "Balance ventilation/distribution system" + "measure_name": "Balance ventilation/distribution system", }, "D_Improve distribution fans": { "technology_category": "OtherHVAC", - "measure_name": "Improve distribution fans" + "measure_name": "Improve distribution fans", }, "D_Add or repair economizer": { "technology_category": "OtherHVAC", - "measure_name": "Add or repair economizer" + "measure_name": "Add or repair economizer", }, "D_Other distribution": { "technology_category": "OtherHVAC", - "measure_name": "Other distribution" + "measure_name": "Other distribution", }, "D_Clean and/or repair": { "technology_category": "OtherHVAC", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "D_Implement training and/or documentation": { "technology_category": "OtherHVAC", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "D_Repair or replace HVAC damper and controller": { "technology_category": "OtherHVAC", - "measure_name": "Repair or replace HVAC damper and controller" + "measure_name": "Repair or replace HVAC damper and controller", }, "D_Repair leaks in ducts": { "technology_category": "OtherHVAC", - "measure_name": "Repair leaks / seal ducts" + "measure_name": "Repair leaks / seal ducts", }, "D_Seal ducts": { "technology_category": "OtherHVAC", - "measure_name": "Repair leaks / seal ducts" + "measure_name": "Repair leaks / seal ducts", }, "D_Replace Package Units": { "technology_category": "OtherHVAC", - "measure_name": "Replace package units" + "measure_name": "Replace package units", }, "D_Replace Packaged Terminal Units": { "technology_category": "OtherHVAC", - "measure_name": "Replace packaged terminal units" + "measure_name": "Replace packaged terminal units", }, "D_Replace or modify AHU": { "technology_category": "OtherHVAC", - "measure_name": "Replace or modify AHU" + "measure_name": "Replace or modify AHU", }, "D_Other ventilation": { "technology_category": "OtherHVAC", - "measure_name": "Other ventilation" + "measure_name": "Other ventilation", }, "D_Improve ventilation fans": { "technology_category": "OtherHVAC", - "measure_name": "Improve ventilation fans" + "measure_name": "Improve ventilation fans", }, "D_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "OtherHVAC", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, "D_Replace burner": { "technology_category": "OtherHVAC", - "measure_name": "Replace burner" + "measure_name": "Replace burner", }, "D_Install passive solar heating": { "technology_category": "OtherHVAC", - "measure_name": "Install passive solar heating" + "measure_name": "Install passive solar heating", }, "D_Replace AC and heating units with ground coupled heat pump systems": { "technology_category": "OtherHVAC", - "measure_name": "Replace AC and heating units with ground coupled heat pump systems" + "measure_name": "Replace AC and heating units with ground coupled heat pump systems", }, "D_Install gas cooling": { "technology_category": "OtherHVAC", - "measure_name": "Install gas cooling" + "measure_name": "Install gas cooling", }, "D_Add enhanced dehumidification": { "technology_category": "OtherHVAC", - "measure_name": "Add enhanced dehumidification" + "measure_name": "Add enhanced dehumidification", }, "D_Install solar ventilation preheating system": { "technology_category": "OtherHVAC", - "measure_name": "Install solar ventilation preheating system" + "measure_name": "Install solar ventilation preheating system", }, "D_Other heating": { "technology_category": "OtherHVAC", - "measure_name": "Other heating" + "measure_name": "Other heating", }, "D_Install air source heat pump": { "technology_category": "OtherHVAC", - "measure_name": "Install air source heat pump" + "measure_name": "Install air source heat pump", }, "D_Install variable refrigerant flow system": { "technology_category": "OtherHVAC", - "measure_name": "Install variable refrigerant flow system" + "measure_name": "Install variable refrigerant flow system", }, "D_Capture and return condensate": { "technology_category": "OtherHVAC", - "measure_name": "Capture and return condensate" + "measure_name": "Capture and return condensate", }, "D_Install demand control ventilation": { "technology_category": "OtherHVAC", - "measure_name": "Install demand control ventilation" + "measure_name": "Install demand control ventilation", }, "D_Install thermal destratification fans": { "technology_category": "OtherHVAC", - "measure_name": "Install thermal destratification fans" + "measure_name": "Install thermal destratification fans", }, - # LightingImprovements "E_Add daylight controls": { - "technology_category": "LightingImprovements", - "measure_name": "Add daylight controls" + "technology_category": "LightingImprovements", + "measure_name": "Add daylight controls", }, "E_Add occupancy sensors": { "technology_category": "LightingImprovements", - "measure_name": "Add occupancy sensors" + "measure_name": "Add occupancy sensors", }, "E_Retrofit with fiber optic lighting technologies": { "technology_category": "LightingImprovements", - "measure_name": "Retrofit with fiber optic lighting technologies" + "measure_name": "Retrofit with fiber optic lighting technologies", }, "E_Retrofit with light emitting diode technologies": { "technology_category": "LightingImprovements", - "measure_name": "Retrofit with light emitting diode technologies" + "measure_name": "Retrofit with light emitting diode technologies", }, "E_Clean and/or repair": { "technology_category": "LightingImprovements", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "E_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "LightingImprovements", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, "E_Retrofit with CFLs": { "technology_category": "LightingImprovements", - "measure_name": "Retrofit with CFLs" + "measure_name": "Retrofit with CFLs", }, "E_Retrofit with T-5": { "technology_category": "LightingImprovements", - "measure_name": "Retrofit with T-5" + "measure_name": "Retrofit with T-5", }, "E_Retrofit with T-8": { "technology_category": "LightingImprovements", - "measure_name": "Retrofit with T-8" + "measure_name": "Retrofit with T-8", }, "E_Install spectrally enhanced lighting": { "technology_category": "LightingImprovements", - "measure_name": "Install spectrally enhanced lighting" + "measure_name": "Install spectrally enhanced lighting", }, "E_Implement training and/or documentation": { "technology_category": "LightingImprovements", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "E_Install timers": { "technology_category": "LightingImprovements", - "measure_name": "Install timers" + "measure_name": "Install timers", }, "E_Replace diffusers": { "technology_category": "LightingImprovements", - "measure_name": "Replace diffusers" + "measure_name": "Replace diffusers", }, "E_Upgrade exterior lighting": { "technology_category": "LightingImprovements", - "measure_name": "Upgrade exterior lighting" + "measure_name": "Upgrade exterior lighting", }, "E_Upgrade exit signs to LED": { "technology_category": "LightingImprovements", - "measure_name": "Upgrade exit signs to LED" + "measure_name": "Upgrade exit signs to LED", }, "E_Install photocell control": { "technology_category": "LightingImprovements", - "measure_name": "Install photocell control" + "measure_name": "Install photocell control", }, - # BuildingEnvelopeModifications "F_Add attic/knee wall insulation": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Add attic/knee wall insulation" + "measure_name": "Add attic/knee wall insulation", }, "F_Add shading devices": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Add shading devices" + "measure_name": "Add shading devices", }, "F_Add window films": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Add window films" + "measure_name": "Add window films", }, "F_Air seal envelope": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Air seal envelope" + "measure_name": "Air seal envelope", }, "F_Install cool/green roof": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Install cool/green roof" + "measure_name": "Install cool/green roof", }, "F_Clean and/or repair": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "F_Increase ceiling insulation": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Increase ceiling insulation" + "measure_name": "Increase ceiling insulation", }, "F_Increase floor insulation": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Increase floor insulation" + "measure_name": "Increase floor insulation", }, "F_Increase roof insulation": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Increase roof insulation" + "measure_name": "Increase roof insulation", }, "F_Increase wall insulation": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Increase wall insulation" + "measure_name": "Increase wall insulation", }, "F_Insulate attic hatch": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Insulate attic hatch / stair box" + "measure_name": "Insulate attic hatch / stair box", }, "F_Insulate attic stair box": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Insulate attic hatch / stair box" + "measure_name": "Insulate attic hatch / stair box", }, "F_Insulate foundation": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Insulate foundation" + "measure_name": "Insulate foundation", }, "F_Insulate thermal bypasses": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Insulate thermal bypasses" + "measure_name": "Insulate thermal bypasses", }, "F_Install or replace solar screens": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Install or replace solar screens" + "measure_name": "Install or replace solar screens", }, "F_Replace glazing": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Replace glazing" + "measure_name": "Replace glazing", }, "F_Replace windows": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Replace windows" + "measure_name": "Replace windows", }, "F_Close elevator and/or stairwell shaft vents": { "technology_category": "BuildingEnvelopeModifications", - "measure_name": "Close elevator and/or stairwell shaft vents" + "measure_name": "Close elevator and/or stairwell shaft vents", }, - # Chilled Water, Hot Water, and Steam Distribution Systems "G_Add recirculating pumps": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Add recirculating pumps" + "measure_name": "Add recirculating pumps", }, "G_Add pipe insulation": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Add pipe insulation" + "measure_name": "Add pipe insulation", }, "G_Add energy recovery": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Add energy recovery" + "measure_name": "Add energy recovery", }, "G_Replace or upgrade water heater": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Replace or upgrade water heater" + "measure_name": "Replace or upgrade water heater", }, "G_Repair or replace existing condensate return systems or install new condensate return systems": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Repair or replace existing condensate return systems or install new condensate return systems" + "measure_name": "Repair or replace existing condensate return systems or install new condensate return systems", }, "G_Install solar hot water system": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Install solar hot water system" + "measure_name": "Install solar hot water system", }, "G_Repair and/or replace steam traps": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Repair and/or replace steam traps" + "measure_name": "Repair and/or replace steam traps", }, "G_Retrofit and replace chiller plant pumping, piping, and controls": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Retrofit and replace chiller plant pumping, piping, and controls" + "measure_name": "Retrofit and replace chiller plant pumping, piping, and controls", }, "G_Replace with higher efficiency pump": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Replace with higher efficiency pump" + "measure_name": "Replace with higher efficiency pump", }, "G_Replace with variable speed pump": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Replace with variable speed pump" + "measure_name": "Replace with variable speed pump", }, "G_Clean and/or repair": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "G_Implement training and/or documentation": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "G_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, "G_Install or upgrade master venting": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Install or upgrade master venting" + "measure_name": "Install or upgrade master venting", }, "G_Replace steam traps with orifice plates": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Replace steam traps with orifice plates" + "measure_name": "Replace steam traps with orifice plates", }, "G_Install steam condensate heat recovery": { "technology_category": "ChilledWaterHotWaterAndSteamDistributionSystems", - "measure_name": "Install steam condensate heat recovery" + "measure_name": "Install steam condensate heat recovery", }, - # Electric Motors and Drives "H_Replace with higher efficiency": { "technology_category": "OtherElectricMotorsAndDrives", - "measure_name": "Replace with higher efficiency" + "measure_name": "Replace with higher efficiency", }, "H_Add VSD motor controller": { "technology_category": "OtherElectricMotorsAndDrives", - "measure_name": "Add VSD motor controller" + "measure_name": "Add VSD motor controller", }, "H_Add drive controls": { "technology_category": "OtherElectricMotorsAndDrives", - "measure_name": "Add drive controls" + "measure_name": "Add drive controls", }, "H_Clean and/or repair": { "technology_category": "OtherElectricMotorsAndDrives", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "H_Implement training and/or documentation": { "technology_category": "OtherElectricMotorsAndDrives", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "H_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "OtherElectricMotorsAndDrives", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Refrigeration "I_Replace ice/refrigeration equipment with high efficiency units": { "technology_category": "Refrigeration", - "measure_name": "Replace ice/refrigeration equipment with high efficiency units" + "measure_name": "Replace ice/refrigeration equipment with high efficiency units", }, "I_Replace air-cooled ice/refrigeration equipment": { "technology_category": "Refrigeration", - "measure_name": "Replace air-cooled ice/refrigeration equipment" + "measure_name": "Replace air-cooled ice/refrigeration equipment", }, "I_Replace refrigerators": { "technology_category": "Refrigeration", - "measure_name": "Replace refrigerators" + "measure_name": "Replace refrigerators", }, "I_Clean and/or repair": { "technology_category": "Refrigeration", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "I_Implement training and/or documentation": { "technology_category": "Refrigeration", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "I_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "Refrigeration", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Distributed Generation "J_Install CHP/cogeneration systems": { "technology_category": "DistributedGeneration", - "measure_name": "Install CHP/cogeneration systems" + "measure_name": "Install CHP/cogeneration systems", }, "J_Install fuel cells": { "technology_category": "DistributedGeneration", - "measure_name": "Install fuel cells" + "measure_name": "Install fuel cells", }, "J_Install microturbines": { "technology_category": "DistributedGeneration", - "measure_name": "Install microturbines" + "measure_name": "Install microturbines", }, "J_Convert fuels": { "technology_category": "DistributedGeneration", - "measure_name": "Convert fuels" + "measure_name": "Convert fuels", }, "J_Clean and/or repair": { "technology_category": "DistributedGeneration", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "J_Implement training and/or documentation": { "technology_category": "DistributedGeneration", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "J_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "DistributedGeneration", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Renewable Energy System "K_Install landfill gas, wastewater treatment plant digester gas, or coal bed methane power plant": { "technology_category": "RenewableEnergySystems", - "measure_name": "Install landfill gas, wastewater treatment plant digester gas, or coal bed methane power plant" + "measure_name": "Install landfill gas, wastewater treatment plant digester gas, or coal bed methane power plant", }, "K_Install photovoltaic system": { "technology_category": "RenewableEnergySystems", - "measure_name": "Install photovoltaic system" + "measure_name": "Install photovoltaic system", }, "K_Install wind energy system": { "technology_category": "RenewableEnergySystems", - "measure_name": "Install wind energy system" + "measure_name": "Install wind energy system", }, "K_Install wood waste or other organic waste stream heating or power plant": { "technology_category": "RenewableEnergySystems", - "measure_name": "Install wood waste or other organic waste stream heating or power plant" + "measure_name": "Install wood waste or other organic waste stream heating or power plant", }, "K_Clean and/or repair": { "technology_category": "RenewableEnergySystems", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "K_Implement training and/or documentation": { "technology_category": "RenewableEnergySystems", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "K_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "RenewableEnergySystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, "K_Install electrical storage": { "technology_category": "RenewableEnergySystems", - "measure_name": "Install electrical storage" + "measure_name": "Install electrical storage", }, - # Energy / Utility Distribution Systems "L_Install gas distribution systems": { "technology_category": "EnergyDistributionSystems", - "measure_name": "Install gas distribution systems" + "measure_name": "Install gas distribution systems", }, "L_Implement power factor corrections": { "technology_category": "EnergyDistributionSystems", - "measure_name": "Implement power factor corrections" + "measure_name": "Implement power factor corrections", }, "L_Implement power quality upgrades": { "technology_category": "EnergyDistributionSystems", - "measure_name": "Implement power quality upgrades" + "measure_name": "Implement power quality upgrades", }, "L_Upgrade transformers": { "technology_category": "EnergyDistributionSystems", - "measure_name": "Upgrade transformers" + "measure_name": "Upgrade transformers", }, "L_Clean and/or repair": { "technology_category": "EnergyDistributionSystems", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "L_Implement training and/or documentation": { "technology_category": "EnergyDistributionSystems", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "L_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "EnergyDistributionSystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Water and Sewer Conservation Systems "M_Install low-flow faucets and showerheads": { "technology_category": "WaterAndSewerConservationSystems", - "measure_name": "Install low-flow faucets and showerheads" + "measure_name": "Install low-flow faucets and showerheads", }, "M_Install low-flow plumbing equipment": { "technology_category": "WaterAndSewerConservationSystems", - "measure_name": "Install low-flow plumbing equipment" + "measure_name": "Install low-flow plumbing equipment", }, "M_Install onsite sewer treatment systems": { "technology_category": "WaterAndSewerConservationSystems", - "measure_name": "Install onsite sewer treatment systems" + "measure_name": "Install onsite sewer treatment systems", }, "M_Implement water efficient irrigation": { "technology_category": "WaterAndSewerConservationSystems", - "measure_name": "Implement water efficient irrigation" + "measure_name": "Implement water efficient irrigation", }, "M_Clean and/or repair": { "technology_category": "WaterAndSewerConservationSystems", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "M_Implement training and/or documentation": { "technology_category": "WaterAndSewerConservationSystems", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "M_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "WaterAndSewerConservationSystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Electrical Peak Shaving / Load Shifting "N_Install thermal energy storage": { "technology_category": "ElectricalPeakShavingLoadShifting", - "measure_name": "Install thermal energy storage" + "measure_name": "Install thermal energy storage", }, "N_Implement training and/or documentation": { "technology_category": "ElectricalPeakShavingLoadShifting", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "N_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "ElectricalPeakShavingLoadShifting", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Rate Adjustments "O_Change to more favorable rate schedule": { "technology_category": "EnergyCostReductionThroughRateAdjustments", - "measure_name": "Change to more favorable rate schedule" + "measure_name": "Change to more favorable rate schedule", }, "O_Energy service billing and meter auditing recommendations": { "technology_category": "EnergyCostReductionThroughRateAdjustments", - "measure_name": "Energy service billing and meter auditing recommendations" + "measure_name": "Energy service billing and meter auditing recommendations", }, "O_Change to lower energy cost supplier(s)": { "technology_category": "EnergyCostReductionThroughRateAdjustments", - "measure_name": "Change to lower energy cost supplier(s)" + "measure_name": "Change to lower energy cost supplier(s)", }, - # Energy Related Process Improvements "P_Implement industrial process improvements": { "technology_category": "EnergyRelatedProcessImprovements", - "measure_name": "Implement industrial process improvements" + "measure_name": "Implement industrial process improvements", }, "P_Implement production and/or manufacturing improvements": { "technology_category": "EnergyRelatedProcessImprovements", - "measure_name": "Implement production and/or manufacturing improvements" + "measure_name": "Implement production and/or manufacturing improvements", }, "P_Clean and/or repair": { "technology_category": "EnergyRelatedProcessImprovements", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "P_Implement training and/or documentation": { "technology_category": "EnergyRelatedProcessImprovements", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "P_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "EnergyRelatedProcessImprovements", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Advanced Metering Systems "Q_Install advanced metering systems": { "technology_category": "AdvancedMeteringSystems", - "measure_name": "Install advanced metering systems" + "measure_name": "Install advanced metering systems", }, "Q_Clean and/or repair": { "technology_category": "AdvancedMeteringSystems", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "Q_Implement training and/or documentation": { "technology_category": "AdvancedMeteringSystems", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "Q_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "AdvancedMeteringSystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Appliance / Plug-load Reductions "R_De-lamp vending machines": { "technology_category": "PlugLoadReductions", - "measure_name": "De-lamp vending machines" + "measure_name": "De-lamp vending machines", }, "R_Install plug load controls": { "technology_category": "PlugLoadReductions", - "measure_name": "Install plug load controls" + "measure_name": "Install plug load controls", }, "R_Clean and/or repair": { "technology_category": "PlugLoadReductions", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "R_Replace with ENERGY STAR rated": { "technology_category": "PlugLoadReductions", - "measure_name": "Replace with ENERGY STAR rated" + "measure_name": "Replace with ENERGY STAR rated", }, "R_Implement training and/or documentation": { "technology_category": "PlugLoadReductions", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "R_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "PlugLoadReductions", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, "R_Replace clothes dryers": { "technology_category": "PlugLoadReductions", - "measure_name": "Replace clothes dryers" + "measure_name": "Replace clothes dryers", }, "R_Replace washing machines": { "technology_category": "PlugLoadReductions", - "measure_name": "Replace washing machines" + "measure_name": "Replace washing machines", }, "R_Automatic shutdown or sleep mode for computers": { "technology_category": "PlugLoadReductions", - "measure_name": "Automatic shutdown or sleep mode for computers" + "measure_name": "Automatic shutdown or sleep mode for computers", }, - # Service Hot Water (SHW) Systems "U_Decrease SHW temperature": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Decrease SHW temperature" + "measure_name": "Decrease SHW temperature", }, "U_Install SHW controls": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Install SHW controls" + "measure_name": "Install SHW controls", }, "U_Install solar thermal SHW": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Install solar thermal SHW" + "measure_name": "Install solar thermal SHW", }, "U_Install water pressure booster": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Install water pressure booster" + "measure_name": "Install water pressure booster", }, "U_Insulate SHW piping": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Insulate SHW piping" + "measure_name": "Insulate SHW piping", }, "U_Insulate SHW tank": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Insulate SHW tank" + "measure_name": "Insulate SHW tank", }, "U_Replace piping": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Replace piping" + "measure_name": "Replace piping", }, "U_Replace tankless coil": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Replace tankless coil" + "measure_name": "Replace tankless coil", }, "U_Separate SHW from heating": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Separate SHW from heating" + "measure_name": "Separate SHW from heating", }, "U_Upgrade SHW boiler": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Upgrade SHW boiler" + "measure_name": "Upgrade SHW boiler", }, "U_Install heat pump SHW system": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Install heat pump SHW system" + "measure_name": "Install heat pump SHW system", }, "U_Install tankless water heaters": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Install tankless water heaters" + "measure_name": "Install tankless water heaters", }, "U_Clean and/or repair": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "U_Implement training and/or documentation": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "U_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "ServiceHotWaterSystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Conveyance Systems "V_Add elevator regenerative drives": { "technology_category": "ConveyanceSystems", - "measure_name": "Add elevator regenerative drives" + "measure_name": "Add elevator regenerative drives", }, "V_Upgrade controls": { "technology_category": "ConveyanceSystems", - "measure_name": "Upgrade controls" + "measure_name": "Upgrade controls", }, "V_Upgrade motors": { "technology_category": "ConveyanceSystems", - "measure_name": "Upgrade motors" + "measure_name": "Upgrade motors", }, "V_Clean and/or repair": { "technology_category": "ConveyanceSystems", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "V_Implement training and/or documentation": { "technology_category": "ConveyanceSystems", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "V_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "ConveyanceSystems", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", }, - # Data Center Energy Conservation Improv "W_Improve data center efficiency": { "technology_category": "DataCenterImprovements", - "measure_name": "Improve data center efficiency" + "measure_name": "Improve data center efficiency", }, "W_Implement hot aisle hold aisle design": { "technology_category": "DataCenterImprovements", - "measure_name": "Implement hot aisle hold aisle design" + "measure_name": "Implement hot aisle hold aisle design", }, "W_Implement server virtualization": { "technology_category": "DataCenterImprovements", - "measure_name": "Implement server virtualization" + "measure_name": "Implement server virtualization", }, "W_Upgrade servers": { "technology_category": "DataCenterImprovements", - "measure_name": "Upgrade servers" + "measure_name": "Upgrade servers", }, "W_Clean and/or repair": { "technology_category": "DataCenterImprovements", - "measure_name": "Clean and/or repair" + "measure_name": "Clean and/or repair", }, "W_Implement training and/or documentation": { "technology_category": "DataCenterImprovements", - "measure_name": "Implement training and/or documentation" + "measure_name": "Implement training and/or documentation", }, "W_Upgrade operating protocols, calibration, and/or sequencing": { "technology_category": "DataCenterImprovements", - "measure_name": "Upgrade operating protocols, calibration, and/or sequencing" - } + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", + }, } diff --git a/buildingsync_asset_extractor/cts/measure_type_to_CTS_field.py b/buildingsync_asset_extractor/cts/measure_type_to_cts_field.py similarity index 97% rename from buildingsync_asset_extractor/cts/measure_type_to_CTS_field.py rename to buildingsync_asset_extractor/cts/measure_type_to_cts_field.py index e919640..75ee3c0 100644 --- a/buildingsync_asset_extractor/cts/measure_type_to_CTS_field.py +++ b/buildingsync_asset_extractor/cts/measure_type_to_cts_field.py @@ -1,4 +1,4 @@ -technology_category_to_CTS_field = { +technology_category_to_cts_field = { "BoilerPlantImprovements": "A_Uncategorized", "ChillerPlantImprovements": "B_Uncategorized", "BuildingAutomationSystems": "C_Uncategorized", diff --git a/buildingsync_asset_extractor/cts/parsers.py b/buildingsync_asset_extractor/cts/parsers.py index 22cb1e3..5107d2a 100644 --- a/buildingsync_asset_extractor/cts/parsers.py +++ b/buildingsync_asset_extractor/cts/parsers.py @@ -6,11 +6,9 @@ from buildingsync_asset_extractor.cts.classes import Facility from buildingsync_asset_extractor.cts.energy_and_water_conservation_measures import ( - ENERGY_AND_WATER_CONSERVATION_MEASURES -) -from buildingsync_asset_extractor.cts.measure_type_to_CTS_field import ( - technology_category_to_CTS_field + ENERGY_AND_WATER_CONSERVATION_MEASURES, ) +from buildingsync_asset_extractor.cts.measure_type_to_cts_field import technology_category_to_cts_field # Gets or creates a logger logging.basicConfig() @@ -21,13 +19,9 @@ def get_covered_facility_identification_information(facility: Facility) -> pd.Series: - user_defined_fields_trees = [ - fa.etree.find("./UserDefinedFields", fa.etree.nsmap) - for fa in facility.appearances - ] + user_defined_fields_trees = [fa.etree.find("./UserDefinedFields", fa.etree.nsmap) for fa in facility.appearances] user_defined_fields_dicts = [ - parse_user_defined_fields(user_defined_fields_tree) - for user_defined_fields_tree in user_defined_fields_trees + parse_user_defined_fields(user_defined_fields_tree) for user_defined_fields_tree in user_defined_fields_trees ] print(user_defined_fields_dicts) @@ -46,21 +40,13 @@ def get_aggregated_findings_of_comprehensive_evaluations_estimated_annual_data(f # data["Evaluation Completion Date"] = # Retro/Re-Commissioning Assessment - is_retrocommissioned = [ - fa.etree.find("./Reports/Report/RetrocommissioningAudit", fa.etree.nsmap) - for fa in facility.appearances - ] - data["Retro/Re-Commissioning Assessment"] = all([x is not None and x.text == "true" for x in is_retrocommissioned]) + is_retrocommissioned = [fa.etree.find("./Reports/Report/RetrocommissioningAudit", fa.etree.nsmap) for fa in facility.appearances] + data["Retro/Re-Commissioning Assessment"] = all(x is not None and x.text == "true" for x in is_retrocommissioned) # Gross Evaluated Square Footage - floor_areas = [ - fa.etree.find("./Sites/Site/Buildings/Building/FloorAreas/FloorArea", fa.etree.nsmap) - for fa in facility.appearances - ] + floor_areas = [fa.etree.find("./Sites/Site/Buildings/Building/FloorAreas/FloorArea", fa.etree.nsmap) for fa in facility.appearances] gross_floor_area = [ - float(fa.find("./FloorAreaValue", fa.nsmap).text) - for fa in floor_areas - if fa.find("./FloorAreaType", fa.nsmap).text == "Gross" + float(fa.find("./FloorAreaValue", fa.nsmap).text) for fa in floor_areas if fa.find("./FloorAreaType", fa.nsmap).text == "Gross" ] data["Gross Evaluated Square Footage"] = sum(gross_floor_area) @@ -72,12 +58,9 @@ def get_aggregated_findings_of_comprehensive_evaluations_estimated_annual_data(f ] def sum_from_package_of_measures_scenarios(xpath: str) -> float: - trees = [ - s.find("./ScenarioType/PackageOfMeasures/" + xpath, s.nsmap) - for s in package_of_measures_scenario_etrees - ] + trees = [s.find("./ScenarioType/PackageOfMeasures/" + xpath, s.nsmap) for s in package_of_measures_scenario_etrees] - return sum([float(t.text)for t in trees if t is not None]) + return sum([float(t.text) for t in trees if t is not None]) data["Estimated Implementation Cost of Measure(s)"] = sum_from_package_of_measures_scenarios("PackageFirstCost") data["Estimated Annual Energy Savings"] = sum_from_package_of_measures_scenarios("AnnualSavingsSiteEnergy") @@ -89,22 +72,24 @@ def sum_from_package_of_measures_scenarios(xpath: str) -> float: # data["Estimated Annual Renewable Thermal Output"] = # just sums of scenarios - data["Estimated Other Annual Ancillary Cost Savings"] = sum_from_package_of_measures_scenarios("OMCostAnnualSavings") + data["Estimated Other Annual Ancillary Cost Savings"] = sum_from_package_of_measures_scenarios( + "OMCostAnnualSavings", + ) return data -def get_aggregated_findings_of_comprehensive_evaluations_estimated_life_cycle_data(facility: Facility) -> pd.Series: +def get_aggregated_findings_of_comprehensive_evaluations_estimated_life_cycle_data(_facility: Facility) -> pd.Series: data = pd.Series() - # building sync doesnt have these + # building sync doesn't have these return data def get_potential_conservation_measures_per_technology_category(facility: Facility) -> pd.Series: - results = {k: 0 for k in ENERGY_AND_WATER_CONSERVATION_MEASURES.keys()} - results.update({k: 0 for k in technology_category_to_CTS_field.values()}) + results = {k: 0 for k in ENERGY_AND_WATER_CONSERVATION_MEASURES} + results.update({k: 0 for k in technology_category_to_cts_field.values()}) # for each measure measures = [ @@ -115,20 +100,25 @@ def get_potential_conservation_measures_per_technology_category(facility: Facili ] for m in measures: # get measure type - (measure_type, _) = next(filter( - lambda x: asdict(m).items() >= x[1].items(), - ENERGY_AND_WATER_CONSERVATION_MEASURES.items() - ), (None, None)) + (measure_type, _) = next( + filter( + lambda x: asdict(m).items() >= x[1].items(), + ENERGY_AND_WATER_CONSERVATION_MEASURES.items(), + ), + (None, None), + ) # account for measure in result if measure_type is not None: - logger.info(f"Measure `{m.id}` counted under {measure_type}` and `{technology_category_to_CTS_field[m.technology_category]}`") + logger.info( + f"Measure `{m.id}` counted under {measure_type}` and `{technology_category_to_cts_field[m.technology_category]}`", + ) results[measure_type] += 1 - results[technology_category_to_CTS_field[m.technology_category]] += 1 + results[technology_category_to_cts_field[m.technology_category]] += 1 else: logger.warning( - f"Measure `{m.id}` unable to be mapped. TechnologyCategory: `{m.technology_category}`, MeasureName: `{m.measure_name}`" + f"Measure `{m.id}` unable to be mapped. TechnologyCategory: `{m.technology_category}`, MeasureName: `{m.measure_name}`", ) return pd.Series(results) @@ -136,7 +126,9 @@ def get_potential_conservation_measures_per_technology_category(facility: Facili def parse_user_defined_fields(user_defined_fields_tree: etree.Element) -> dict[str, str]: return { - user_defined_field_tree.find("./FieldName", user_defined_field_tree.nsmap).text: - user_defined_field_tree.find("./FieldValue", user_defined_field_tree.nsmap).text + user_defined_field_tree.find("./FieldName", user_defined_field_tree.nsmap).text: user_defined_field_tree.find( + "./FieldValue", + user_defined_field_tree.nsmap, + ).text for user_defined_field_tree in user_defined_fields_tree } diff --git a/buildingsync_asset_extractor/cts_main.py b/buildingsync_asset_extractor/cts_main.py index 2bf5813..8c7644f 100644 --- a/buildingsync_asset_extractor/cts_main.py +++ b/buildingsync_asset_extractor/cts_main.py @@ -33,19 +33,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************************************* """ + from pathlib import Path from buildingsync_asset_extractor.cts.cts import building_sync_to_cts # test input files -PRIMARYSCHOOL_1_FILE_PATH = Path(__file__).parents[1] / 'tests/files/PrimarySchool-1.xml' -PRIMARYSCHOOL_2_FILE_PATH = Path(__file__).parents[1] / 'tests/files/PrimarySchool-2.xml' -OFFICE_3_FILE_PATH = Path(__file__).parents[1] / 'tests/files/Office-3.xml' +PRIMARYSCHOOL_1_FILE_PATH = Path(__file__).parents[1] / "tests/files/PrimarySchool-1.xml" +PRIMARYSCHOOL_2_FILE_PATH = Path(__file__).parents[1] / "tests/files/PrimarySchool-2.xml" +OFFICE_3_FILE_PATH = Path(__file__).parents[1] / "tests/files/Office-3.xml" input_paths_list = [PRIMARYSCHOOL_1_FILE_PATH, PRIMARYSCHOOL_2_FILE_PATH, OFFICE_3_FILE_PATH] # test output file location -output_path = Path(__file__).parents[1] / 'tests/output/cts_output.xlsx' +output_path = Path(__file__).parents[1] / "tests/output/cts_output.xlsx" # run the CTS spreadsheet maker building_sync_to_cts(input_paths_list, output_path) diff --git a/buildingsync_asset_extractor/formatters.py b/buildingsync_asset_extractor/formatters.py index 21e97ff..163ee32 100644 --- a/buildingsync_asset_extractor/formatters.py +++ b/buildingsync_asset_extractor/formatters.py @@ -7,7 +7,7 @@ from buildingsync_asset_extractor.lighting_processing.lighting_processing import ( LightingData, LightingDataLPD, - LightingDataPower + LightingDataPower, ) # Gets or creates a logger @@ -18,9 +18,9 @@ logger.setLevel(logging.DEBUG) -class Formatter(): - """These functions take in processed data, calucates the asset and it's unit, then exports it. - """ +class Formatter: + """These functions take in processed data, calculates the asset and its unit, then exports it.""" + def __init__(self, export_asset: Callable[[str, Any], None], export_asset_units: Callable[[str, Any], None]): self.export_asset = export_asset self.export_asset_units = export_asset_units @@ -28,7 +28,7 @@ def __init__(self, export_asset: Callable[[str, Any], None], export_asset_units: def format_age_results(self, name: str, results: list[SystemData], process_type: str, units: Optional[str]) -> None: # process results value = None - if process_type.endswith('oldest'): + if process_type.endswith("oldest"): res_vals = [sub.value for sub in results] s_res = sorted(res_vals) if s_res: @@ -37,7 +37,7 @@ def format_age_results(self, name: str, results: list[SystemData], process_type: self.export_asset(name, str(value)) self.export_asset_units(name, units) - elif process_type.endswith('newest'): + elif process_type.endswith("newest"): res_vals = [sub.value for sub in results] s_res = sorted(res_vals, reverse=True) if s_res: @@ -45,14 +45,14 @@ def format_age_results(self, name: str, results: list[SystemData], process_type: self.export_asset(name, str(value)) self.export_asset_units(name, units) - elif process_type.endswith('average'): + elif process_type.endswith("average"): self.format_custom_avg_results(name, results, units) def format_80_percent_results(self, name: str, results: list[SystemData], units: Optional[str]) -> None: - """ format 80% rule results - the "primary" type returned must at least serve 80% of the area by - 1. Capacity - 2. Served space area + """format 80% rule results + the "primary" type returned must at least serve 80% of the area by + 1. Capacity + 2. Served space area """ if len(results) == 0: # export None @@ -74,16 +74,16 @@ def format_80_percent_results(self, name: str, results: list[SystemData], units: # add all capacities # pick largest one and make sure it's 80% of total found = 0 - total = sum(capacities) # type: ignore + total = sum(capacities) # type: ignore[arg-type] if total > 0: primaries = {} for res in results: if res.value not in primaries: primaries[res.value] = 0.0 - primaries[res.value] += float(res.cap) # type: ignore + primaries[res.value] += float(res.cap) # type: ignore[arg-type] for p in primaries: - if float(primaries[p])/total >= 0.8: + if float(primaries[p]) / total >= 0.8: # this fuel meets the 80% threshold by capacity found = 1 self.export_asset(name, p) @@ -91,14 +91,14 @@ def format_80_percent_results(self, name: str, results: list[SystemData], units: return if found == 0: - # nothing matched this criteria, return 'Mixed' - self.export_asset(name, 'mixed') + # nothing matched these criteria, return 'Mixed' + self.export_asset(name, "mixed") self.export_asset_units(name, units) return if None not in sqfts: # sqft method - total = sum(sqfts) # type: ignore + total = sum(sqfts) # type: ignore[arg-type] found = 0 if total > 0: primaries = {} @@ -109,7 +109,7 @@ def format_80_percent_results(self, name: str, results: list[SystemData], units: primaries[res.value] += res.sqft for p in primaries: - if float(primaries[p])/total >= 0.8: + if float(primaries[p]) / total >= 0.8: # this fuel meets the 80% threshold by capacity found = 1 self.export_asset(name, p) @@ -117,21 +117,20 @@ def format_80_percent_results(self, name: str, results: list[SystemData], units: return if found == 0: - # nothing matched this criteria, return 'Mixed' - self.export_asset(name, 'mixed') + # nothing matched these criteria, return 'Mixed' + self.export_asset(name, "mixed") self.export_asset_units(name, units) return # still here? return unknown - self.export_asset(name, 'unknown') + self.export_asset(name, "unknown") self.export_asset_units(name, units) - return def format_lighting_results(self, name: str, results: list[LightingData], units: Optional[str]) -> None: - """ custom processing for lighting efficiency - 1. if 'lpd' is present, average the values - 2. else if percentpremisesserved - 3. otherwise regular sqft + """custom processing for lighting efficiency + 1. if 'lpd' is present, average the values + 2. else if percentpremisesserved + 3. otherwise regular sqft """ if len(results) == 0: # export None, no units @@ -140,14 +139,14 @@ def format_lighting_results(self, name: str, results: list[LightingData], units: return # check method 1 - has_lpd = all([isinstance(r, LightingDataLPD) for r in results]) + has_lpd = all(isinstance(r, LightingDataLPD) for r in results) # for weighted average, re-find Watts from LPD and LinkedPremises and divide by total sqft if has_lpd: value = 0.0 total_sqft = 0.0 for r in results: - value += r.lpd * r.sqft # type: ignore # TODO: remove. + value += r.lpd * r.sqft # type: ignore[attr-defined] total_sqft += r.sqft if value > 0: value = value / total_sqft @@ -159,14 +158,14 @@ def format_lighting_results(self, name: str, results: list[LightingData], units: # check method 2 # need both PercentPremises AND LinkedPremises for this # running sum of all watts / running sum of all fractions of sqft - has_perc = all([r.sqft_percent is not None for r in results]) - has_power = all([isinstance(r, LightingDataPower) for r in results]) + has_perc = all(r.sqft_percent is not None for r in results) + has_power = all(isinstance(r, LightingDataPower) for r in results) if has_perc and has_power: power = 0 sqft_total = 0.0 for r in results: - power += r.power # type: ignore - sqft_total = r.sqft_percent / 100 * r.sqft # type: ignore + power += r.power # type: ignore[attr-defined] + sqft_total = r.sqft_percent / 100 * r.sqft # type: ignore[operator] if power > 0: value = power / sqft_total self.export_asset(name, value) @@ -177,7 +176,7 @@ def format_lighting_results(self, name: str, results: list[LightingData], units: sqfts = [sub.sqft if sub.sqft is None else float(sub.sqft) for sub in results] if None not in sqfts and has_power: # sqft methods - remapped_power = [sub.power for sub in results] # type: ignore + remapped_power = [sub.power for sub in results] # type: ignore[attr-defined] remapped_sqft = [sub.sqft for sub in results] top = sum(remapped_power) bottom = sum(remapped_sqft) @@ -188,17 +187,17 @@ def format_lighting_results(self, name: str, results: list[LightingData], units: return # can't calculate - self.export_asset(name, 'unknown') + self.export_asset(name, "unknown") self.export_asset_units(name, units) return def format_custom_avg_results(self, name: str, results: list[SystemData], units: Optional[str]) -> None: - """ format weighted average - 1. Ensure all units are the same - 2. Attempt to calculate with installed power (NOT IMPLEMENTED) - 3. Attempt to calculate with capacity (cap) - 4. Attempt to calculate with served space area (sqrt) - Don't export units for 'average age' (review this in the future) + """format weighted average + 1. Ensure all units are the same + 2. Attempt to calculate with installed power (NOT IMPLEMENTED) + 3. Attempt to calculate with capacity (cap) + 4. Attempt to calculate with served space area (sqrt) + Don't export units for 'average age' (review this in the future) """ if len(results) == 0: @@ -208,8 +207,8 @@ def format_custom_avg_results(self, name: str, results: list[SystemData], units: return # 1. units - if units == 'mixed': - self.export_asset(name, 'mixed') + if units == "mixed": + self.export_asset(name, "mixed") self.export_asset_units(name, units) return @@ -228,12 +227,12 @@ def format_custom_avg_results(self, name: str, results: list[SystemData], units: cap_total = 0.0 eff_total = 0.0 for res in results: - cap_total = cap_total + float(res.cap) # type: ignore - eff_total = eff_total + (float(res.value) * float(res.cap)) # type: ignore + cap_total = cap_total + float(res.cap) # type: ignore[arg-type] + eff_total = eff_total + (float(res.value) * float(res.cap)) # type: ignore[arg-type] total: Union[float, str] = eff_total / cap_total # special case for average age: take the floor since partial year doesn't make sense - if name.lower().endswith('age'): + if name.lower().endswith("age"): total = str(int(total)) self.export_asset(name, total) @@ -243,20 +242,20 @@ def format_custom_avg_results(self, name: str, results: list[SystemData], units: elif None not in sqfts: # sqft methods remapped_res = {sub.value: sub.sqft for sub in results} - self.format_avg_sqft_results(name, remapped_res, units) # type: ignore + self.format_avg_sqft_results(name, remapped_res, units) # type: ignore[arg-type] return else: # just average - total = sum(values)/len(values) # type: ignore + total = sum(values) / len(values) # type: ignore[arg-type] # special case for average age: take the floor since partial year doesn't make sense - if name.lower().endswith('age'): + if name.lower().endswith("age"): total = int(total) self.export_asset(name, total) self.export_asset_units(name, units) return def format_sqft_results(self, name: str, results: dict[str, float], units: Optional[str]) -> None: - """ return primary and secondary for top 2 results by sqft """ + """return primary and secondary for top 2 results by sqft""" # NOTE: this is the only method that modifies the export name ' # by appending 'primary' and 'secondary' # no units associated with this now @@ -264,7 +263,7 @@ def format_sqft_results(self, name: str, results: dict[str, float], units: Optio # filter and sort results filtered_res = {k: v for k, v in results.items() if v != 0} s_res = dict(sorted(filtered_res.items(), key=lambda kv: kv[1], reverse=True)) - logger.debug('sorted results with zeros removed: {}'.format(s_res)) + logger.debug(f"sorted results with zeros removed: {s_res}") value = None value2 = None @@ -272,16 +271,16 @@ def format_sqft_results(self, name: str, results: dict[str, float], units: Optio s_keys = list(s_res.keys()) if s_keys: value = s_keys[0] - self.export_asset('Primary ' + name, value) - self.export_asset_units('Primary ' + name, units) + self.export_asset("Primary " + name, value) + self.export_asset_units("Primary " + name, units) - if (len(s_keys) > 1): + if len(s_keys) > 1: value2 = s_keys[1] - self.export_asset('Secondary ' + name, value2) - self.export_asset_units('Secondary ' + name, units) + self.export_asset("Secondary " + name, value2) + self.export_asset_units("Secondary " + name, units) def format_avg_sqft_results(self, name: str, results: dict[Any, float], units: Optional[str]) -> None: - """ weighted average of results """ + """weighted average of results""" # in this case the result keys will convert to numbers # to calculate the weighted average @@ -298,28 +297,24 @@ def format_avg_sqft_results(self, name: str, results: dict[Any, float], units: O total = running_sum / total_sqft # special case for average age: take the floor since partial year doesn't make sense - if name.lower().endswith('age') and total is not None: + if name.lower().endswith("age") and total is not None: total = str(int(total)) # add to assets self.export_asset(name, total) self.export_asset_units(name, units) - def format_electrification_pontential(self, name: str, results: list[SystemData], units: Optional[str]) -> None: - """Sum non electric capacites""" + def format_electrification_potential(self, name: str, results: list[SystemData], units: Optional[str]) -> None: + """Sum non electric capacities""" # If no SystemDatas, then None if len(results) == 0: self.export_asset(name, None) self.export_asset_units(name, units) return - non_electric = [ - sd for sd in results - if sd.value not in electric_fuel_types - and sd.cap is not None - ] + non_electric = [sd for sd in results if sd.value not in electric_fuel_types and sd.cap is not None] - # if no non electric SystemDatas, then 0 + # if no non-electric SystemDatas, then 0 if len(non_electric) == 0: self.export_asset(name, 0) self.export_asset_units(name, units) @@ -338,13 +333,15 @@ def format_electrification_pontential(self, name: str, results: list[SystemData] return # else unknown - self.export_asset(name, 'unknown') + self.export_asset(name, "unknown") self.export_asset_units(name, None) return - def remap_results(self, results: list[SystemData]) -> \ - Tuple[list[Optional[float]], list[Optional[float]], list[Optional[str]], list[Optional[float]]]: - """ Remap results from a list of dictionaries to 4 lists """ + def remap_results( + self, + results: list[SystemData], + ) -> Tuple[list[Optional[float]], list[Optional[float]], list[Optional[str]], list[Optional[float]]]: + """Remap results from a list of dictionaries to 4 lists""" try: values = [sub.value if sub.value is None else float(sub.value) for sub in results] except ValueError: diff --git a/buildingsync_asset_extractor/lighting_processing/building_occ_class_to_building_type.py b/buildingsync_asset_extractor/lighting_processing/building_occ_class_to_building_type.py index 1e3bf63..5e1fa11 100644 --- a/buildingsync_asset_extractor/lighting_processing/building_occ_class_to_building_type.py +++ b/buildingsync_asset_extractor/lighting_processing/building_occ_class_to_building_type.py @@ -1,68 +1,68 @@ building_occ_class_to_building_type = { - 'Assembly-Convention center': 'Convention center', - 'Assembly-Cultural entertainment': 'Performing arts theater', - 'Assembly-Indoor arena': 'Sports Arena', - 'Assembly-Race track': 'Sports Arena', - 'Assembly-Religious': 'Religious building', - 'Assembly-Social entertainment': 'Performing arts theater', - 'Assembly-Stadium': 'Gymnasium', - 'Assembly-Stadium (closed)': 'Gymnasium', - 'Assembly-Stadium (open)': 'Gymnasium', - 'Bar': 'Dining: Bar Lounge/Leisure', - 'Bar-Nightclub': 'Dining: Bar Lounge/Leisure', - 'Convenience store': 'Retail', - 'Courthouse': 'Court house', - 'Education': 'School/University', - 'Education-Adult': 'School/University', - 'Education-Higher': 'School/University', - 'Education-Preschool or daycare': 'School/University', - 'Education-Primary': 'School/University', - 'Education-Secondary': 'School/University', - 'Education-Vocational': 'School/University', - 'Food service': 'Dining: Cafeteria/Fast Food', - 'Food service-Fast': 'Dining: Cafeteria/Fast Food', - 'Food service-Full': 'Dining: Family', - 'Food service-Institutional': 'Dining: Cafeteria/Fast Food', - 'Food service-Limited': 'Dining: Cafeteria/Fast Food', - 'Health care': 'Hospital', - 'Industrial': 'Manufacturing Facility', - 'Industrial manufacturing plant': 'Manufacturing facility', - 'Lodging': 'Hotel', - 'Lodging with extended amenities': 'Hotel', - 'Lodging with limited amenities': 'Motel', - 'Lodging-Barracks': 'Domitory', - 'Lodging-Institutional': 'Domitory', - 'Mixed-use commercial': 'Office', - 'Multifamily': 'Multi-Family', - 'Multifamily with commercial': 'Multi-Family', - 'Office': 'Office', - 'Parking': 'Parking Garage', - 'Public safety': 'Police station', - 'Public safety station': 'Police station', - 'Public safety station-Fire': 'Fire Station', - 'Public safety station-Police': 'Police station', - 'Public safety-Correctional facility': 'Penitentiary', - 'Public safety-Detention center': 'Penitentiary', - 'Recreation': 'Exercise Center', - 'Recreation-Bowling alley': 'Exercise Center', - 'Recreation-Fitness center': 'Exercise Center', - 'Recreation-Ice rink': 'Exercise Center', - 'Recreation-Indoor sport': 'Exercise Center', - 'Recreation-Pool': 'Exercise Center', - 'Recreation-Roller rink': 'Exercise Center', - 'Retail': 'Retail', - 'Retail-Automobile dealership': 'Automotive Facility', - 'Retail-Dry goods retail': 'Retail', - 'Retail-Enclosed mall': 'Retail', - 'Retail-Hypermarket': 'Retail', - 'Retail-Mall': 'Retail', - 'Retail-Strip mall': 'Retail', - 'Service-Postal': 'Post Office', - 'Service-Production and assembly': 'Manufacturing Facility', - 'Service-Repair': 'Manufacturing Facility', - 'Transportation terminal': 'Transportation', - 'Warehouse': 'Warehouse', - 'Warehouse-Refrigerated': 'Warehouse', - 'Warehouse-Self-storage': 'Warehouse', - 'Warehouse-Unrefrigerated': 'Warehouse' - } + "Assembly-Convention center": "Convention center", + "Assembly-Cultural entertainment": "Performing arts theater", + "Assembly-Indoor arena": "Sports Arena", + "Assembly-Race track": "Sports Arena", + "Assembly-Religious": "Religious building", + "Assembly-Social entertainment": "Performing arts theater", + "Assembly-Stadium": "Gymnasium", + "Assembly-Stadium (closed)": "Gymnasium", + "Assembly-Stadium (open)": "Gymnasium", + "Bar": "Dining: Bar Lounge/Leisure", + "Bar-Nightclub": "Dining: Bar Lounge/Leisure", + "Convenience store": "Retail", + "Courthouse": "Court house", + "Education": "School/University", + "Education-Adult": "School/University", + "Education-Higher": "School/University", + "Education-Preschool or daycare": "School/University", + "Education-Primary": "School/University", + "Education-Secondary": "School/University", + "Education-Vocational": "School/University", + "Food service": "Dining: Cafeteria/Fast Food", + "Food service-Fast": "Dining: Cafeteria/Fast Food", + "Food service-Full": "Dining: Family", + "Food service-Institutional": "Dining: Cafeteria/Fast Food", + "Food service-Limited": "Dining: Cafeteria/Fast Food", + "Health care": "Hospital", + "Industrial": "Manufacturing Facility", + "Industrial manufacturing plant": "Manufacturing facility", + "Lodging": "Hotel", + "Lodging with extended amenities": "Hotel", + "Lodging with limited amenities": "Motel", + "Lodging-Barracks": "Domitory", + "Lodging-Institutional": "Domitory", + "Mixed-use commercial": "Office", + "Multifamily": "Multi-Family", + "Multifamily with commercial": "Multi-Family", + "Office": "Office", + "Parking": "Parking Garage", + "Public safety": "Police station", + "Public safety station": "Police station", + "Public safety station-Fire": "Fire Station", + "Public safety station-Police": "Police station", + "Public safety-Correctional facility": "Penitentiary", + "Public safety-Detention center": "Penitentiary", + "Recreation": "Exercise Center", + "Recreation-Bowling alley": "Exercise Center", + "Recreation-Fitness center": "Exercise Center", + "Recreation-Ice rink": "Exercise Center", + "Recreation-Indoor sport": "Exercise Center", + "Recreation-Pool": "Exercise Center", + "Recreation-Roller rink": "Exercise Center", + "Retail": "Retail", + "Retail-Automobile dealership": "Automotive Facility", + "Retail-Dry goods retail": "Retail", + "Retail-Enclosed mall": "Retail", + "Retail-Hypermarket": "Retail", + "Retail-Mall": "Retail", + "Retail-Strip mall": "Retail", + "Service-Postal": "Post Office", + "Service-Production and assembly": "Manufacturing Facility", + "Service-Repair": "Manufacturing Facility", + "Transportation terminal": "Transportation", + "Warehouse": "Warehouse", + "Warehouse-Refrigerated": "Warehouse", + "Warehouse-Self-storage": "Warehouse", + "Warehouse-Unrefrigerated": "Warehouse", +} diff --git a/buildingsync_asset_extractor/lighting_processing/building_space_type_to_lpd.py b/buildingsync_asset_extractor/lighting_processing/building_space_type_to_lpd.py index 0d56f2c..fb50a57 100644 --- a/buildingsync_asset_extractor/lighting_processing/building_space_type_to_lpd.py +++ b/buildingsync_asset_extractor/lighting_processing/building_space_type_to_lpd.py @@ -10,115 +10,559 @@ class BuildingSpaceTypeLPD: building_space_type_to_lpd = [ - BuildingSpaceTypeLPD(building_type="Other", section_type="Atrium", lpd_by_year={1999: 1.3, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: None, 2013: None, 2019: 0.49}), - BuildingSpaceTypeLPD(building_type="Auditorium", section_type="Audience Seating Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.79, 2013: 0.63, 2019: 0.61}), - BuildingSpaceTypeLPD(building_type="Convention center", section_type="Audience Seating Area", lpd_by_year={1999: 0.5, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.82, 2013: 0.82, 2019: None}), - BuildingSpaceTypeLPD(building_type="Exercise Center", section_type="Audience Seating Area", lpd_by_year={1999: 0.5, 2001: 0.3, 2004: 0.3, 2007: 0.3, 2010: None, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Gymnasium", section_type="Audience Seating Area", lpd_by_year={1999: 0.5, 2001: 0.4, 2004: 0.4, 2007: 0.4, 2010: 0.43, 2013: 0.65, 2019: 0.23}), - BuildingSpaceTypeLPD(building_type="Motion picture theater", section_type="Audience Seating Area", lpd_by_year={1999: 1.3, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.14, 2013: 1.14, 2019: 0.27}), - BuildingSpaceTypeLPD(building_type="Penitentiary", section_type="Audience Seating Area", lpd_by_year={1999: 1.9, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.43, 2013: 0.28, 2019: 0.67}), - BuildingSpaceTypeLPD(building_type="Performing arts theater", section_type="Audience Seating Area", lpd_by_year={1999: 1.8, 2001: 2.6, 2004: 2.6, 2007: 2.6, 2010: 2.43, 2013: 2.43, 2019: 1.16}), - BuildingSpaceTypeLPD(building_type="Religious building", section_type="Audience Seating Area", lpd_by_year={1999: 3.2, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.53, 2013: 1.53, 2019: 0.72}), - BuildingSpaceTypeLPD(building_type="Sports arena", section_type="Audience Seating Area", lpd_by_year={1999: 0.5, 2001: 0.4, 2004: 0.4, 2007: 0.4, 2010: 0.43, 2013: 0.43, 2019: 0.33}), - BuildingSpaceTypeLPD(building_type="Transportation", section_type="Audience Seating Area", lpd_by_year={1999: None, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: 0.54, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Audience Seating Area", lpd_by_year={1999: 1.6, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: None, 2013: 0.43, 2019: 0.23}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Banking Activity Area", lpd_by_year={1999: 2.4, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.38, 2013: 1.01, 2019: 0.61}), - BuildingSpaceTypeLPD(building_type="Penitentiary", section_type="Classroom/Lecture Hall / Training Room", lpd_by_year={1999: 1.4, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.34, 2013: 1.34, 2019: 0.89}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Classroom/Lecture Hall / Training Room", lpd_by_year={1999: 1.6, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.24, 2013: 1.24, 2019: 0.71}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Conference/Meeting / Multipurpose Room", lpd_by_year={1999: 1.5, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.23, 2013: 1.23, 2019: 0.97}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Confinement Cells", lpd_by_year={1999: 1.1, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 1.1, 2013: 0.81, 2019: 0.7}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Copy/Print Room", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.72, 2019: 0.31}), - BuildingSpaceTypeLPD(building_type="Facility for the visually impaired", section_type="Corridor", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.66, 2013: 0.92, 2019: 0.71}), - BuildingSpaceTypeLPD(building_type="Hospital", section_type="Corridor", lpd_by_year={1999: 1.6, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.89, 2013: 0.99, 2019: 0.71}), - BuildingSpaceTypeLPD(building_type="Manufacturing facility", section_type="Corridor", lpd_by_year={1999: 0.5, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: 0.41, 2013: 0.41, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Corridor", lpd_by_year={1999: 0.7, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: 0.66, 2013: 0.66, 2019: 0.41}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Courtroom", lpd_by_year={1999: 2.1, 2001: 1.9, 2004: 1.9, 2007: 1.9, 2010: 1.72, 2013: 1.72, 2019: 1.2}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Judges' Chambers", lpd_by_year={1999: 1.1, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.17, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Computer Room", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.71, 2019: 0.94}), - BuildingSpaceTypeLPD(building_type="Hotel", section_type="Dining Area", lpd_by_year={1999: 1.0, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 0.82, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Motel", section_type="Dining Area", lpd_by_year={1999: 1.2, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.88, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Penitentiary", section_type="Dining Area", lpd_by_year={1999: 1.4, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.07, 2013: 0.96, 2019: 0.42}), - BuildingSpaceTypeLPD(building_type="Facility for the visually impaired", section_type="Dining Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 2.65, 2019: 1.27}), - BuildingSpaceTypeLPD(building_type="Bar/lounge or leisure dining", section_type="Dining Area", lpd_by_year={1999: 1.2, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.31, 2013: 1.07, 2019: 0.86}), - BuildingSpaceTypeLPD(building_type="Cafeteria or fast food dining", section_type="Dining Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.65, 2019: 0.4}), - BuildingSpaceTypeLPD(building_type="Family dining", section_type="Dining Area", lpd_by_year={1999: 2.2, 2001: 2.1, 2004: 2.1, 2007: 2.1, 2010: 0.89, 2013: 0.89, 2019: 0.6}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Dining Area", lpd_by_year={1999: 1.4, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.65, 2013: 0.65, 2019: 0.43}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Electrical/Mechanical Room", lpd_by_year={1999: 1.3, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 0.95, 2013: 0.42, 2019: 0.43}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Emergency Vehicle Garage", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.56, 2019: 0.52}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Food Preparation Area", lpd_by_year={1999: 2.2, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.99, 2013: 1.21, 2019: 1.09}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Guest Room", lpd_by_year={1999: 2.5, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.11, 2013: 0.91, 2019: 0.41}), - BuildingSpaceTypeLPD(building_type="Classroom (school)", section_type="Laboratory ", lpd_by_year={1999: None, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.28, 2013: 1.43, 2019: 1.11}), - BuildingSpaceTypeLPD(building_type="Police station", section_type="Laboratory ", lpd_by_year={1999: 1.8, 2001: None, 2004: None, 2007: None, 2010: None, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other ", section_type="Laboratory", lpd_by_year={1999: 1.8, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.81, 2013: 1.81, 2019: 1.33}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Laundry/Washing Area", lpd_by_year={1999: 0.7, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.6, 2013: 0.6, 2019: 0.53}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Loading Dock, Interior", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.47, 2019: 0.88}), - BuildingSpaceTypeLPD(building_type="Facility for the visually impaired", section_type="Lobby", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.8, 2019: 1.69}), - BuildingSpaceTypeLPD(building_type="Elevator", section_type="Lobby", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.64, 2013: 0.64, 2019: 0.65}), - BuildingSpaceTypeLPD(building_type="Hotel", section_type="Lobby", lpd_by_year={1999: 1.7, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.06, 2013: 1.06, 2019: 0.51}), - BuildingSpaceTypeLPD(building_type="Motion picture theater", section_type="Lobby", lpd_by_year={1999: 0.8, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.52, 2013: 0.59, 2019: 0.23}), - BuildingSpaceTypeLPD(building_type="Performing arts theater", section_type="Lobby", lpd_by_year={1999: 1.2, 2001: 3.3, 2004: 3.3, 2007: 3.3, 2010: 2.0, 2013: 2.0, 2019: 1.25}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Lobby", lpd_by_year={1999: 1.8, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 0.9, 2013: 0.9, 2019: 0.84}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Locker Room", lpd_by_year={1999: None, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.75, 2013: 0.75, 2019: 0.52}), - BuildingSpaceTypeLPD(building_type="Hospital / Healthcare facility", section_type="Lounge / Breakroom", lpd_by_year={1999: 1.4, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 1.07, 2013: 0.92, 2019: 0.42}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Lounge / Breakroom", lpd_by_year={1999: 1.4, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.73, 2013: 0.73, 2019: 0.59}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Office", lpd_by_year={1999: 1.43, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.07, 2013: 1.07, 2019: 0.67}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Parking Area", lpd_by_year={1999: 0.2, 2001: 0.2, 2004: 0.2, 2007: 0.2, 2010: 0.19, 2013: 0.19, 2019: 0.15}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Pharmacy Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.68, 2019: 1.66}), - BuildingSpaceTypeLPD(building_type="Facility for the visually impaired", section_type="Restroom", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.21, 2019: 1.26}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Restroom", lpd_by_year={1999: 1.0, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: None, 2013: 0.98, 2019: 0.63}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Sales Area", lpd_by_year={1999: 2.1, 2001: 2.1, 2004: 1.7, 2007: 1.7, 2010: 1.68, 2013: 1.44, 2019: 1.05}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Seating Area, General", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.54, 2019: 0.23}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Stairway", lpd_by_year={1999: 0.9, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.69, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Stairwell", lpd_by_year={1999: 0.9, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: None, 2013: 0.69, 2019: 0.49}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Storage Room", lpd_by_year={1999: 0.7, 2001: 0.55, 2004: 0.55, 2007: 0.55, 2010: 0.63, 2013: 0.83, 2019: 0.42}), - BuildingSpaceTypeLPD(building_type="Hospital", section_type="Storage Room", lpd_by_year={1999: 2.9, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: None, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Museum", section_type="Storage Room", lpd_by_year={1999: 1.4, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: None, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Vehicular Maintenance Area / Automotive", lpd_by_year={1999: 1.4, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.67, 2013: 0.67, 2019: 0.6}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Workshop", lpd_by_year={1999: 2.5, 2001: 1.9, 2004: 1.9, 2007: 1.9, 2010: 1.59, 2013: 1.59, 2019: 1.26}), - BuildingSpaceTypeLPD(building_type="Facility for the Visually Impaired", section_type="Chapel", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 2.21, 2019: 0.7}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Recreation room/common livingroom", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 2.41, 2019: 1.77}), - BuildingSpaceTypeLPD(building_type="Convention Center", section_type="Exhibit Space", lpd_by_year={1999: 3.3, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.45, 2013: 1.45, 2019: 0.61}), - BuildingSpaceTypeLPD(building_type="Dormitory", section_type="Living Quarters", lpd_by_year={1999: 1.9, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.38, 2013: 0.38, 2019: 0.5}), - BuildingSpaceTypeLPD(building_type="Fire Station", section_type="Engine Room", lpd_by_year={1999: 0.9, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 0.56, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Sleeping Quarters", lpd_by_year={1999: 1.1, 2001: 0.3, 2004: 0.3, 2007: 0.3, 2010: 0.25, 2013: 0.22, 2019: 0.23}), - BuildingSpaceTypeLPD(building_type="Gymnasium / Fitness Center", section_type="Exercise area", lpd_by_year={1999: 1.1, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.72, 2013: 0.72, 2019: 0.9}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Playing area", lpd_by_year={1999: 1.9, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.2, 2013: 1.2, 2019: 0.85}), - BuildingSpaceTypeLPD(building_type="Hospital / Healthcare Facility", section_type="Emergency", lpd_by_year={1999: 2.8, 2001: 2.7, 2004: 2.7, 2007: 2.7, 2010: 2.26, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Exam/treatment", lpd_by_year={1999: 1.6, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.66, 2013: 1.66, 2019: 1.4}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Imaging / radiology", lpd_by_year={1999: 0.4, 2001: 0.4, 2004: 0.4, 2007: 0.4, 2010: 1.32, 2013: 1.51, 2019: 0.94}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Medical supply", lpd_by_year={1999: 3.0, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.27, 2013: 0.74, 2019: 0.62}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Nursery", lpd_by_year={1999: 1.0, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.88, 2013: 0.88, 2019: 0.92}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Nurse’s station", lpd_by_year={1999: 1.8, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.87, 2013: 0.71, 2019: 1.17}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Operating room", lpd_by_year={1999: 7.6, 2001: 2.2, 2004: 2.2, 2007: 2.2, 2010: 1.89, 2013: 2.48, 2019: 2.26}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Patient room", lpd_by_year={1999: 1.2, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.62, 2013: 0.62, 2019: 0.68}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Pharmacy", lpd_by_year={1999: 2.3, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.14, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Physical therapy", lpd_by_year={1999: 1.9, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.91, 2013: 0.91, 2019: 0.91}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Recovery", lpd_by_year={1999: 2.6, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 1.15, 2013: 1.15, 2019: 1.25}), - BuildingSpaceTypeLPD(building_type="Library", section_type="Card File and Cataloging", lpd_by_year={1999: 1.4, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.72, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Reading area", lpd_by_year={1999: 1.8, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.93, 2013: 1.06, 2019: 0.96}), - BuildingSpaceTypeLPD(building_type="Other", section_type="The stacks", lpd_by_year={1999: 1.9, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.71, 2013: 1.71, 2019: 1.18}), - BuildingSpaceTypeLPD(building_type="Manufacturing Facility", section_type="Detailed manufacturing area", lpd_by_year={1999: 6.2, 2001: 2.1, 2004: 2.1, 2007: 2.1, 2010: 1.29, 2013: 1.29, 2019: 0.8}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Equipment room", lpd_by_year={1999: 0.8, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.95, 2013: 0.74, 2019: 0.76}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Extra high bay area (>50 ft height)", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 1.05, 2013: 1.05, 2019: 1.42}), - BuildingSpaceTypeLPD(building_type="Other", section_type="High bay area (25-50 ft height)", lpd_by_year={1999: 3.0, 2001: 1.7, 2004: None, 2007: 1.7, 2010: 1.23, 2013: 1.23, 2019: 1.24}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Low bay area (<25 ft height)", lpd_by_year={1999: 2.1, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.19, 2013: 1.19, 2019: 0.86}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Control room", lpd_by_year={1999: 0.5, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: None, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Museum", section_type="General exhibition area", lpd_by_year={1999: 1.6, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 1.05, 2013: 1.05, 2019: 0.31}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Restoration room", lpd_by_year={1999: 2.5, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.02, 2013: 1.02, 2019: 1.1}), - BuildingSpaceTypeLPD(building_type="Performing Arts Theater", section_type="Dressing Room", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.4, 2013: 0.61, 2019: 0.41}), - BuildingSpaceTypeLPD(building_type="Post Office", section_type="Sorting Area", lpd_by_year={1999: 1.7, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.94, 2013: 0.94, 2019: 0.76}), - BuildingSpaceTypeLPD(building_type="Religious Buildings", section_type="Fellowship hall", lpd_by_year={1999: 2.3, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.64, 2013: 0.64, 2019: 0.54}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Worship/pulpit/choir area", lpd_by_year={1999: 5.2, 2001: 2.4, 2004: 2.4, 2007: 2.4, 2010: 1.53, 2013: 1.53, 2019: 0.85}), - BuildingSpaceTypeLPD(building_type="Retail Facilities", section_type="Dressing/fitting room", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.87, 2013: 0.71, 2019: 0.51}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Mall concourse", lpd_by_year={1999: 1.8, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.1, 2013: 1.1, 2019: 0.82}), - BuildingSpaceTypeLPD(building_type="Sports Arena Class I facility", section_type="Playing Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 3.01, 2013: 3.68, 2019: 2.94}), - BuildingSpaceTypeLPD(building_type="Sports Arena Class II facility", section_type="Playing Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 1.92, 2013: 2.4, 2019: 2.01}), - BuildingSpaceTypeLPD(building_type="Sports Arena Class III facility", section_type="Playing Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 1.2, 2013: 1.8, 2019: 1.3}), - BuildingSpaceTypeLPD(building_type="Sports Arena Class IV facility", section_type="Playing Area", lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.72, 2013: 1.2, 2019: 0.86}), - BuildingSpaceTypeLPD(building_type="Ring Sports Arena", section_type="Playing Area", lpd_by_year={1999: 3.8, 2001: 2.7, 2004: 2.7, 2007: 2.7, 2010: 2.68, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Court Sports Area", section_type="Playing Area", lpd_by_year={1999: 4.3, 2001: 2.3, 2004: 2.3, 2007: 2.3, 2010: None, 2013: None, 2019: None}), - BuildingSpaceTypeLPD(building_type="Transportation Facility", section_type="Baggage / carousel area", lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.76, 2013: 0.53, 2019: 0.39}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Airport concourse", lpd_by_year={1999: 0.7, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.36, 2013: 0.36, 2019: 0.25}), - BuildingSpaceTypeLPD(building_type="Other", section_type="Ticket counter", lpd_by_year={1999: 1.8, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.08, 2013: 0.8, 2019: 0.51}), - BuildingSpaceTypeLPD(building_type="Warehouse", section_type="Storage Area", lpd_by_year={1999: 1.35, 2001: 1.15, 2004: 1.15, 2007: 1.15, 2010: 0.76, 2013: 0.76, 2019: 0.51}), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Atrium", + lpd_by_year={1999: 1.3, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: None, 2013: None, 2019: 0.49}, + ), + BuildingSpaceTypeLPD( + building_type="Auditorium", + section_type="Audience Seating Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.79, 2013: 0.63, 2019: 0.61}, + ), + BuildingSpaceTypeLPD( + building_type="Convention center", + section_type="Audience Seating Area", + lpd_by_year={1999: 0.5, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.82, 2013: 0.82, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Exercise Center", + section_type="Audience Seating Area", + lpd_by_year={1999: 0.5, 2001: 0.3, 2004: 0.3, 2007: 0.3, 2010: None, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Gymnasium", + section_type="Audience Seating Area", + lpd_by_year={1999: 0.5, 2001: 0.4, 2004: 0.4, 2007: 0.4, 2010: 0.43, 2013: 0.65, 2019: 0.23}, + ), + BuildingSpaceTypeLPD( + building_type="Motion picture theater", + section_type="Audience Seating Area", + lpd_by_year={1999: 1.3, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.14, 2013: 1.14, 2019: 0.27}, + ), + BuildingSpaceTypeLPD( + building_type="Penitentiary", + section_type="Audience Seating Area", + lpd_by_year={1999: 1.9, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.43, 2013: 0.28, 2019: 0.67}, + ), + BuildingSpaceTypeLPD( + building_type="Performing arts theater", + section_type="Audience Seating Area", + lpd_by_year={1999: 1.8, 2001: 2.6, 2004: 2.6, 2007: 2.6, 2010: 2.43, 2013: 2.43, 2019: 1.16}, + ), + BuildingSpaceTypeLPD( + building_type="Religious building", + section_type="Audience Seating Area", + lpd_by_year={1999: 3.2, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.53, 2013: 1.53, 2019: 0.72}, + ), + BuildingSpaceTypeLPD( + building_type="Sports arena", + section_type="Audience Seating Area", + lpd_by_year={1999: 0.5, 2001: 0.4, 2004: 0.4, 2007: 0.4, 2010: 0.43, 2013: 0.43, 2019: 0.33}, + ), + BuildingSpaceTypeLPD( + building_type="Transportation", + section_type="Audience Seating Area", + lpd_by_year={1999: None, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: 0.54, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Audience Seating Area", + lpd_by_year={1999: 1.6, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: None, 2013: 0.43, 2019: 0.23}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Banking Activity Area", + lpd_by_year={1999: 2.4, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.38, 2013: 1.01, 2019: 0.61}, + ), + BuildingSpaceTypeLPD( + building_type="Penitentiary", + section_type="Classroom/Lecture Hall / Training Room", + lpd_by_year={1999: 1.4, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.34, 2013: 1.34, 2019: 0.89}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Classroom/Lecture Hall / Training Room", + lpd_by_year={1999: 1.6, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.24, 2013: 1.24, 2019: 0.71}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Conference/Meeting / Multipurpose Room", + lpd_by_year={1999: 1.5, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.23, 2013: 1.23, 2019: 0.97}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Confinement Cells", + lpd_by_year={1999: 1.1, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 1.1, 2013: 0.81, 2019: 0.7}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Copy/Print Room", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.72, 2019: 0.31}, + ), + BuildingSpaceTypeLPD( + building_type="Facility for the visually impaired", + section_type="Corridor", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.66, 2013: 0.92, 2019: 0.71}, + ), + BuildingSpaceTypeLPD( + building_type="Hospital", + section_type="Corridor", + lpd_by_year={1999: 1.6, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.89, 2013: 0.99, 2019: 0.71}, + ), + BuildingSpaceTypeLPD( + building_type="Manufacturing facility", + section_type="Corridor", + lpd_by_year={1999: 0.5, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: 0.41, 2013: 0.41, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Corridor", + lpd_by_year={1999: 0.7, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: 0.66, 2013: 0.66, 2019: 0.41}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Courtroom", + lpd_by_year={1999: 2.1, 2001: 1.9, 2004: 1.9, 2007: 1.9, 2010: 1.72, 2013: 1.72, 2019: 1.2}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Judges' Chambers", + lpd_by_year={1999: 1.1, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.17, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Computer Room", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.71, 2019: 0.94}, + ), + BuildingSpaceTypeLPD( + building_type="Hotel", + section_type="Dining Area", + lpd_by_year={1999: 1.0, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 0.82, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Motel", + section_type="Dining Area", + lpd_by_year={1999: 1.2, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.88, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Penitentiary", + section_type="Dining Area", + lpd_by_year={1999: 1.4, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.07, 2013: 0.96, 2019: 0.42}, + ), + BuildingSpaceTypeLPD( + building_type="Facility for the visually impaired", + section_type="Dining Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 2.65, 2019: 1.27}, + ), + BuildingSpaceTypeLPD( + building_type="Bar/lounge or leisure dining", + section_type="Dining Area", + lpd_by_year={1999: 1.2, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.31, 2013: 1.07, 2019: 0.86}, + ), + BuildingSpaceTypeLPD( + building_type="Cafeteria or fast food dining", + section_type="Dining Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.65, 2019: 0.4}, + ), + BuildingSpaceTypeLPD( + building_type="Family dining", + section_type="Dining Area", + lpd_by_year={1999: 2.2, 2001: 2.1, 2004: 2.1, 2007: 2.1, 2010: 0.89, 2013: 0.89, 2019: 0.6}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Dining Area", + lpd_by_year={1999: 1.4, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.65, 2013: 0.65, 2019: 0.43}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Electrical/Mechanical Room", + lpd_by_year={1999: 1.3, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 0.95, 2013: 0.42, 2019: 0.43}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Emergency Vehicle Garage", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.56, 2019: 0.52}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Food Preparation Area", + lpd_by_year={1999: 2.2, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.99, 2013: 1.21, 2019: 1.09}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Guest Room", + lpd_by_year={1999: 2.5, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.11, 2013: 0.91, 2019: 0.41}, + ), + BuildingSpaceTypeLPD( + building_type="Classroom (school)", + section_type="Laboratory ", + lpd_by_year={1999: None, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.28, 2013: 1.43, 2019: 1.11}, + ), + BuildingSpaceTypeLPD( + building_type="Police station", + section_type="Laboratory ", + lpd_by_year={1999: 1.8, 2001: None, 2004: None, 2007: None, 2010: None, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other ", + section_type="Laboratory", + lpd_by_year={1999: 1.8, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.81, 2013: 1.81, 2019: 1.33}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Laundry/Washing Area", + lpd_by_year={1999: 0.7, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.6, 2013: 0.6, 2019: 0.53}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Loading Dock, Interior", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.47, 2019: 0.88}, + ), + BuildingSpaceTypeLPD( + building_type="Facility for the visually impaired", + section_type="Lobby", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.8, 2019: 1.69}, + ), + BuildingSpaceTypeLPD( + building_type="Elevator", + section_type="Lobby", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.64, 2013: 0.64, 2019: 0.65}, + ), + BuildingSpaceTypeLPD( + building_type="Hotel", + section_type="Lobby", + lpd_by_year={1999: 1.7, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.06, 2013: 1.06, 2019: 0.51}, + ), + BuildingSpaceTypeLPD( + building_type="Motion picture theater", + section_type="Lobby", + lpd_by_year={1999: 0.8, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.52, 2013: 0.59, 2019: 0.23}, + ), + BuildingSpaceTypeLPD( + building_type="Performing arts theater", + section_type="Lobby", + lpd_by_year={1999: 1.2, 2001: 3.3, 2004: 3.3, 2007: 3.3, 2010: 2.0, 2013: 2.0, 2019: 1.25}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Lobby", + lpd_by_year={1999: 1.8, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 0.9, 2013: 0.9, 2019: 0.84}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Locker Room", + lpd_by_year={1999: None, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.75, 2013: 0.75, 2019: 0.52}, + ), + BuildingSpaceTypeLPD( + building_type="Hospital / Healthcare facility", + section_type="Lounge / Breakroom", + lpd_by_year={1999: 1.4, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 1.07, 2013: 0.92, 2019: 0.42}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Lounge / Breakroom", + lpd_by_year={1999: 1.4, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.73, 2013: 0.73, 2019: 0.59}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Office", + lpd_by_year={1999: 1.43, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.07, 2013: 1.07, 2019: 0.67}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Parking Area", + lpd_by_year={1999: 0.2, 2001: 0.2, 2004: 0.2, 2007: 0.2, 2010: 0.19, 2013: 0.19, 2019: 0.15}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Pharmacy Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.68, 2019: 1.66}, + ), + BuildingSpaceTypeLPD( + building_type="Facility for the visually impaired", + section_type="Restroom", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 1.21, 2019: 1.26}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Restroom", + lpd_by_year={1999: 1.0, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: None, 2013: 0.98, 2019: 0.63}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Sales Area", + lpd_by_year={1999: 2.1, 2001: 2.1, 2004: 1.7, 2007: 1.7, 2010: 1.68, 2013: 1.44, 2019: 1.05}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Seating Area, General", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 0.54, 2019: 0.23}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Stairway", + lpd_by_year={1999: 0.9, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.69, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Stairwell", + lpd_by_year={1999: 0.9, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: None, 2013: 0.69, 2019: 0.49}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Storage Room", + lpd_by_year={1999: 0.7, 2001: 0.55, 2004: 0.55, 2007: 0.55, 2010: 0.63, 2013: 0.83, 2019: 0.42}, + ), + BuildingSpaceTypeLPD( + building_type="Hospital", + section_type="Storage Room", + lpd_by_year={1999: 2.9, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: None, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Museum", + section_type="Storage Room", + lpd_by_year={1999: 1.4, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: None, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Vehicular Maintenance Area / Automotive", + lpd_by_year={1999: 1.4, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.67, 2013: 0.67, 2019: 0.6}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Workshop", + lpd_by_year={1999: 2.5, 2001: 1.9, 2004: 1.9, 2007: 1.9, 2010: 1.59, 2013: 1.59, 2019: 1.26}, + ), + BuildingSpaceTypeLPD( + building_type="Facility for the Visually Impaired", + section_type="Chapel", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 2.21, 2019: 0.7}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Recreation room/common livingroom", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: None, 2013: 2.41, 2019: 1.77}, + ), + BuildingSpaceTypeLPD( + building_type="Convention Center", + section_type="Exhibit Space", + lpd_by_year={1999: 3.3, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.45, 2013: 1.45, 2019: 0.61}, + ), + BuildingSpaceTypeLPD( + building_type="Dormitory", + section_type="Living Quarters", + lpd_by_year={1999: 1.9, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.38, 2013: 0.38, 2019: 0.5}, + ), + BuildingSpaceTypeLPD( + building_type="Fire Station", + section_type="Engine Room", + lpd_by_year={1999: 0.9, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 0.56, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Sleeping Quarters", + lpd_by_year={1999: 1.1, 2001: 0.3, 2004: 0.3, 2007: 0.3, 2010: 0.25, 2013: 0.22, 2019: 0.23}, + ), + BuildingSpaceTypeLPD( + building_type="Gymnasium / Fitness Center", + section_type="Exercise area", + lpd_by_year={1999: 1.1, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.72, 2013: 0.72, 2019: 0.9}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Playing area", + lpd_by_year={1999: 1.9, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.2, 2013: 1.2, 2019: 0.85}, + ), + BuildingSpaceTypeLPD( + building_type="Hospital / Healthcare Facility", + section_type="Emergency", + lpd_by_year={1999: 2.8, 2001: 2.7, 2004: 2.7, 2007: 2.7, 2010: 2.26, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Exam/treatment", + lpd_by_year={1999: 1.6, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.66, 2013: 1.66, 2019: 1.4}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Imaging / radiology", + lpd_by_year={1999: 0.4, 2001: 0.4, 2004: 0.4, 2007: 0.4, 2010: 1.32, 2013: 1.51, 2019: 0.94}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Medical supply", + lpd_by_year={1999: 3.0, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.27, 2013: 0.74, 2019: 0.62}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Nursery", + lpd_by_year={1999: 1.0, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.88, 2013: 0.88, 2019: 0.92}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Nurse’s station", # noqa: RUF001 + lpd_by_year={1999: 1.8, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.87, 2013: 0.71, 2019: 1.17}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Operating room", + lpd_by_year={1999: 7.6, 2001: 2.2, 2004: 2.2, 2007: 2.2, 2010: 1.89, 2013: 2.48, 2019: 2.26}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Patient room", + lpd_by_year={1999: 1.2, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.62, 2013: 0.62, 2019: 0.68}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Pharmacy", + lpd_by_year={1999: 2.3, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.14, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Physical therapy", + lpd_by_year={1999: 1.9, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.91, 2013: 0.91, 2019: 0.91}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Recovery", + lpd_by_year={1999: 2.6, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 1.15, 2013: 1.15, 2019: 1.25}, + ), + BuildingSpaceTypeLPD( + building_type="Library", + section_type="Card File and Cataloging", + lpd_by_year={1999: 1.4, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.72, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Reading area", + lpd_by_year={1999: 1.8, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.93, 2013: 1.06, 2019: 0.96}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="The stacks", + lpd_by_year={1999: 1.9, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.71, 2013: 1.71, 2019: 1.18}, + ), + BuildingSpaceTypeLPD( + building_type="Manufacturing Facility", + section_type="Detailed manufacturing area", + lpd_by_year={1999: 6.2, 2001: 2.1, 2004: 2.1, 2007: 2.1, 2010: 1.29, 2013: 1.29, 2019: 0.8}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Equipment room", + lpd_by_year={1999: 0.8, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.95, 2013: 0.74, 2019: 0.76}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Extra high bay area (>50 ft height)", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 1.05, 2013: 1.05, 2019: 1.42}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="High bay area (25-50 ft height)", + lpd_by_year={1999: 3.0, 2001: 1.7, 2004: None, 2007: 1.7, 2010: 1.23, 2013: 1.23, 2019: 1.24}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Low bay area (<25 ft height)", + lpd_by_year={1999: 2.1, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.19, 2013: 1.19, 2019: 0.86}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Control room", + lpd_by_year={1999: 0.5, 2001: 0.5, 2004: 0.5, 2007: 0.5, 2010: None, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Museum", + section_type="General exhibition area", + lpd_by_year={1999: 1.6, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 1.05, 2013: 1.05, 2019: 0.31}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Restoration room", + lpd_by_year={1999: 2.5, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.02, 2013: 1.02, 2019: 1.1}, + ), + BuildingSpaceTypeLPD( + building_type="Performing Arts Theater", + section_type="Dressing Room", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.4, 2013: 0.61, 2019: 0.41}, + ), + BuildingSpaceTypeLPD( + building_type="Post Office", + section_type="Sorting Area", + lpd_by_year={1999: 1.7, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.94, 2013: 0.94, 2019: 0.76}, + ), + BuildingSpaceTypeLPD( + building_type="Religious Buildings", + section_type="Fellowship hall", + lpd_by_year={1999: 2.3, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.64, 2013: 0.64, 2019: 0.54}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Worship/pulpit/choir area", + lpd_by_year={1999: 5.2, 2001: 2.4, 2004: 2.4, 2007: 2.4, 2010: 1.53, 2013: 1.53, 2019: 0.85}, + ), + BuildingSpaceTypeLPD( + building_type="Retail Facilities", + section_type="Dressing/fitting room", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.87, 2013: 0.71, 2019: 0.51}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Mall concourse", + lpd_by_year={1999: 1.8, 2001: 1.7, 2004: 1.7, 2007: 1.7, 2010: 1.1, 2013: 1.1, 2019: 0.82}, + ), + BuildingSpaceTypeLPD( + building_type="Sports Arena Class I facility", + section_type="Playing Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 3.01, 2013: 3.68, 2019: 2.94}, + ), + BuildingSpaceTypeLPD( + building_type="Sports Arena Class II facility", + section_type="Playing Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 1.92, 2013: 2.4, 2019: 2.01}, + ), + BuildingSpaceTypeLPD( + building_type="Sports Arena Class III facility", + section_type="Playing Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 1.2, 2013: 1.8, 2019: 1.3}, + ), + BuildingSpaceTypeLPD( + building_type="Sports Arena Class IV facility", + section_type="Playing Area", + lpd_by_year={1999: None, 2001: None, 2004: None, 2007: None, 2010: 0.72, 2013: 1.2, 2019: 0.86}, + ), + BuildingSpaceTypeLPD( + building_type="Ring Sports Arena", + section_type="Playing Area", + lpd_by_year={1999: 3.8, 2001: 2.7, 2004: 2.7, 2007: 2.7, 2010: 2.68, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Court Sports Area", + section_type="Playing Area", + lpd_by_year={1999: 4.3, 2001: 2.3, 2004: 2.3, 2007: 2.3, 2010: None, 2013: None, 2019: None}, + ), + BuildingSpaceTypeLPD( + building_type="Transportation Facility", + section_type="Baggage / carousel area", + lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.76, 2013: 0.53, 2019: 0.39}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Airport concourse", + lpd_by_year={1999: 0.7, 2001: 0.6, 2004: 0.6, 2007: 0.6, 2010: 0.36, 2013: 0.36, 2019: 0.25}, + ), + BuildingSpaceTypeLPD( + building_type="Other", + section_type="Ticket counter", + lpd_by_year={1999: 1.8, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.08, 2013: 0.8, 2019: 0.51}, + ), + BuildingSpaceTypeLPD( + building_type="Warehouse", + section_type="Storage Area", + lpd_by_year={1999: 1.35, 2001: 1.15, 2004: 1.15, 2007: 1.15, 2010: 0.76, 2013: 0.76, 2019: 0.51}, + ), ] diff --git a/buildingsync_asset_extractor/lighting_processing/building_type_to_lpd.py b/buildingsync_asset_extractor/lighting_processing/building_type_to_lpd.py index ae4ca20..481feae 100644 --- a/buildingsync_asset_extractor/lighting_processing/building_type_to_lpd.py +++ b/buildingsync_asset_extractor/lighting_processing/building_type_to_lpd.py @@ -9,37 +9,136 @@ class BuildingTypeLPD: building_type_to_lpd = [ - BuildingTypeLPD(building_type="Automotive Facility", lpd_by_year={1999: 1.5, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.82, 2013: 0.8, 2019: 0.75}), - BuildingTypeLPD(building_type="Convention Center", lpd_by_year={1999: 1.4, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.08, 2013: 1.01, 2019: 0.64}), - BuildingTypeLPD(building_type="Court House", lpd_by_year={1999: 1.4, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.05, 2013: 1.01, 2019: 0.79}), - BuildingTypeLPD(building_type="Dining: Bar Lounge/Leisure", lpd_by_year={1999: 1.5, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 0.99, 2013: 1.01, 2019: 0.8}), - BuildingTypeLPD(building_type="Dining: Cafeteria/Fast Food", lpd_by_year={1999: 1.8, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 0.9, 2013: 0.9, 2019: 0.76}), - BuildingTypeLPD(building_type="Dining: Family ", lpd_by_year={1999: 1.9, 2001: 1.6, 2004: 1.6, 2007: 1.6, 2010: 0.89, 2013: 0.95, 2019: 0.71}), - BuildingTypeLPD(building_type="Dormitory ", lpd_by_year={1999: 1.5, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.61, 2013: 0.57, 2019: 0.53}), - BuildingTypeLPD(building_type="Exercise Center", lpd_by_year={1999: 1.4, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.88, 2013: 0.84, 2019: 0.72}), - BuildingTypeLPD(building_type="Fire Station ", lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.71, 2013: 0.671, 2019: 0.56}), - BuildingTypeLPD(building_type="Gymnasium", lpd_by_year={1999: 1.7, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.0, 2013: 0.94, 2019: 0.76}), - BuildingTypeLPD(building_type="Health Care-Clinic", lpd_by_year={1999: 1.6, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.87, 2013: 0.9, 2019: 0.81}), - BuildingTypeLPD(building_type="Hospital", lpd_by_year={1999: 1.6, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.21, 2013: 1.05, 2019: 0.96}), - BuildingTypeLPD(building_type="Hotel", lpd_by_year={1999: 1.7, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 1.0, 2013: 0.87, 2019: 0.56}), - BuildingTypeLPD(building_type="Library", lpd_by_year={1999: 1.5, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.18, 2013: 1.19, 2019: 0.83}), - BuildingTypeLPD(building_type="Manufacturing Facility", lpd_by_year={1999: 2.2, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.11, 2013: 1.17, 2019: 0.82}), - BuildingTypeLPD(building_type="Motel", lpd_by_year={1999: 2.0, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.88, 2013: 0.87, 2019: 0.56}), - BuildingTypeLPD(building_type="Motion Picture Theater", lpd_by_year={1999: 1.6, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.83, 2013: 0.76, 2019: 0.44}), - BuildingTypeLPD(building_type="Multi-Family", lpd_by_year={1999: 1.0, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.6, 2013: 0.51, 2019: 0.45}), - BuildingTypeLPD(building_type="Museum", lpd_by_year={1999: 1.6, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.06, 2013: 1.02, 2019: 0.55}), - BuildingTypeLPD(building_type="Office", lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.9, 2013: 0.82, 2019: 0.64}), - BuildingTypeLPD(building_type="Parking Garage", lpd_by_year={1999: 0.3, 2001: 0.3, 2004: 0.3, 2007: 0.3, 2010: 0.25, 2013: 0.21, 2019: 0.18}), - BuildingTypeLPD(building_type="Penitentiary", lpd_by_year={1999: 1.2, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.97, 2013: 0.81, 2019: 0.69}), - BuildingTypeLPD(building_type="Performing Arts Theater", lpd_by_year={1999: 1.5, 2001: 1.6, 2004: 1.6, 2007: 1.6, 2010: 1.39, 2013: 1.39, 2019: 0.84}), - BuildingTypeLPD(building_type="Police Station", lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.96, 2013: 0.87, 2019: 0.66}), - BuildingTypeLPD(building_type="Post Office", lpd_by_year={1999: 1.6, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.87, 2013: 0.87, 2019: 0.65}), - BuildingTypeLPD(building_type="Religious Building", lpd_by_year={1999: 2.2, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.05, 2013: 1.0, 2019: 0.67}), - BuildingTypeLPD(building_type="Retail", lpd_by_year={1999: 1.9, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.4, 2013: 1.26, 2019: 0.84}), - BuildingTypeLPD(building_type="School/University", lpd_by_year={1999: 1.5, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.99, 2013: 0.87, 2019: 0.72}), - BuildingTypeLPD(building_type="Sports Arena", lpd_by_year={1999: 1.5, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.78, 2013: 0.91, 2019: 0.76}), - BuildingTypeLPD(building_type="Town Hall", lpd_by_year={1999: 1.4, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.92, 2013: 0.89, 2019: 0.69}), - BuildingTypeLPD(building_type="Transportation", lpd_by_year={1999: 1.2, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.77, 2013: 0.7, 2019: 0.5}), - BuildingTypeLPD(building_type="Warehouse", lpd_by_year={1999: 1.2, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 0.66, 2013: 0.66, 2019: 0.45}), - BuildingTypeLPD(building_type="Workshop", lpd_by_year={1999: 1.7, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.2, 2013: 1.19, 2019: 0.91}), + BuildingTypeLPD( + building_type="Automotive Facility", + lpd_by_year={1999: 1.5, 2001: 0.9, 2004: 0.9, 2007: 0.9, 2010: 0.82, 2013: 0.8, 2019: 0.75}, + ), + BuildingTypeLPD( + building_type="Convention Center", + lpd_by_year={1999: 1.4, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.08, 2013: 1.01, 2019: 0.64}, + ), + BuildingTypeLPD( + building_type="Court House", + lpd_by_year={1999: 1.4, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.05, 2013: 1.01, 2019: 0.79}, + ), + BuildingTypeLPD( + building_type="Dining: Bar Lounge/Leisure", + lpd_by_year={1999: 1.5, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 0.99, 2013: 1.01, 2019: 0.8}, + ), + BuildingTypeLPD( + building_type="Dining: Cafeteria/Fast Food", + lpd_by_year={1999: 1.8, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 0.9, 2013: 0.9, 2019: 0.76}, + ), + BuildingTypeLPD( + building_type="Dining: Family ", + lpd_by_year={1999: 1.9, 2001: 1.6, 2004: 1.6, 2007: 1.6, 2010: 0.89, 2013: 0.95, 2019: 0.71}, + ), + BuildingTypeLPD( + building_type="Dormitory ", + lpd_by_year={1999: 1.5, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.61, 2013: 0.57, 2019: 0.53}, + ), + BuildingTypeLPD( + building_type="Exercise Center", + lpd_by_year={1999: 1.4, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.88, 2013: 0.84, 2019: 0.72}, + ), + BuildingTypeLPD( + building_type="Fire Station ", + lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.71, 2013: 0.671, 2019: 0.56}, + ), + BuildingTypeLPD( + building_type="Gymnasium", + lpd_by_year={1999: 1.7, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.0, 2013: 0.94, 2019: 0.76}, + ), + BuildingTypeLPD( + building_type="Health Care-Clinic", + lpd_by_year={1999: 1.6, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.87, 2013: 0.9, 2019: 0.81}, + ), + BuildingTypeLPD( + building_type="Hospital", + lpd_by_year={1999: 1.6, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 1.21, 2013: 1.05, 2019: 0.96}, + ), + BuildingTypeLPD( + building_type="Hotel", + lpd_by_year={1999: 1.7, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 1.0, 2013: 0.87, 2019: 0.56}, + ), + BuildingTypeLPD( + building_type="Library", + lpd_by_year={1999: 1.5, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.18, 2013: 1.19, 2019: 0.83}, + ), + BuildingTypeLPD( + building_type="Manufacturing Facility", + lpd_by_year={1999: 2.2, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.11, 2013: 1.17, 2019: 0.82}, + ), + BuildingTypeLPD( + building_type="Motel", + lpd_by_year={1999: 2.0, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.88, 2013: 0.87, 2019: 0.56}, + ), + BuildingTypeLPD( + building_type="Motion Picture Theater", + lpd_by_year={1999: 1.6, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.83, 2013: 0.76, 2019: 0.44}, + ), + BuildingTypeLPD( + building_type="Multi-Family", + lpd_by_year={1999: 1.0, 2001: 0.7, 2004: 0.7, 2007: 0.7, 2010: 0.6, 2013: 0.51, 2019: 0.45}, + ), + BuildingTypeLPD( + building_type="Museum", + lpd_by_year={1999: 1.6, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 1.06, 2013: 1.02, 2019: 0.55}, + ), + BuildingTypeLPD( + building_type="Office", + lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.9, 2013: 0.82, 2019: 0.64}, + ), + BuildingTypeLPD( + building_type="Parking Garage", + lpd_by_year={1999: 0.3, 2001: 0.3, 2004: 0.3, 2007: 0.3, 2010: 0.25, 2013: 0.21, 2019: 0.18}, + ), + BuildingTypeLPD( + building_type="Penitentiary", + lpd_by_year={1999: 1.2, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.97, 2013: 0.81, 2019: 0.69}, + ), + BuildingTypeLPD( + building_type="Performing Arts Theater", + lpd_by_year={1999: 1.5, 2001: 1.6, 2004: 1.6, 2007: 1.6, 2010: 1.39, 2013: 1.39, 2019: 0.84}, + ), + BuildingTypeLPD( + building_type="Police Station", + lpd_by_year={1999: 1.3, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.96, 2013: 0.87, 2019: 0.66}, + ), + BuildingTypeLPD( + building_type="Post Office", + lpd_by_year={1999: 1.6, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.87, 2013: 0.87, 2019: 0.65}, + ), + BuildingTypeLPD( + building_type="Religious Building", + lpd_by_year={1999: 2.2, 2001: 1.3, 2004: 1.3, 2007: 1.3, 2010: 1.05, 2013: 1.0, 2019: 0.67}, + ), + BuildingTypeLPD( + building_type="Retail", + lpd_by_year={1999: 1.9, 2001: 1.5, 2004: 1.5, 2007: 1.5, 2010: 1.4, 2013: 1.26, 2019: 0.84}, + ), + BuildingTypeLPD( + building_type="School/University", + lpd_by_year={1999: 1.5, 2001: 1.2, 2004: 1.2, 2007: 1.2, 2010: 0.99, 2013: 0.87, 2019: 0.72}, + ), + BuildingTypeLPD( + building_type="Sports Arena", + lpd_by_year={1999: 1.5, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.78, 2013: 0.91, 2019: 0.76}, + ), + BuildingTypeLPD( + building_type="Town Hall", + lpd_by_year={1999: 1.4, 2001: 1.1, 2004: 1.1, 2007: 1.1, 2010: 0.92, 2013: 0.89, 2019: 0.69}, + ), + BuildingTypeLPD( + building_type="Transportation", + lpd_by_year={1999: 1.2, 2001: 1.0, 2004: 1.0, 2007: 1.0, 2010: 0.77, 2013: 0.7, 2019: 0.5}, + ), + BuildingTypeLPD( + building_type="Warehouse", + lpd_by_year={1999: 1.2, 2001: 0.8, 2004: 0.8, 2007: 0.8, 2010: 0.66, 2013: 0.66, 2019: 0.45}, + ), + BuildingTypeLPD( + building_type="Workshop", + lpd_by_year={1999: 1.7, 2001: 1.4, 2004: 1.4, 2007: 1.4, 2010: 1.2, 2013: 1.19, 2019: 0.91}, + ), ] diff --git a/buildingsync_asset_extractor/lighting_processing/lighting_processing.py b/buildingsync_asset_extractor/lighting_processing/lighting_processing.py index d75a584..e96797a 100644 --- a/buildingsync_asset_extractor/lighting_processing/lighting_processing.py +++ b/buildingsync_asset_extractor/lighting_processing/lighting_processing.py @@ -5,25 +5,22 @@ from lxml.etree import ElementTree from buildingsync_asset_extractor.lighting_processing.building_occ_class_to_building_type import ( - building_occ_class_to_building_type + building_occ_class_to_building_type, ) from buildingsync_asset_extractor.lighting_processing.building_space_type_to_lpd import ( BuildingSpaceTypeLPD, - building_space_type_to_lpd -) -from buildingsync_asset_extractor.lighting_processing.building_type_to_lpd import ( - BuildingTypeLPD, - building_type_to_lpd + building_space_type_to_lpd, ) +from buildingsync_asset_extractor.lighting_processing.building_type_to_lpd import BuildingTypeLPD, building_type_to_lpd from buildingsync_asset_extractor.lighting_processing.section_occ_class_to_section_type import ( - section_occ_class_to_section_type + section_occ_class_to_section_type, ) if TYPE_CHECKING: from buildingsync_asset_extractor.processor import BSyncProcessor -BUILDING_PATH = '/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building' +BUILDING_PATH = "/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building" LIGHTING_SYSTEM_PATH = "/BuildingSync/Facilities/Facility/Systems/LightingSystems/LightingSystem" @@ -43,35 +40,34 @@ class LightingDataPower(LightingData): power: float -def process_buildings_lighting_systems(bsync_processor: "BSyncProcessor") -> list[LightingData]: +def process_buildings_lighting_systems(bsync_processor: "BSyncProcessor") -> list[LightingData]: # noqa: PLR0915 """Given a bsync_processor with a single building, get all of its lightingData. - This function is HUGE, and thus broken down into a series of more digestable functions, starting + This function is HUGE, and thus broken down into a series of more digestible functions, starting with _process_buildings_lighting_systems. """ + def _process_buildings_lighting_systems() -> list[LightingData]: - """Given a bsync_processor with a single building, get each section's LightingData. - """ + """Given a bsync_processor with a single building, get each section's LightingData.""" # assert only one building in doc buildings = bsync_processor.xp(bsync_processor.doc, BUILDING_PATH) if len(buildings) == 1: building = buildings[0] else: raise ValueError( - f"process_lighting requires a document with a singular building. The given document has {len(buildings)} buildings" + f"process_lighting requires a document with a singular building. The given document has {len(buildings)} buildings", ) # get the lighting datas for each section lighting_datas: list[LightingData] = [] - sections = bsync_processor.xp(building, './/' + 'Sections/Section') + sections = bsync_processor.xp(building, ".//" + "Sections/Section") for section in sections: lighting_datas += process_sections_lighting_systems(section) return lighting_datas def process_sections_lighting_systems(section: ElementTree) -> list[LightingData]: - """Get all the section's lightingData. - """ + """Get all the section's lightingData.""" lighting_systems: list[ElementTree] = get_sections_lighting_systems(section) # if no lighting systems, method_4 on the whole section. @@ -112,26 +108,25 @@ def process_sections_lighting_systems(section: ElementTree) -> list[LightingData return lighting_datas def get_sections_lighting_systems(section: ElementTree) -> list[ElementTree]: - """Get all the lighting systems linked to the section. - """ - section_ID = section.get("ID") + """Get all the lighting systems linked to the section.""" + section_id = section.get("ID") sections_lighting_systems = [] all_lighting_systems = bsync_processor.xp(bsync_processor.doc, LIGHTING_SYSTEM_PATH) for ls in all_lighting_systems: - linked_sections = bsync_processor.xp(ls, './/' + 'LinkedSectionID') - linked_section_IDs = [s.get('IDref') for s in linked_sections] + linked_sections = bsync_processor.xp(ls, ".//" + "LinkedSectionID") + linked_section_ids = [s.get("IDref") for s in linked_sections] - if section_ID in linked_section_IDs: + if section_id in linked_section_ids: sections_lighting_systems.append(ls) return sections_lighting_systems def get_lighting_system_sqft_percent(lighting_system: ElementTree) -> Optional[float]: - """Get lighting systems PercentPremisesServed. - """ + """Get lighting systems PercentPremisesServed.""" percent_premises_served = bsync_processor.xp( - lighting_system, './/' + 'PercentPremisesServed' + lighting_system, + ".//" + "PercentPremisesServed", ) if len(percent_premises_served) > 0: return float(percent_premises_served[0].text) @@ -139,25 +134,22 @@ def get_lighting_system_sqft_percent(lighting_system: ElementTree) -> Optional[f return None def get_section_gross_floor_area(section: ElementTree) -> float: - """Get sections gross floor area. - """ - section_ID = section.get("ID") + """Get sections gross floor area.""" + section_id = section.get("ID") - return bsync_processor.sections[section_ID].areas.get("Gross") # type: ignore + return bsync_processor.sections[section_id].areas.get("Gross") # type: ignore[return-value] def get_lighting_system_sqft(section: ElementTree, lighting_system: ElementTree) -> float: - """Give a section and lighting system, get the sqft the lighting system covers within that section. - """ - section_ID = section.get("ID") - all_linked_sections = bsync_processor.xp(lighting_system, './/' + 'LinkedSectionID') - linked_sections = [ls for ls in all_linked_sections if ls.get('IDref') == section_ID] + """Give a section and lighting system, get the sqft the lighting system covers within that section.""" + section_id = section.get("ID") + all_linked_sections = bsync_processor.xp(lighting_system, ".//" + "LinkedSectionID") + linked_sections = [ls for ls in all_linked_sections if ls.get("IDref") == section_id] return sum([bsync_processor.compute_sqft(ls) for ls in linked_sections]) def method_1(lighting_system: ElementTree) -> Optional[float]: - """return lighting system's InstalledPower. - """ - installed_powers = bsync_processor.xp(lighting_system, './/' + 'InstalledPower') + """return lighting system's InstalledPower.""" + installed_powers = bsync_processor.xp(lighting_system, ".//" + "InstalledPower") if len(installed_powers) > 0: return float(installed_powers[0].text) @@ -167,20 +159,16 @@ def method_1(lighting_system: ElementTree) -> Optional[float]: def method_2(lighting_system: ElementTree) -> Optional[float]: """Lamp Power * # Lamps per Luminaire * # Luminaire * Quantity - number_of_luminaireses may be taken from user defined fields. + number_of_luminaires may be taken from user defined fields. """ - number_of_luminaireses = bsync_processor.xp(lighting_system, './/' + 'NumberOfLuminaires') - number_of_lamps_per_luminaires = bsync_processor.xp(lighting_system, './/' + 'NumberOfLampsPerLuminaire') - lamp_powers = bsync_processor.xp(lighting_system, './/' + 'LampPower') - quantity_elements = bsync_processor.xp(lighting_system, './/' + 'Quantity') + number_of_luminaires = bsync_processor.xp(lighting_system, ".//" + "NumberOfLuminaires") + number_of_lamps_per_luminaires = bsync_processor.xp(lighting_system, ".//" + "NumberOfLampsPerLuminaire") + lamp_powers = bsync_processor.xp(lighting_system, ".//" + "LampPower") + quantity_elements = bsync_processor.xp(lighting_system, ".//" + "Quantity") # method 2a: Lamp Power * # Lamps per Luminaire * # Luminaire * Quantity - if ( - len(lamp_powers) > 0 and - len(number_of_lamps_per_luminaires) > 0 and - len(number_of_luminaireses) > 0 - ): - power = float(lamp_powers[0].text) * float(number_of_lamps_per_luminaires[0].text) * float(number_of_luminaireses[0].text) + if len(lamp_powers) > 0 and len(number_of_lamps_per_luminaires) > 0 and len(number_of_luminaires) > 0: + power = float(lamp_powers[0].text) * float(number_of_lamps_per_luminaires[0].text) * float(number_of_luminaires[0].text) if quantity_elements: power *= float(quantity_elements[0].text) @@ -188,40 +176,30 @@ def method_2(lighting_system: ElementTree) -> Optional[float]: # method 2b: Lamp Power * # Lamps per Luminaire * # Luminaire # where # Luminaire is user defined - elif ( - len(lamp_powers) > 0 and - len(number_of_lamps_per_luminaires) > 0 - ): + elif len(lamp_powers) > 0 and len(number_of_lamps_per_luminaires) > 0: # try to get # luminaires a different way # UDF: '* Quantity Of Luminaires For *' # example: Common Areas Quantity Of Luminaires For Section-101919600 - user_defined_fields = bsync_processor._get_user_defined_feilds(lighting_system) + user_defined_fields = bsync_processor._get_user_defined_fields(lighting_system) user_defined_values = [ int(value) for (name, value) in user_defined_fields - if 'Quantity Of Luminaires For' in name - and value.replace(" ", "").isnumeric() + if "Quantity Of Luminaires For" in name and value.replace(" ", "").isnumeric() ] qty_val = sum(user_defined_values) if qty_val > 0: - return ( - int(lamp_powers[0].text) * - int(number_of_lamps_per_luminaires[0].text) * - qty_val - ) + return int(lamp_powers[0].text) * int(number_of_lamps_per_luminaires[0].text) * qty_val return None def method_3(lighting_system: ElementTree) -> Optional[float]: - """Look in UDF for "Lighting Power Density For ..." - """ - user_defined_fields = bsync_processor._get_user_defined_feilds(lighting_system) + """Look in UDF for "Lighting Power Density For ..." """ + user_defined_fields = bsync_processor._get_user_defined_fields(lighting_system) user_defined_values = [ - int(value) for (name, value) - in user_defined_fields - if 'Lighting Power Density For' in name - and value.replace(" ", "").isnumeric() + int(value) + for (name, value) in user_defined_fields + if "Lighting Power Density For" in name and value.replace(" ", "").isnumeric() ] qty_val = sum(user_defined_values) @@ -231,8 +209,7 @@ def method_3(lighting_system: ElementTree) -> Optional[float]: return None def method_4(section: ElementTree) -> Optional[float]: - """Use decision matrix to get lpd from building/section occupancy class and year. - """ + """Use decision matrix to get lpd from building/section occupancy class and year.""" building = section.getparent().getparent() section_occ_class = get_occupancy_classification(section) building_occ_class = get_occupancy_classification(building) @@ -288,7 +265,7 @@ def method_4(section: ElementTree) -> Optional[float]: def get_occupancy_classification(element: ElementTree) -> Optional[str]: """Get a building or sections OccupancyClassification""" - occupancy_classification = bsync_processor.xp(element, './' + 'OccupancyClassification') + occupancy_classification = bsync_processor.xp(element, "./" + "OccupancyClassification") if len(occupancy_classification) > 0: return occupancy_classification[0].text @@ -297,15 +274,15 @@ def get_occupancy_classification(element: ElementTree) -> Optional[str]: def get_year(building: ElementTree) -> Optional[float]: """Get a building's Year.""" - year_of_lastest_retrofit = bsync_processor.xp(building, './/' + 'YearOfLatestRetrofit') - if len(year_of_lastest_retrofit) > 0: - return float(year_of_lastest_retrofit[0].text) + year_of_latest_retrofit = bsync_processor.xp(building, ".//" + "YearOfLatestRetrofit") + if len(year_of_latest_retrofit) > 0: + return float(year_of_latest_retrofit[0].text) - year_of_last_major_remodel = bsync_processor.xp(building, './/' + 'YearOfLastMajorRemodel') + year_of_last_major_remodel = bsync_processor.xp(building, ".//" + "YearOfLastMajorRemodel") if len(year_of_last_major_remodel) > 0: return float(year_of_last_major_remodel[0].text) - year_of_construction = bsync_processor.xp(building, './/' + 'YearOfConstruction') + year_of_construction = bsync_processor.xp(building, ".//" + "YearOfConstruction") if len(year_of_construction) > 0: return float(year_of_construction[0].text) diff --git a/buildingsync_asset_extractor/lighting_processing/section_occ_class_to_section_type.py b/buildingsync_asset_extractor/lighting_processing/section_occ_class_to_section_type.py index 3d40392..ac0996b 100644 --- a/buildingsync_asset_extractor/lighting_processing/section_occ_class_to_section_type.py +++ b/buildingsync_asset_extractor/lighting_processing/section_occ_class_to_section_type.py @@ -1,56 +1,56 @@ section_occ_class_to_section_type = { - 'Atrium': 'Atrium', - 'Auditorium': 'Audience Seating Area', - 'Chemical storage room': 'Storage Room', - 'Classroom': 'Classroom/Lecture Hall', - 'Computer lab': 'Computer Room', - 'Conference room': 'Conference/Meeting', - 'Corridor': 'Corridor', - 'Courtroom': 'Courtroom', - 'Dining area': 'Dining Area', - 'Dressing area': 'Dressing Room', - 'Food sales-Grocery store': 'Sales Area', - 'Health care-Inpatient hospital': 'Patient room', - 'Health care-Outpatient facility': 'Emergency', - 'Health care-Outpatient rehabilitation': 'Recovery', - 'Health care-Outpatient surgical': 'Operating room', - 'Health care-Pharmacy': 'Pharmacy', - 'Health care-Residential treatment center': 'Exam/treatment', - 'Health care-Skilled nursing facility': 'Nursery', - 'Kitchen': 'Food Preparation Area', - 'Kitchenette': 'Food Preparation Area', - 'Laboratory': 'Laboratory', - 'Laboratory-Medical': 'Laboratory', - 'Laboratory-Testing': 'Laboratory', - 'Laundry area': 'Laundry/Washing Area', - 'Living area': 'Living Quarters', - 'Lobby': 'Lobby', - 'Lodging area': 'Living Quarters', - 'Mechanical room': ' Electrical/Mechanical Room', - 'Non-chemical storage room': 'Storage Room', - 'Office': 'Office', - 'Office work area': 'Office', - 'Office-Financial': 'Office', - 'Parking': 'Parking Area', - 'Printing room': 'Copy/Print Room', - 'Recreation-Bowling alley': 'Playing area', - 'Recreation-Fitness center': 'Exercise area', - 'Recreation-Ice rink': 'Playing area', - 'Recreation-Indoor sport': 'Playing area', - 'Recreation-Pool': 'Playing area', - 'Recreation-Roller rink': 'Playing area', - 'Refrigerated storage': 'Storage Room', - 'Restroom': 'Restroom', - 'Service-Laundry or dry cleaning': 'Laundry/Washing Area', - 'Service-Postal': 'Sorting Area?', - 'Service-Production and assembly': 'multiple categories under Manufacturing Facility', - 'Shipping and receiving': 'Sorting Area', - 'Sleeping area': 'Sleeping Quarters', - 'Spectator area': 'Audience Seating Area', - 'Sport play area': 'Playing Area', - 'Transportation waiting area': 'Audience Seating Area', - 'Warehouse': 'Storage Area', - 'Warehouse-Refrigerated': 'Storage Area', - 'Warehouse-Self-storage': 'Storage Area', - 'Warehouse-Unrefrigerated': 'Storage Area' - } + "Atrium": "Atrium", + "Auditorium": "Audience Seating Area", + "Chemical storage room": "Storage Room", + "Classroom": "Classroom/Lecture Hall", + "Computer lab": "Computer Room", + "Conference room": "Conference/Meeting", + "Corridor": "Corridor", + "Courtroom": "Courtroom", + "Dining area": "Dining Area", + "Dressing area": "Dressing Room", + "Food sales-Grocery store": "Sales Area", + "Health care-Inpatient hospital": "Patient room", + "Health care-Outpatient facility": "Emergency", + "Health care-Outpatient rehabilitation": "Recovery", + "Health care-Outpatient surgical": "Operating room", + "Health care-Pharmacy": "Pharmacy", + "Health care-Residential treatment center": "Exam/treatment", + "Health care-Skilled nursing facility": "Nursery", + "Kitchen": "Food Preparation Area", + "Kitchenette": "Food Preparation Area", + "Laboratory": "Laboratory", + "Laboratory-Medical": "Laboratory", + "Laboratory-Testing": "Laboratory", + "Laundry area": "Laundry/Washing Area", + "Living area": "Living Quarters", + "Lobby": "Lobby", + "Lodging area": "Living Quarters", + "Mechanical room": " Electrical/Mechanical Room", + "Non-chemical storage room": "Storage Room", + "Office": "Office", + "Office work area": "Office", + "Office-Financial": "Office", + "Parking": "Parking Area", + "Printing room": "Copy/Print Room", + "Recreation-Bowling alley": "Playing area", + "Recreation-Fitness center": "Exercise area", + "Recreation-Ice rink": "Playing area", + "Recreation-Indoor sport": "Playing area", + "Recreation-Pool": "Playing area", + "Recreation-Roller rink": "Playing area", + "Refrigerated storage": "Storage Room", + "Restroom": "Restroom", + "Service-Laundry or dry cleaning": "Laundry/Washing Area", + "Service-Postal": "Sorting Area?", + "Service-Production and assembly": "multiple categories under Manufacturing Facility", + "Shipping and receiving": "Sorting Area", + "Sleeping area": "Sleeping Quarters", + "Spectator area": "Audience Seating Area", + "Sport play area": "Playing Area", + "Transportation waiting area": "Audience Seating Area", + "Warehouse": "Storage Area", + "Warehouse-Refrigerated": "Storage Area", + "Warehouse-Self-storage": "Storage Area", + "Warehouse-Unrefrigerated": "Storage Area", +} diff --git a/buildingsync_asset_extractor/main.py b/buildingsync_asset_extractor/main.py index 1c52dba..5060074 100644 --- a/buildingsync_asset_extractor/main.py +++ b/buildingsync_asset_extractor/main.py @@ -33,16 +33,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************************************* """ + import os from buildingsync_asset_extractor.processor import BSyncProcessor # 1: regular test -filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'tests/files/completetest.xml') -out_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'assets_output.json') -out_file2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'assets_output2.json') +filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "tests/files/completetest.xml") +out_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets_output.json") +out_file2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets_output2.json") -print("filename: {}".format(filename)) +print(f"filename: {filename}") # bp = BSyncProcessor(filename=filename, logger_level='DEBUG') bp = BSyncProcessor(filename=filename) diff --git a/buildingsync_asset_extractor/processor.py b/buildingsync_asset_extractor/processor.py index 6514811..7fa3c22 100644 --- a/buildingsync_asset_extractor/processor.py +++ b/buildingsync_asset_extractor/processor.py @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************************************* """ + import dataclasses import json import logging @@ -45,18 +46,12 @@ from lxml import etree from lxml.etree import ElementTree -from buildingsync_asset_extractor.bae_types import ( - Asset, - AssetData, - AssetDef, - Section, - SystemData -) +from buildingsync_asset_extractor.bae_types import Asset, AssetData, AssetDef, Section, SystemData from buildingsync_asset_extractor.errors import BSyncProcessorError from buildingsync_asset_extractor.formatters import Formatter from buildingsync_asset_extractor.lighting_processing.lighting_processing import ( LightingData, - process_buildings_lighting_systems + process_buildings_lighting_systems, ) # Gets or creates a logger @@ -68,24 +63,25 @@ # BuildingSync Schema location BUILDINGSYNC_SCHEMA_URL = "http://buildingsync.net/schemas/bedes-auc/2019" -DEFAULT_ASSETS_DEF_FILE = 'asset_definitions.json' # in package's config directory +DEFAULT_ASSETS_DEF_FILE = "asset_definitions.json" # in package's config directory # Processor class loads an XML file and extracts assets class BSyncProcessor: - - def __init__(self, - filename: Optional[Union[Path, str]] = None, - data: Optional[bytes] = None, - asset_defs_filename: Optional[str] = None, - logger_level: Optional[str] = 'INFO') -> None: + def __init__( + self, + filename: Optional[Union[Path, str]] = None, + data: Optional[bytes] = None, + asset_defs_filename: Optional[str] = None, + logger_level: Optional[str] = "INFO", + ) -> None: """class instantiator - :param filename: str, xml to parse - :param asset_defs_filename: Optional(str), asset definition abs filepath + :param filename: str, xml to parse + :param asset_defs_filename: Optional(str), asset definition abs filepath """ # set logger ll = logging.INFO - if logger_level == 'DEBUG': + if logger_level == "DEBUG": ll = logging.DEBUG logger.setLevel(ll) @@ -93,7 +89,7 @@ def __init__(self, if filename: logger.debug("Filename passed into BAE constructor") self.filename = filename - with open(self.filename, mode='rb') as file: + with open(self.filename, mode="rb") as file: self.file_data = file.read() elif data: logger.debug("Data passed into BAE constructor") @@ -113,19 +109,13 @@ def initialize_vars(self, asset_defs_filename: Optional[str]) -> None: if asset_defs_filename: self.config_filename = asset_defs_filename # open abs path - with open(self.config_filename, mode='rb') as f: - self.asset_defs = [ - AssetDef(**asset_def) - for asset_def in json.load(f)['asset_definitions'] - ] + with open(self.config_filename, mode="rb") as f: + self.asset_defs = [AssetDef(**asset_def) for asset_def in json.load(f)["asset_definitions"]] else: self.config_filename = DEFAULT_ASSETS_DEF_FILE # open with importlib.resources - file = files('buildingsync_asset_extractor.config').joinpath(self.config_filename).read_text() - self.asset_defs = [ - AssetDef(**asset_def) - for asset_def in json.loads(file)['asset_definitions'] - ] + file = files("buildingsync_asset_extractor.config").joinpath(self.config_filename).read_text() + self.asset_defs = [AssetDef(**asset_def) for asset_def in json.loads(file)["asset_definitions"]] self.namespaces: dict[str, str] = {} self.sections: dict[Any, Section] = {} # Section by ID @@ -140,126 +130,118 @@ def initialize_vars(self, asset_defs_filename: Optional[str]) -> None: def set_asset_defs_file(self, asset_defs_filename: Union[Path, str]) -> None: # set and parse self.config_filename = str(asset_defs_filename) - with open(self.config_filename, mode='rb') as file: - self.asset_defs = [ - AssetDef(**asset_def) - for asset_def in json.load(file)['asset_definitions'] - ] + with open(self.config_filename, mode="rb") as file: + self.asset_defs = [AssetDef(**asset_def) for asset_def in json.load(file)["asset_definitions"]] def get_asset_defs(self) -> list[AssetDef]: - """ return asset definitions array """ + """return asset definitions array""" return self.asset_defs def set_namespaces(self) -> None: """set namespaces from xml file""" context = etree.XML(self.file_data) - namespaces = context.xpath('//namespace::*') + namespaces = context.xpath("//namespace::*") for key, value in namespaces: - - # only register the namespace that matches BUILDGINSYNC_SCHEMA_URL - # NOTE: lxml/xpath is strange: if no named namespace, you have assign it a default + # only register the namespace that matches BUILDINGSYNC_SCHEMA_URL + # NOTE: lxml/xpath is strange: if no named namespace, you have to assign it a default # otherwise to matches are ever found in the schema with the xpath() method if value == BUILDINGSYNC_SCHEMA_URL: self.key = key - if self.key != '' and self.key is not None: - self.namespaces[key] = value - etree.register_namespace(key, value) - # also add the colon - self.key = self.key + ":" - else: - # make up a prefix since it's blank and assign it the key - key = 'auc' - self.namespaces[key] = value - etree.register_namespace(key, value) - self.key = 'auc:' + if not self.key: + # make up a prefix since it's blank + self.key = "auc" + + self.namespaces[self.key] = value + etree.register_namespace(self.key, value) + # also add the colon + self.key = f"{self.key}:" break - logger.debug("Namespaces set to: {}".format(self.namespaces)) + logger.debug(f"Namespaces set to: {self.namespaces}") if not namespaces: - raise BSyncProcessorError('No namespace was found in this file. Please modify your file and try again.') + raise BSyncProcessorError("No namespace was found in this file. Please modify your file and try again.") def get_namespaces(self) -> dict[str, str]: - """ return namespaces """ + """return namespaces""" return self.namespaces def get_doc(self) -> etree: - """ return parsed xml doc """ + """return parsed xml doc""" return self.doc def get_sections(self) -> dict[str, Section]: - """ return sections """ + """return sections""" return self.sections def get_assets(self) -> list[Asset]: - """ return asset data """ + """return asset data""" return self.asset_data.assets def save(self, filename: Union[Path, str]) -> None: - """ save assets data to JSON file - :param filename: str, filename to save + """save assets data to JSON file + :param filename: str, filename to save """ - with open(filename, 'w') as outfile: + with open(filename, "w") as outfile: json.dump(dataclasses.asdict(self.asset_data), outfile, indent=4) - logger.info('Assets saved to {}'.format(filename)) + logger.info(f"Assets saved to {filename}") def parse_xml(self) -> None: """parse xml file""" - self.doc: etree = etree.parse(BytesIO(self.file_data)) + self.doc: etree = etree.parse(BytesIO(self.file_data)) # noqa: S320 def convert_to_ns(self, path: str) -> str: - """ modify the path to include the namespace (ns) prefix specified in the xml file - :param path: str, xml xpath - returns modified path + """modify the path to include the namespace (ns) prefix specified in the xml file + :param path: str, xml xpath + returns modified path """ if self.key is None: raise BSyncProcessorError("key not set") - parts = path.split('/') + parts = path.split("/") for i, p in enumerate(parts): - if p != "" and p != ".": + if p not in ("", "."): parts[i] = self.key + p newpath = "/".join(parts) - if not newpath.startswith('/') and not newpath.startswith('.'): + if not newpath.startswith("/") and not newpath.startswith("."): newpath = self.key + newpath return newpath def process_sections(self) -> None: """process Sections to get sqft info to calculate primary and secondary sqft served - Grab breakdown within each section (Gross, Tenant, Unconditioned, etc) + Grab breakdown within each section (Gross, Tenant, Unconditioned, etc) """ # TODO: add try blocks - r = self.xp(self.doc, '/BuildingSync/Facilities/Facility/Sites/Site/' + - 'Buildings/Building/Sections/Section') + r = self.xp(self.doc, "/BuildingSync/Facilities/Facility/Sites/Site/" + "Buildings/Building/Sections/Section") for item in r: - id = item.get('ID') - self.sections[id] = Section( + item_id = item.get("ID") + self.sections[item_id] = Section( type=None, areas={}, ) - types = self.xp(item, './SectionType') + types = self.xp(item, "./SectionType") if types: - self.sections[id].type = types[0].text + self.sections[item_id].type = types[0].text - fas = self.xp(item, './FloorAreas/FloorArea') + fas = self.xp(item, "./FloorAreas/FloorArea") for fa in fas: fatype = None faval = 0.0 for child in fa: - if child.tag.endswith('FloorAreaType'): + if child.tag.endswith("FloorAreaType"): fatype = child.text - elif child.tag.endswith('FloorAreaValue'): + elif child.tag.endswith("FloorAreaValue"): faval = float(child.text) if fatype is not None: - self.sections[id].areas[fatype] = faval + self.sections[item_id].areas[fatype] = faval - logger.debug("Sections set to: {}".format(self.sections)) + logger.debug(f"Sections set to: {self.sections}") def extract(self) -> None: """extract and flatten assets data""" @@ -269,20 +251,20 @@ def extract(self) -> None: # process json file for asset in self.asset_defs: - logger.debug("...processing {}".format(asset.name)) - if 'sqft' in asset.type: + logger.debug(f"...processing {asset.name}") + if "sqft" in asset.type: self.process_sqft_asset(asset, asset.type) - elif asset.type == 'num': + elif asset.type == "num": self.process_count_asset(asset) - elif 'age' in asset.type: + elif "age" in asset.type: self.process_age_asset(asset, asset.type) - elif 'custom' in asset.type: + elif "custom" in asset.type: self.process_custom_asset(asset) - logger.debug('Assets: {}'.format(self.asset_data)) + logger.debug(f"Assets: {self.asset_data}") def export_asset(self, name: str, value: Any) -> None: - """ export asset to asset_data """ + """export asset to asset_data""" # first round if value is a float if isinstance(value, float): value = round(value, self.round_digits) @@ -290,13 +272,13 @@ def export_asset(self, name: str, value: Any) -> None: self.asset_data.assets.append(Asset(name, value)) def export_asset_units(self, name: str, value: Optional[str]) -> None: - """ export an asset's units - append "Units" to name and save units name """ + """export an asset's units + append "Units" to name and save units name""" if value != "No units": - self.asset_data.assets.append(Asset(name=name + ' Units', value=value)) + self.asset_data.assets.append(Asset(name=name + " Units", value=value)) def get_units(self, results: Union[list[SystemData], list[LightingData]]) -> Optional[str]: - """ attempt to get units or return mixed if multiple units are listed """ + """attempt to get units or return mixed if multiple units are listed""" units = None if len(results) > 0: if isinstance(results[0], SystemData) and results[0].units is not None: @@ -304,7 +286,7 @@ def get_units(self, results: Union[list[SystemData], list[LightingData]]) -> Opt for res in results: if isinstance(res, SystemData) and res.units is not None and res.units != units: # export "mixed" since we can't convert units, no units - units = 'mixed' + units = "mixed" return units def get_plant(self, item: ElementTree) -> Optional[ElementTree]: @@ -312,10 +294,10 @@ def get_plant(self, item: ElementTree) -> Optional[ElementTree]: plant = None the_type = self.get_heat_cool_type(item.tag) if the_type is not None: - plantIDmatch = self.xp(item, './/' + 'Source' + the_type + 'PlantID') - if len(plantIDmatch) > 0: - # logger.debug(f"found a plant ID match: {plantIDmatch[0].attrib['IDref']}") - plants = self.xp(self.doc, "//" + the_type + "Plant[@ID = '" + plantIDmatch[0].attrib['IDref'] + "']") + plant_id_match = self.xp(item, ".//" + "Source" + the_type + "PlantID") + if len(plant_id_match) > 0: + # logger.debug(f"found a plant ID match: {plant_id_match[0].attrib['IDref']}") + plants = self.xp(self.doc, "//" + the_type + "Plant[@ID = '" + plant_id_match[0].attrib["IDref"] + "']") # logger.debug(f"found {len(plants)} plant matches!") if len(plants) > 0: plant = plants[0] @@ -325,20 +307,20 @@ def get_plant(self, item: ElementTree) -> Optional[ElementTree]: def get_heat_cool_type(self, asset: str) -> Optional[str]: the_type = None logger.debug(f"GETTING HEAT COOL TYPE FOR ASSET: {asset}") - if 'Heating' in asset: - the_type = 'Heating' - if 'Cooling' in asset: - the_type = 'Cooling' + if "Heating" in asset: + the_type = "Heating" + if "Cooling" in asset: + the_type = "Cooling" return the_type def hvac_search(self, item: ElementTree, asset: AssetDef) -> list[ElementTree]: - """ Perform a 2-level search - 1. First look in HeatingAndCoolingSystems/Sources/Source - 2. If not there, look for a plant ID and look in there - Can be reused for several assets + """Perform a 2-level search + 1. First look in HeatingAndCoolingSystems/Sources/Source + 2. If not there, look for a plant ID and look in there + Can be reused for several assets """ # method 1: find within HeatingAndCoolingSystems or DomesticHotWaterSystems - matches = self.xp(item, './/' + asset.key) + matches = self.xp(item, ".//" + asset.key) # expects 0 or 1 match # logger.debug(f"number of matches for {item}: {len(matches)}") if len(matches) == 0: @@ -346,40 +328,40 @@ def hvac_search(self, item: ElementTree, asset: AssetDef) -> list[ElementTree]: plant = self.get_plant(item) if plant is not None: # now get asset key within this element - matches = self.xp(plant, './/' + asset.key) + matches = self.xp(plant, ".//" + asset.key) return matches def process_age_asset(self, asset: AssetDef, process_type: str) -> None: - """ retrieves, in order, either 'YearOfManufacture' or YearInstalled' element of an equipment type - returns either the oldest or newest, as specified. - for weighted average processing order: 1) installed power (not implemented), 2) capacity, 3) served space area + """retrieves, in order, either 'YearOfManufacture' or YearInstalled' element of an equipment type + returns either the oldest or newest, as specified. + for weighted average processing order: 1) installed power (not implemented), 2) capacity, 3) served space area """ results: list[SystemData] = [] items = self.xp(self.doc, asset.parent_path) for item in items: - matches = self.xp(item, './/' + asset.key) + matches = self.xp(item, ".//" + asset.key) for m in matches: # priority 1: YearOfManufacture - years = self.xp(m, './/YearOfManufacture') + years = self.xp(m, ".//YearOfManufacture") if len(years) == 0: # priority 2: YearInstalled - years = self.xp(m, './/YearInstalled') - if len(years) == 0: - # SPECIAL Case for HVAC HeatingSource / CoolingSource Equipment: can find plantID and look for - # YearInstalled there - if m.tag.endswith('HeatingSource') or m.tag.endswith('CoolingSource'): - plant = self.get_plant(m) - if plant is not None: - years = self.xp(plant, './/YearInstalled') + years = self.xp(m, ".//YearInstalled") + + # SPECIAL Case for HVAC HeatingSource / CoolingSource Equipment: can find plantID and look for + # YearInstalled there + if len(years) == 0 and (m.tag.endswith("HeatingSource") or m.tag.endswith("CoolingSource")): + plant = self.get_plant(m) + if plant is not None: + years = self.xp(plant, ".//YearInstalled") if years: match = years[0] res = SystemData( value=match.text, ) - if process_type.endswith('average'): + if process_type.endswith("average"): # check for capacity cap, cap_units = self.get_capacity(match) res.cap = cap @@ -388,7 +370,7 @@ def process_age_asset(self, asset: AssetDef, process_type: str) -> None: sqft_total = 0.0 # EEK this will vary wildly hvac_system = item.getparent().getparent().getparent() - linked_sections = self.xp(hvac_system, './/' + 'LinkedSectionID') + linked_sections = self.xp(hvac_system, ".//" + "LinkedSectionID") for ls in linked_sections: sqft_total += self.compute_sqft(ls) res.sqft = sqft_total @@ -404,16 +386,13 @@ def process_age_asset(self, asset: AssetDef, process_type: str) -> None: self.formatter.format_age_results(asset.export_name, results, process_type, units) def process_count_asset(self, asset: AssetDef) -> None: - """ process count asset """ + """process count asset""" # if there are keys, total is num of keys # else total is None items = self.xp(self.doc, asset.parent_path) - all_matches = [self.xp(item, './/' + asset.key) for item in items] + all_matches = [self.xp(item, ".//" + asset.key) for item in items] all_matches = [matches for matches in all_matches if matches] - if all_matches == []: - total = None - else: - total = sum([len(m) for m in all_matches]) + total = None if all_matches == [] else sum([len(m) for m in all_matches]) # set units units: Optional[str] = "No units" @@ -426,26 +405,23 @@ def process_count_asset(self, asset: AssetDef) -> None: self.export_asset_units(asset.export_name, units) def process_sqft_asset(self, asset: AssetDef, process_type: str) -> None: - """ process sqft asset - either a ranking by total sqft or a weighted average + """process sqft asset + either a ranking by total sqft or a weighted average """ results: dict[str, float] = {} items = self.xp(self.doc, asset.parent_path) for item in items: sqft_total = 0.0 - matches = self.xp(item, './/' + asset.key) + matches = self.xp(item, ".//" + asset.key) # special processing for UDFs - if asset.key.endswith('UserDefinedField'): + if asset.key.endswith("UserDefinedField"): matches = self.find_udf_values(matches, asset.name) for match in matches: # get asset label and initialize results array in not done already - if isinstance(match, str): - label = match - else: - label = match.text + label = match if isinstance(match, str) else match.text if label not in results: results[label] = 0.0 @@ -453,7 +429,7 @@ def process_sqft_asset(self, asset: AssetDef, process_type: str) -> None: results[label] += sqft_total # store results - logger.debug("process type: {}".format(process_type)) + logger.debug(f"process type: {process_type}") logger.debug(f"RESULTS for {asset.export_name}: {results}") # set units @@ -463,9 +439,9 @@ def process_sqft_asset(self, asset: AssetDef, process_type: str) -> None: if asset.units is not None: units = asset.units - if process_type == 'sqft': + if process_type == "sqft": self.formatter.format_sqft_results(asset.export_name, results, units) - elif process_type == 'avg_sqft': + elif process_type == "avg_sqft": self.formatter.format_avg_sqft_results(asset.export_name, results, units) def process_custom_asset(self, asset: AssetDef) -> None: @@ -474,9 +450,9 @@ def process_custom_asset(self, asset: AssetDef) -> None: # This function should contain all of the little variations # Functions downstream should be more generic name_of_units_field = { - 'AnnualHeatingEfficiency': 'AnnualHeatingEfficiencyUnits', - 'AnnualCoolingEfficiency': 'AnnualCoolingEfficiencyUnits', - 'WaterHeaterEfficiency': 'WaterHeaterEfficiencyType' + "AnnualHeatingEfficiency": "AnnualHeatingEfficiencyUnits", + "AnnualCoolingEfficiency": "AnnualCoolingEfficiencyUnits", + "WaterHeaterEfficiency": "WaterHeaterEfficiencyType", } # get name of units field to calculate @@ -486,69 +462,65 @@ def process_custom_asset(self, asset: AssetDef) -> None: units_to_export = name_of_units_field[asset.name] custom_assets: dict[str, Callable[[], Union[list[SystemData], list[LightingData]]]] = { - 'AnnualHeatingEfficiency': lambda: self.process_system(asset, units_to_export), - 'AnnualCoolingEfficiency': lambda: self.process_system(asset, units_to_export), - 'PrimaryFuel': lambda: self.process_system(asset, units_to_export), - 'ElectrificationPotential': lambda: self.process_system(asset, units_to_export), - 'WaterHeaterEfficiency': lambda: self.process_system(asset, units_to_export), - 'LightingSystemEfficiency': lambda: process_buildings_lighting_systems(self) + "AnnualHeatingEfficiency": lambda: self.process_system(asset, units_to_export), + "AnnualCoolingEfficiency": lambda: self.process_system(asset, units_to_export), + "PrimaryFuel": lambda: self.process_system(asset, units_to_export), + "ElectrificationPotential": lambda: self.process_system(asset, units_to_export), + "WaterHeaterEfficiency": lambda: self.process_system(asset, units_to_export), + "LightingSystemEfficiency": lambda: process_buildings_lighting_systems(self), } # these will get formated with the 80% function and lighting respectively (rest will use custom avg) - assets_80_percent = ['PrimaryFuel'] - assets_lighting = ['LightingSystemEfficiency'] + assets_80_percent = ["PrimaryFuel"] + assets_lighting = ["LightingSystemEfficiency"] results: Union[list[SystemData], list[LightingData]] if asset.name in custom_assets: results = custom_assets[asset.name]() else: logger.warn(f"Custom Processing for {asset.name} has not been implemented. Asset will be ignored.") - results = [] # type: ignore + results = [] # calculate actual units units: Optional[str] = "No units" if asset.export_units: - if asset.units is not None: - units = asset.units - else: - units = self.get_units(results) + units = asset.units if asset.units is not None else self.get_units(results) if asset.name in assets_80_percent: - self.formatter.format_80_percent_results(asset.export_name, results, units) # type: ignore + self.formatter.format_80_percent_results(asset.export_name, results, units) # type: ignore[arg-type] elif asset.name in assets_lighting: - self.formatter.format_lighting_results(asset.export_name, results, units) # type: ignore + self.formatter.format_lighting_results(asset.export_name, results, units) # type: ignore[arg-type] elif asset.name == "ElectrificationPotential": - self.formatter.format_electrification_pontential(asset.export_name, results, units) # type: ignore + self.formatter.format_electrification_potential(asset.export_name, results, units) # type: ignore[arg-type] else: - self.formatter.format_custom_avg_results(asset.export_name, results, units) # type: ignore + self.formatter.format_custom_avg_results(asset.export_name, results, units) # type: ignore[arg-type] def process_system(self, asset: AssetDef, units_keyname: Optional[str]) -> list[SystemData]: - """ Process Heating/Cooling and DomesticHotWater System Assets - order to check in: - 3) 1 SPECIAL CASE - Heating Efficiency: check under HeatingSource/HeatingSourceType/Furnace - and use ThermalEfficiency with units of "Thermal Efficiency" + """Process Heating/Cooling and DomesticHotWater System Assets + order to check in: + 3) 1 SPECIAL CASE - Heating Efficiency: check under HeatingSource/HeatingSourceType/Furnace + and use ThermalEfficiency with units of "Thermal Efficiency" """ results: list[SystemData] = [] matches = [] items = self.xp(self.doc, asset.parent_path) for item in items: - matches = self.hvac_search(item, asset) # special case for Efficiency (3rd priority) - if 'Efficiency' in asset.name and len(matches) == 0: + if "Efficiency" in asset.name and len(matches) == 0: # look for ThermalEfficiency under HeatingSourceType/ - matches = self.xp(item, './/' + 'ThermalEfficiency') + matches = self.xp(item, ".//" + "ThermalEfficiency") # this should be same for all methods for match in matches: units = None if units_keyname is not None and units_keyname != "No units": - unit_match = self.xp(match.getparent(), './/' + units_keyname) + unit_match = self.xp(match.getparent(), ".//" + units_keyname) if len(unit_match) > 0: units = unit_match[0].text - elif 'ThermalEfficiency' in match.tag: + elif "ThermalEfficiency" in match.tag: # special case for Heating Efficiency units = "Thermal Efficiency" @@ -564,16 +536,16 @@ def process_system(self, asset: AssetDef, units_keyname: Optional[str]) -> list[ units=units, cap=cap, cap_units=cap_units, - sqft=sqft_total - ) + sqft=sqft_total, + ), ) logger.debug(f"RESULTS for {asset.export_name}: {results}") return results def get_linked_section_sqft(self, item: ElementTree) -> float: - """ Find LinkedPremises at the right level (2 down from Systems) - and calculate total sqft from the sections returned + """Find LinkedPremises at the right level (2 down from Systems) + and calculate total sqft from the sections returned """ if self.key is None: raise BSyncProcessorError("key not set") @@ -581,22 +553,22 @@ def get_linked_section_sqft(self, item: ElementTree) -> float: sqft_total = 0.0 linked_sections = [] path = self.doc.getpath(item) - paths = path.split('/') + paths = path.split("/") # get sqft this asset applies to (2 methods) - if paths[-1].endswith('Section'): + if paths[-1].endswith("Section"): # 1: key is within a Section element # within a section, grab ID to retrieve sqft - id = item.get('ID') - sqft_total += self.retrieve_sqft(id) + item_id = item.get("ID") + sqft_total += self.retrieve_sqft(item_id) else: # 2: assume LinkedPremises section within this path # special case for Systems (recurse up to 2 levels past "Systems") - if 'Systems' in path: + if "Systems" in path: system = item - sys_idx = paths.index(self.key + 'Systems') if self.key + 'Systems' in paths else -1 + sys_idx = paths.index(self.key + "Systems") if self.key + "Systems" in paths else -1 # logger.debug(f"INDEX of Systems: {sys_idx}") if sys_idx > 0: # get_parent() recurse backwards to 2 levels past Systems @@ -605,10 +577,10 @@ def get_linked_section_sqft(self, item: ElementTree) -> float: system = system.getparent() logger.debug(f"Assuming linked premises is in element: {self.doc.getpath(system)}") - linked_sections = self.xp(system, './/' + 'LinkedSectionID') + linked_sections = self.xp(system, ".//" + "LinkedSectionID") else: - linked_sections = self.xp(item, './/' + 'LinkedSectionID') + linked_sections = self.xp(item, ".//" + "LinkedSectionID") for ls in linked_sections: sqft_total += self.compute_sqft(ls) @@ -616,7 +588,7 @@ def get_linked_section_sqft(self, item: ElementTree) -> float: return sqft_total def get_capacity(self, el: etree) -> Tuple[Optional[str], Optional[str]]: - """ Capacity order: + """Capacity order: 1) HVACSystem/HeatingAndCoolingSystems/HeatingSources/HeatingSource/Capacity and CapacityUnits 2) HVACSystem/HeatingAndCoolingSystems/HeatingSources/HeatingSource/OutputCapacity (deprecation soon) 3) HVACSystem/HeatingPlants/HeatingPlant//Capacity and CapacityUnits @@ -626,37 +598,37 @@ def get_capacity(self, el: etree) -> Tuple[Optional[str], Optional[str]]: cap_units = None path = el.getparent() # for ThermalEfficiency, need to go up 3 levels - if 'ThermalEfficiency' in el.tag: + if "ThermalEfficiency" in el.tag: path = path.getparent().getparent() # prefer Capacity over OutputCapacity as it will be deprecated - matches = self.xp(path, './/' + 'Capacity') + matches = self.xp(path, ".//" + "Capacity") if len(matches) == 0: - matches = self.xp(path, './/' + 'OutputCapacity') + matches = self.xp(path, ".//" + "OutputCapacity") if matches: # expects 0 or 1 for idx, match in enumerate(matches): cap = match.text - unit_match = self.xp(path, './/' + 'CapacityUnits') + unit_match = self.xp(path, ".//" + "CapacityUnits") if len(unit_match) > 0: cap_units = unit_match[0].text - elif 'ThermalEfficiency' in el.tag: - cap_units = 'Thermal Efficiency' + elif "ThermalEfficiency" in el.tag: + cap_units = "Thermal Efficiency" return cap, cap_units def find_udf_values(self, matches: list[ElementTree], name: str) -> list[Optional[str]]: - """ processes a list of UDF matches - retrieves the FieldValue whose FieldName matches the name passed in - returns an array of values + """processes a list of UDF matches + retrieves the FieldValue whose FieldName matches the name passed in + returns an array of values """ results = [] for match in matches: keep = 0 tmp_val = None for child in list(match): - if child.tag.endswith('FieldName') and child.text == name: + if child.tag.endswith("FieldName") and child.text == name: keep = 1 - if child.tag.endswith('FieldValue'): + if child.tag.endswith("FieldValue"): tmp_val = child.text if keep == 1: @@ -665,9 +637,9 @@ def find_udf_values(self, matches: list[ElementTree], name: str) -> list[Optiona return results def clean_name(self, name: str) -> str: - """ clean keyname """ - name = name.replace('HVAC', 'Hvac').replace(' ', '') - return re.sub(r'(? list[ElementTree]: """use xpath function and specify namespace @@ -677,40 +649,40 @@ def xp(self, element: ElementTree, path: str) -> list[ElementTree]: return element.xpath(newpath, namespaces=self.namespaces) def retrieve_sqft(self, section_id: str) -> float: - """ retrieves square footage given the sectionID - assumes 'Conditioned' if it exists; otherwise uses the 'Gross' floor area - returns square footage + """retrieves square footage given the sectionID + assumes 'Conditioned' if it exists; otherwise uses the 'Gross' floor area + returns square footage """ if section_id in self.sections: areas = self.sections[section_id].areas - if 'Conditioned' in areas: - return areas['Conditioned'] - elif 'Gross' in areas: - return areas['Gross'] + if "Conditioned" in areas: + return areas["Conditioned"] + elif "Gross" in areas: + return areas["Gross"] raise BSyncProcessorError( - 'Error retrieving section sqft...No Conditioned area or Gross area found for section {}'.format(section_id) + f"Error retrieving section sqft...No Conditioned area or Gross area found for section {section_id}", ) def compute_sqft(self, section: ElementTree) -> float: - """ compute square footage by either percentage or value method - returns sum of squarefootages the asset is applied to in the LinkedSection - the type of floor area to use in the calculation should be specified with "FloorAreaType" element. + """compute square footage by either percentage or value method + returns sum of squarefootages the asset is applied to in the LinkedSection + the type of floor area to use in the calculation should be specified with "FloorAreaType" element. """ - sid = section.get('IDref') + sid = section.get("IDref") sqft = 0.0 - floor_areas = self.xp(section, './/FloorAreas/FloorArea') + floor_areas = self.xp(section, ".//FloorAreas/FloorArea") for f in floor_areas: # get types and percentages and add to running total - the_type = self.xp(f, './/FloorAreaType')[0].text + the_type = self.xp(f, ".//FloorAreaType")[0].text # this could be percentage or value - percent = self.xp(f, './/FloorAreaPercentage') + percent = self.xp(f, ".//FloorAreaPercentage") if percent and the_type in self.sections[sid].areas: - logger.debug('type: {}, section: {}, areas: {}'.format(the_type, sid, self.sections[sid].areas)) + logger.debug(f"type: {the_type}, section: {sid}, areas: {self.sections[sid].areas}") sqft += float(percent[0].text) * self.sections[sid].areas[the_type] / 100 else: # get value instead - val = self.xp(f, './/FloorAreaValue') + val = self.xp(f, ".//FloorAreaValue") if val: sqft += float(val[0].text) @@ -719,21 +691,17 @@ def compute_sqft(self, section: ElementTree) -> float: @classmethod def get_default_asset_defs(cls) -> list[AssetDef]: assets_defs_filename = DEFAULT_ASSETS_DEF_FILE - file = files('buildingsync_asset_extractor.config').joinpath(assets_defs_filename).read_text() - return [ - AssetDef(**asset_def) - for asset_def in json.loads(file)['asset_definitions'] - ] - - def _get_user_defined_feilds(self, element: etree.Element) -> list[Tuple[str, str]]: - """Return (name, value) tuples of UserDefinedFields in element. - """ + file = files("buildingsync_asset_extractor.config").joinpath(assets_defs_filename).read_text() + return [AssetDef(**asset_def) for asset_def in json.loads(file)["asset_definitions"]] + + def _get_user_defined_fields(self, element: etree.Element) -> list[Tuple[str, str]]: + """Return (name, value) tuples of UserDefinedFields in element.""" res = [] - user_defined_feilds = self.xp(element, './/' + 'UserDefinedField') + user_defined_fields = self.xp(element, ".//" + "UserDefinedField") - for user_defined_feild in user_defined_feilds: - name = next(iter(self.xp(user_defined_feild, './/' + 'FieldName')), None) - value = next(iter(self.xp(user_defined_feild, './/' + 'FieldValue')), None) + for user_defined_field in user_defined_fields: + name = next(iter(self.xp(user_defined_field, ".//" + "FieldName")), None) + value = next(iter(self.xp(user_defined_field, ".//" + "FieldValue")), None) if name is not None and name.text and value is not None and value.text: res.append((name.text, value.text)) diff --git a/poetry.lock b/poetry.lock index 2e91374..db88deb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "atomicwrites" @@ -12,30 +12,43 @@ files = [ [[package]] name = "attrs" -version = "21.4.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" files = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] -docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] [[package]] name = "colorama" -version = "0.4.5" +version = "0.4.6" description = "Cross-platform colored terminal text." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] @@ -54,52 +67,83 @@ test = ["nose"] [[package]] name = "coverage" -version = "6.4.1" +version = "7.6.1" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, - {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"}, - {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"}, - {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"}, - {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"}, - {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"}, - {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"}, - {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"}, - {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"}, - {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"}, - {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"}, - {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"}, - {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"}, - {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"}, - {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"}, - {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, - {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [package.dependencies] @@ -108,6 +152,17 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + [[package]] name = "et-xmlfile" version = "1.1.0" @@ -119,183 +174,299 @@ files = [ {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, ] +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + +[[package]] +name = "identify" +version = "2.6.1" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "importlib-resources" -version = "5.8.0" +version = "5.13.0" description = "Read resources from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "importlib_resources-5.8.0-py3-none-any.whl", hash = "sha256:7952325ffd516c05a8ad0858c74dff2c3343f136fe66a6002b2623dd1d43f223"}, - {file = "importlib_resources-5.8.0.tar.gz", hash = "sha256:568c9f16cb204f9decc8d6d24a572eeea27dacbb4cee9e6b03a8025736769751"}, + {file = "importlib_resources-5.13.0-py3-none-any.whl", hash = "sha256:9f7bd0c97b79972a6cce36a366356d16d5e13b09679c11a58f1014bfdf8e64b2"}, + {file = "importlib_resources-5.13.0.tar.gz", hash = "sha256:82d5c6cca930697dbbd86c93333bb2c2e72861d4789a11c2662b933e5ad2b528"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jsonschema" -version = "4.17.3" +version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, ] [package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" [[package]] name = "lxml" -version = "4.9.1" +version = "5.3.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -files = [ - {file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"}, - {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"}, - {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc"}, - {file = "lxml-4.9.1-cp27-cp27m-win32.whl", hash = "sha256:86e92728ef3fc842c50a5cb1d5ba2bc66db7da08a7af53fb3da79e202d1b2cd3"}, - {file = "lxml-4.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4cfbe42c686f33944e12f45a27d25a492cc0e43e1dc1da5d6a87cbcaf2e95627"}, - {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dad7b164905d3e534883281c050180afcf1e230c3d4a54e8038aa5cfcf312b84"}, - {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a614e4afed58c14254e67862456d212c4dcceebab2eaa44d627c2ca04bf86837"}, - {file = "lxml-4.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f9ced82717c7ec65a67667bb05865ffe38af0e835cdd78728f1209c8fffe0cad"}, - {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:d9fc0bf3ff86c17348dfc5d322f627d78273eba545db865c3cd14b3f19e57fa5"}, - {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e5f66bdf0976ec667fc4594d2812a00b07ed14d1b44259d19a41ae3fff99f2b8"}, - {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fe17d10b97fdf58155f858606bddb4e037b805a60ae023c009f760d8361a4eb8"}, - {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8caf4d16b31961e964c62194ea3e26a0e9561cdf72eecb1781458b67ec83423d"}, - {file = "lxml-4.9.1-cp310-cp310-win32.whl", hash = "sha256:4780677767dd52b99f0af1f123bc2c22873d30b474aa0e2fc3fe5e02217687c7"}, - {file = "lxml-4.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a188cd292c4d2fcd78d04f863b789ef43aa129b233d7c9004de08693728b"}, - {file = "lxml-4.9.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:be9eb06489bc975c38706902cbc6888f39e946b81383abc2838d186f0e8b6a9d"}, - {file = "lxml-4.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f1be258c4d3dc609e654a1dc59d37b17d7fef05df912c01fc2e15eb43a9735f3"}, - {file = "lxml-4.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:927a9dd016d6033bc12e0bf5dee1dde140235fc8d0d51099353c76081c03dc29"}, - {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9232b09f5efee6a495a99ae6824881940d6447debe272ea400c02e3b68aad85d"}, - {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:04da965dfebb5dac2619cb90fcf93efdb35b3c6994fea58a157a834f2f94b318"}, - {file = "lxml-4.9.1-cp35-cp35m-win32.whl", hash = "sha256:4d5bae0a37af799207140652a700f21a85946f107a199bcb06720b13a4f1f0b7"}, - {file = "lxml-4.9.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4878e667ebabe9b65e785ac8da4d48886fe81193a84bbe49f12acff8f7a383a4"}, - {file = "lxml-4.9.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:1355755b62c28950f9ce123c7a41460ed9743c699905cbe664a5bcc5c9c7c7fb"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:bcaa1c495ce623966d9fc8a187da80082334236a2a1c7e141763ffaf7a405067"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eafc048ea3f1b3c136c71a86db393be36b5b3d9c87b1c25204e7d397cee9536"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:13c90064b224e10c14dcdf8086688d3f0e612db53766e7478d7754703295c7c8"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206a51077773c6c5d2ce1991327cda719063a47adc02bd703c56a662cdb6c58b"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e8f0c9d65da595cfe91713bc1222af9ecabd37971762cb830dea2fc3b3bb2acf"}, - {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0a4d179c9a941eb80c3a63cdb495e539e064f8054230844dcf2fcb812b71d3"}, - {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:830c88747dce8a3e7525defa68afd742b4580df6aa2fdd6f0855481e3994d391"}, - {file = "lxml-4.9.1-cp36-cp36m-win32.whl", hash = "sha256:1e1cf47774373777936c5aabad489fef7b1c087dcd1f426b621fda9dcc12994e"}, - {file = "lxml-4.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:5974895115737a74a00b321e339b9c3f45c20275d226398ae79ac008d908bff7"}, - {file = "lxml-4.9.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1423631e3d51008871299525b541413c9b6c6423593e89f9c4cfbe8460afc0a2"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:2aaf6a0a6465d39b5ca69688fce82d20088c1838534982996ec46633dc7ad6cc"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:9f36de4cd0c262dd9927886cc2305aa3f2210db437aa4fed3fb4940b8bf4592c"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae06c1e4bc60ee076292e582a7512f304abdf6c70db59b56745cca1684f875a4"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:57e4d637258703d14171b54203fd6822fda218c6c2658a7d30816b10995f29f3"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d279033bf614953c3fc4a0aa9ac33a21e8044ca72d4fa8b9273fe75359d5cca"}, - {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a60f90bba4c37962cbf210f0188ecca87daafdf60271f4c6948606e4dabf8785"}, - {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2264f341dd81e41f3fffecec6e446aa2121e0b8d026fb5130e02de1402785"}, - {file = "lxml-4.9.1-cp37-cp37m-win32.whl", hash = "sha256:27e590352c76156f50f538dbcebd1925317a0f70540f7dc8c97d2931c595783a"}, - {file = "lxml-4.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:eea5d6443b093e1545ad0210e6cf27f920482bfcf5c77cdc8596aec73523bb7e"}, - {file = "lxml-4.9.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f05251bbc2145349b8d0b77c0d4e5f3b228418807b1ee27cefb11f69ed3d233b"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:487c8e61d7acc50b8be82bda8c8d21d20e133c3cbf41bd8ad7eb1aaeb3f07c97"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d1a92d8e90b286d491e5626af53afef2ba04da33e82e30744795c71880eaa21"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b570da8cd0012f4af9fa76a5635cd31f707473e65a5a335b186069d5c7121ff2"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ef87fca280fb15342726bd5f980f6faf8b84a5287fcc2d4962ea8af88b35130"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:93e414e3206779ef41e5ff2448067213febf260ba747fc65389a3ddaa3fb8715"}, - {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6653071f4f9bac46fbc30f3c7838b0e9063ee335908c5d61fb7a4a86c8fd2036"}, - {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:32a73c53783becdb7eaf75a2a1525ea8e49379fb7248c3eeefb9412123536387"}, - {file = "lxml-4.9.1-cp38-cp38-win32.whl", hash = "sha256:1a7c59c6ffd6ef5db362b798f350e24ab2cfa5700d53ac6681918f314a4d3b94"}, - {file = "lxml-4.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:1436cf0063bba7888e43f1ba8d58824f085410ea2025befe81150aceb123e345"}, - {file = "lxml-4.9.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:4beea0f31491bc086991b97517b9683e5cfb369205dac0148ef685ac12a20a67"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:41fb58868b816c202e8881fd0f179a4644ce6e7cbbb248ef0283a34b73ec73bb"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bd34f6d1810d9354dc7e35158aa6cc33456be7706df4420819af6ed966e85448"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:edffbe3c510d8f4bf8640e02ca019e48a9b72357318383ca60e3330c23aaffc7"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d949f53ad4fc7cf02c44d6678e7ff05ec5f5552b235b9e136bd52e9bf730b91"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:079b68f197c796e42aa80b1f739f058dcee796dc725cc9a1be0cdb08fc45b000"}, - {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9c3a88d20e4fe4a2a4a84bf439a5ac9c9aba400b85244c63a1ab7088f85d9d25"}, - {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4e285b5f2bf321fc0857b491b5028c5f276ec0c873b985d58d7748ece1d770dd"}, - {file = "lxml-4.9.1-cp39-cp39-win32.whl", hash = "sha256:ef72013e20dd5ba86a8ae1aed7f56f31d3374189aa8b433e7b12ad182c0d2dfb"}, - {file = "lxml-4.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:10d2017f9150248563bb579cd0d07c61c58da85c922b780060dcc9a3aa9f432d"}, - {file = "lxml-4.9.1-pp37-pypy37_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538747a9d7827ce3e16a8fdd201a99e661c7dee3c96c885d8ecba3c35d1032c"}, - {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0645e934e940107e2fdbe7c5b6fb8ec6232444260752598bc4d09511bd056c0b"}, - {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6daa662aba22ef3258934105be2dd9afa5bb45748f4f702a3b39a5bf53a1f4dc"}, - {file = "lxml-4.9.1-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:603a464c2e67d8a546ddaa206d98e3246e5db05594b97db844c2f0a1af37cf5b"}, - {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c4b2e0559b68455c085fb0f6178e9752c4be3bba104d6e881eb5573b399d1eb2"}, - {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0f3f0059891d3254c7b5fb935330d6db38d6519ecd238ca4fce93c234b4a0f73"}, - {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c852b1530083a620cb0de5f3cd6826f19862bafeaf77586f1aef326e49d95f0c"}, - {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"}, - {file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"}, +python-versions = ">=3.6" +files = [ + {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, + {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7"}, + {file = "lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80"}, + {file = "lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3"}, + {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b"}, + {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be"}, + {file = "lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9"}, + {file = "lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1"}, + {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859"}, + {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d"}, + {file = "lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30"}, + {file = "lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f"}, + {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a"}, + {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b"}, + {file = "lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957"}, + {file = "lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d"}, + {file = "lxml-5.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237"}, + {file = "lxml-5.3.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577"}, + {file = "lxml-5.3.0-cp36-cp36m-win32.whl", hash = "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70"}, + {file = "lxml-5.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c"}, + {file = "lxml-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b"}, + {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5"}, + {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11"}, + {file = "lxml-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84"}, + {file = "lxml-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e"}, + {file = "lxml-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920"}, + {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945"}, + {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42"}, + {file = "lxml-5.3.0-cp38-cp38-win32.whl", hash = "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e"}, + {file = "lxml-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903"}, + {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de"}, + {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a"}, + {file = "lxml-5.3.0-cp39-cp39-win32.whl", hash = "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff"}, + {file = "lxml-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c"}, + {file = "lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] +html-clean = ["lxml-html-clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.7)"] +source = ["Cython (>=3.0.11)"] [[package]] name = "mypy" -version = "1.1.1" +version = "1.11.2" description = "Optional static typing for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mypy-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39c7119335be05630611ee798cc982623b9e8f0cff04a0b48dfc26100e0b97af"}, - {file = "mypy-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61bf08362e93b6b12fad3eab68c4ea903a077b87c90ac06c11e3d7a09b56b9c1"}, - {file = "mypy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbb19c9f662e41e474e0cff502b7064a7edc6764f5262b6cd91d698163196799"}, - {file = "mypy-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:315ac73cc1cce4771c27d426b7ea558fb4e2836f89cb0296cbe056894e3a1f78"}, - {file = "mypy-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5cb14ff9919b7df3538590fc4d4c49a0f84392237cbf5f7a816b4161c061829e"}, - {file = "mypy-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26cdd6a22b9b40b2fd71881a8a4f34b4d7914c679f154f43385ca878a8297389"}, - {file = "mypy-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b5f81b40d94c785f288948c16e1f2da37203c6006546c5d947aab6f90aefef2"}, - {file = "mypy-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21b437be1c02712a605591e1ed1d858aba681757a1e55fe678a15c2244cd68a5"}, - {file = "mypy-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d809f88734f44a0d44959d795b1e6f64b2bbe0ea4d9cc4776aa588bb4229fc1c"}, - {file = "mypy-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:a380c041db500e1410bb5b16b3c1c35e61e773a5c3517926b81dfdab7582be54"}, - {file = "mypy-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b7c7b708fe9a871a96626d61912e3f4ddd365bf7f39128362bc50cbd74a634d5"}, - {file = "mypy-1.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1c10fa12df1232c936830839e2e935d090fc9ee315744ac33b8a32216b93707"}, - {file = "mypy-1.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0a28a76785bf57655a8ea5eb0540a15b0e781c807b5aa798bd463779988fa1d5"}, - {file = "mypy-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ef6a01e563ec6a4940784c574d33f6ac1943864634517984471642908b30b6f7"}, - {file = "mypy-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d64c28e03ce40d5303450f547e07418c64c241669ab20610f273c9e6290b4b0b"}, - {file = "mypy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64cc3afb3e9e71a79d06e3ed24bb508a6d66f782aff7e56f628bf35ba2e0ba51"}, - {file = "mypy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce61663faf7a8e5ec6f456857bfbcec2901fbdb3ad958b778403f63b9e606a1b"}, - {file = "mypy-1.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b0c373d071593deefbcdd87ec8db91ea13bd8f1328d44947e88beae21e8d5e9"}, - {file = "mypy-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:2888ce4fe5aae5a673386fa232473014056967f3904f5abfcf6367b5af1f612a"}, - {file = "mypy-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:19ba15f9627a5723e522d007fe708007bae52b93faab00f95d72f03e1afa9598"}, - {file = "mypy-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:59bbd71e5c58eed2e992ce6523180e03c221dcd92b52f0e792f291d67b15a71c"}, - {file = "mypy-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9401e33814cec6aec8c03a9548e9385e0e228fc1b8b0a37b9ea21038e64cdd8a"}, - {file = "mypy-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b398d8b1f4fba0e3c6463e02f8ad3346f71956b92287af22c9b12c3ec965a9f"}, - {file = "mypy-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:69b35d1dcb5707382810765ed34da9db47e7f95b3528334a3c999b0c90fe523f"}, - {file = "mypy-1.1.1-py3-none-any.whl", hash = "sha256:4e4e8b362cdf99ba00c2b218036002bdcdf1e0de085cdb296a49df03fb31dfc4"}, - {file = "mypy-1.1.1.tar.gz", hash = "sha256:ae9ceae0f5b9059f33dbc62dea087e942c0ccab4b7a003719cb70f9b8abfa32f"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] -python2 = ["typed-ast (>=1.4.0,<2)"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] @@ -309,60 +480,142 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + [[package]] name = "numpy" -version = "1.26.4" +version = "2.0.2" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"}, + {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"}, + {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"}, + {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"}, + {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"}, + {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"}, + {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"}, + {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"}, + {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"}, + {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, +] + +[[package]] +name = "numpy" +version = "2.1.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab"}, + {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7"}, + {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6"}, + {file = "numpy-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0"}, + {file = "numpy-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647"}, + {file = "numpy-2.1.1-cp310-cp310-win32.whl", hash = "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728"}, + {file = "numpy-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95"}, + {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca"}, + {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf"}, + {file = "numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e"}, + {file = "numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2"}, + {file = "numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d"}, + {file = "numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521"}, + {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b"}, + {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201"}, + {file = "numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a"}, + {file = "numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313"}, + {file = "numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed"}, + {file = "numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0"}, + {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb"}, + {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df"}, + {file = "numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78"}, + {file = "numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556"}, + {file = "numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b"}, + {file = "numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468"}, + {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef"}, + {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f"}, + {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c"}, + {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39"}, + {file = "numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd"}, ] [[package]] name = "openpyxl" -version = "3.1.2" +version = "3.1.5" description = "A Python library to read/write Excel 2010 xlsx/xlsm files" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, - {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, + {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, + {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, ] [package.dependencies] @@ -370,54 +623,64 @@ et-xmlfile = "*" [[package]] name = "packaging" -version = "21.3" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - [[package]] name = "pandas" -version = "2.2.2" +version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, - {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, - {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, ] [package.dependencies] @@ -456,84 +719,63 @@ test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.9.2)"] [[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.8" files = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" +name = "pre-commit" +version = "3.8.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.6.8" +python-versions = ">=3.9" files = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, + {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, + {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, ] -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" [[package]] -name = "pyrsistent" -version = "0.20.0" -description = "Persistent/Functional/Immutable data structures" +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" optional = false -python-versions = ">=3.8" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ - {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"}, - {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"}, - {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"}, - {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"}, - {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"}, - {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"}, - {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"}, - {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"}, - {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"}, - {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"}, - {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"}, - {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"}, - {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"}, - {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"}, - {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"}, - {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"}, - {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"}, - {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"}, - {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"}, - {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"}, - {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"}, - {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"}, - {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"}, - {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"}, - {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"}, - {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"}, - {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"}, - {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"}, - {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"}, - {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"}, - {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"}, - {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"}, + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] [[package]] @@ -594,13 +836,229 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "rpds-py" +version = "0.20.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, +] + +[[package]] +name = "ruff" +version = "0.6.8" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"}, + {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"}, + {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"}, + {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"}, + {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"}, + {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"}, + {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"}, ] [[package]] @@ -643,42 +1101,66 @@ files = [ [[package]] name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] +[[package]] +name = "virtualenv" +version = "20.26.5" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, + {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + [[package]] name = "zipp" -version = "3.8.0" +version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, - {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] -docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] -testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "3fe02f24be1bbaa1ddbaddef7f22258850c55a8aa1a527381faeb9183a06b9f9" +python-versions = ">=3.9, <3.13" +content-hash = "6a2d8bddaae0a1f0b5588fe5543c0c5fb73ddd91f8c8bbde209cef54090213a5" diff --git a/pyproject.toml b/pyproject.toml index 63b8257..73085ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "buildingsync-asset-extractor" -version = "0.2.0" +version = "0.2.1" description = "BuildingSync Asset Extractor (BAE)" authors = [ "Katherine Fleming ", @@ -14,23 +14,31 @@ homepage = "https://buildingsync.net" repository = "https://github.com/BuildingSync/BuildingSync-asset-extractor" documentation = "https://github.com/BuildingSync/BuildingSync-asset-extractor" keywords = ["BuildingSync", "Building Data Exchange"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] [tool.poetry.dependencies] -python = "^3.9" -lxml = "4.9.1" -importlib-resources = "^5.7.0" +python = ">=3.9, <3.13" +lxml = "^5.3.0" +importlib-resources = "^5.13.0" styleframe = "^4.2" -pandas = "^2.2.0" +pandas = "^2.2.3" [tool.poetry.dev-dependencies] +mypy = "^1.11.2" +pre-commit = "^3.8.0" pytest = "7.1.2" pytest-cov = "3.0.0" - -[tool.poetry.group.dev.dependencies] -mypy = "^1.1.1" +ruff = "^0.6.7" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core>=1.9.0"] build-backend = "poetry.core.masonry.api" [tool.mypy] diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..9445151 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,67 @@ +fix = true +line-length = 140 + +[format] +# preview = true +docstring-code-format = true + +# https://docs.astral.sh/ruff/linter/#rule-selection +[lint] +# preview = true +# Enable these rules +extend-select = [ + "A", # flake8-builtins + "ARG", # flake8-unused-arguments + # "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "COM", # flake8-commas + # "DTZ", # flake8-datetimez + "E", # Error + "EXE", # flake8-executable + "F", # Pyflakes + "I", # isort + "ICN", # flake8-import-conventions + "ISC", # flake8-implicit-str-concat + "N", # pep8-naming + "PD", # pandas-vet + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PLC", # Pylint Convention + "PLE", # Pylint Error + "PLR", # Pylint Refactor + "PLW", # Pylint Warning + "PT", # flake8-pytest-style + "Q", # flake8-quotes + "RUF", # Ruff-specific rules + "S", # flake8-bandit + "SIM", # flake8-simplify + "T10", # flake8-debugger + "TID", # flake8-tidy-imports + "UP", # pyupgrade +] +# except for these specific errors +ignore = [ + "E402", # module-import-not-at-top-of-file + "E501", # line-too-long + "E731", # lambda-assignment + "PLR2004", # magic-value-comparison +] + +[lint.per-file-ignores] +"**/__init__.py" = [ + "F401", # unused-import +] +"tests/**/test_*" = [ + "S101", # assert +] + +#exclude = +# .tox +# .eggs +# build +# dist + +[lint.pylint] +# Raise the allowed limits the least possible amount https://docs.astral.sh/ruff/settings/#pylint-max-branches +max-statements = 58 +max-branches = 24 diff --git a/tests/cts/test_cts.py b/tests/cts/test_cts.py index e69c247..d80656a 100644 --- a/tests/cts/test_cts.py +++ b/tests/cts/test_cts.py @@ -9,9 +9,7 @@ from buildingsync_asset_extractor.cts.cts import aggregate_facilities BLANK_CTS_FILE_PATH = ( - Path(__file__).parents[2] / - "buildingsync_asset_extractor" / "cts" / - "CTS Comprehensive Evaluation Upload Template_20240312_021125.xlsx" + Path(__file__).parents[2] / "buildingsync_asset_extractor" / "cts" / "CTS Comprehensive Evaluation Upload Template_20240312_021125.xlsx" ) BLANK_CTS = pd.read_excel(BLANK_CTS_FILE_PATH, sheet_name="Evaluation Upload Template") @@ -32,7 +30,10 @@ def test_aggregate_facilities(self) -> None: def test_cts(self) -> None: # Action with tempfile.TemporaryDirectory() as temp_dir: - building_sync_to_cts([PRIMARYSCHOOL_1_FILE_PATH, PRIMARYSCHOOL_2_FILE_PATH, OFFICE_3_FILE_PATH], Path(temp_dir) / "output.xlsx") + building_sync_to_cts( + [PRIMARYSCHOOL_1_FILE_PATH, PRIMARYSCHOOL_2_FILE_PATH, OFFICE_3_FILE_PATH], + Path(temp_dir) / "output.xlsx", + ) populated_cts = pd.read_excel(Path(temp_dir) / "output.xlsx") # Assertion diff --git a/tests/lighting_processing/test_lighting_processing.py b/tests/lighting_processing/test_lighting_processing.py index 35bae16..2564d62 100644 --- a/tests/lighting_processing/test_lighting_processing.py +++ b/tests/lighting_processing/test_lighting_processing.py @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************************************* """ + import unittest from pathlib import Path @@ -41,13 +42,13 @@ from buildingsync_asset_extractor.lighting_processing.lighting_processing import ( LightingDataLPD, LightingDataPower, - process_buildings_lighting_systems + process_buildings_lighting_systems, ) from buildingsync_asset_extractor.processor import BSyncProcessor -SECTION_PATH = '/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building/Sections/Section' +SECTION_PATH = "/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building/Sections/Section" -linked_section_with_floor_area_percentage = etree.XML(''' +linked_section_with_floor_area_percentage = etree.XML(""" -''') +""") -linked_section_with_floor_area_value = etree.XML(''' +linked_section_with_floor_area_value = etree.XML(""" -''') +""") class TestBSyncProcessor(unittest.TestCase): def setUp(self) -> None: - self.testfile = Path(__file__).parents[1] / 'files' / 'completetest.xml' - self.no_ns_testfile = Path(__file__).parents[1] / 'files' / 'testfile2.xml' - self.output_dir = Path(__file__).parents[1] / 'output' - self.out_file = 'testoutput.json' - self.out_file_2 = 'testoutput_2.json' - self.test_assets_file = Path(__file__).parents[1] / 'files' / 'test_asset_defs.json' + self.testfile = Path(__file__).parents[1] / "files" / "completetest.xml" + self.no_ns_testfile = Path(__file__).parents[1] / "files" / "testfile2.xml" + self.output_dir = Path(__file__).parents[1] / "output" + self.out_file = "testoutput.json" + self.out_file_2 = "testoutput_2.json" + self.test_assets_file = Path(__file__).parents[1] / "files" / "test_asset_defs.json" self.num_assets_to_extract = 23 self.num_sections_in_testfile = 3 # create output dir self.output_dir.mkdir(parents=True, exist_ok=True) - print("TESTFILE: {}".format(self.testfile)) + print(f"TESTFILE: {self.testfile}") self.bp = BSyncProcessor(self.testfile) def test_process_lighting_method_1(self) -> None: @@ -104,7 +105,7 @@ def test_process_lighting_method_1(self) -> None: lighting_systems.remove(e) # add method 1 lighting system - method_1_ls = etree.XML(''' + method_1_ls = etree.XML(""" 1.0 2.0 @@ -114,11 +115,11 @@ def test_process_lighting_method_1(self) -> None: - ''') + """) lighting_systems.append(method_1_ls) # add sections to lighting system - linked_sections = self.bp.xp(method_1_ls, './/LinkedPremises/Section')[0] + linked_sections = self.bp.xp(method_1_ls, ".//LinkedPremises/Section")[0] linked_sections.append(linked_section_with_floor_area_percentage) linked_sections.append(linked_section_with_floor_area_value) @@ -150,7 +151,7 @@ def test_process_lighting_method_2_a(self) -> None: lighting_systems.remove(e) # add method 2 lighting system - method_2_ls = etree.XML(''' + method_2_ls = etree.XML(""" None: - ''') + """) lighting_systems.append(method_2_ls) # add sections to lighting system - linked_sections = self.bp.xp(method_2_ls, './/LinkedPremises/Section')[0] + linked_sections = self.bp.xp(method_2_ls, ".//LinkedPremises/Section")[0] linked_sections.append(linked_section_with_floor_area_percentage) linked_sections.append(linked_section_with_floor_area_value) @@ -200,7 +201,7 @@ def test_process_lighting_method_2_b(self) -> None: lighting_systems.remove(e) # add method 2 lighting system - method_2_ls = etree.XML(''' + method_2_ls = etree.XML(""" 2 3 @@ -208,34 +209,34 @@ def test_process_lighting_method_2_b(self) -> None: - ''') + """) lighting_systems.append(method_2_ls) # add user defined feilds to lighting system - good_field = etree.XML(''' + good_field = etree.XML(""" Quantity Of Luminaires For 2 - ''') - bad_name_feild = etree.XML(''' + """) + bad_name_feild = etree.XML(""" irrelevant 3 - ''') - bad_value_field = etree.XML(''' + """) + bad_value_field = etree.XML(""" Quantity Of Luminaires For bad value - ''') + """) method_2_ls.append(good_field) method_2_ls.append(bad_name_feild) method_2_ls.append(bad_value_field) # add sections to lighting system - linked_sections = self.bp.xp(method_2_ls, './/LinkedPremises/Section')[0] + linked_sections = self.bp.xp(method_2_ls, ".//LinkedPremises/Section")[0] linked_sections.append(linked_section_with_floor_area_percentage) linked_sections.append(linked_section_with_floor_area_value) @@ -267,40 +268,40 @@ def test_process_lighting_method_3(self) -> None: lighting_systems.remove(e) # add method 3 lighting system - method_3_ls = etree.XML(''' + method_3_ls = etree.XML(""" - ''') + """) lighting_systems.append(method_3_ls) # add user defined feilds to lighting system - good_field = etree.XML(''' + good_field = etree.XML(""" Lighting Power Density For 1 - ''') - bad_name_feild = etree.XML(''' + """) + bad_name_feild = etree.XML(""" irrelevant 2 - ''') - bad_value_field = etree.XML(''' + """) + bad_value_field = etree.XML(""" Lighting Power Density For bad value - ''') + """) method_3_ls.append(good_field) method_3_ls.append(bad_name_feild) method_3_ls.append(bad_value_field) # add sections to lighting system - linked_sections = self.bp.xp(method_3_ls, './/LinkedPremises/Section')[0] + linked_sections = self.bp.xp(method_3_ls, ".//LinkedPremises/Section")[0] linked_sections.append(linked_section_with_floor_area_percentage) linked_sections.append(linked_section_with_floor_area_value) @@ -326,7 +327,7 @@ def test_process_lighting_method_4_on_section(self) -> None: self.bp.process_sections() # get sections and clear it - section_path = '/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building/Sections' + section_path = "/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building/Sections" sections = self.bp.xp(self.bp.doc, section_path)[0] for s in sections: sections.remove(s) @@ -337,19 +338,21 @@ def test_process_lighting_method_4_on_section(self) -> None: for e in lighting_systems: lighting_systems.remove(e) - building_path = '/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building' + building_path = "/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building" building = self.bp.xp(self.bp.doc, building_path)[0] - building.append(etree.XML(''' + building.append( + etree.XML(""" Assembly-Convention center - ''')) + """), + ) # add section - section = etree.XML(''' + section = etree.XML(""" Auditorium - ''') + """) sections.append(section) # Action # @@ -363,7 +366,7 @@ def test_process_lighting_no_method_works(self) -> None: self.bp.process_sections() # get sections and clear it - section_path = '/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building/Sections' + section_path = "/BuildingSync/Facilities/Facility/Sites/Site/Buildings/Building/Sections" sections = self.bp.xp(self.bp.doc, section_path)[0] for s in sections: sections.remove(s) @@ -375,32 +378,34 @@ def test_process_lighting_no_method_works(self) -> None: lighting_systems.remove(e) # add section - section = etree.XML(''' + section = etree.XML(""" Bad Classification - ''') + """) sections.append(section) # empty lighting systems - lighting_system = etree.XML(''' + lighting_system = etree.XML(""" - ''') + """) lighting_systems.append(lighting_system) # add sections to lighting system - linked_sections = self.bp.xp(lighting_system, './/LinkedPremises/Section')[0] - linked_sections.append(etree.XML(''' + linked_sections = self.bp.xp(lighting_system, ".//LinkedPremises/Section")[0] + linked_sections.append( + etree.XML(""" - ''')) + """), + ) # Action # results = process_buildings_lighting_systems(self.bp) @@ -419,17 +424,17 @@ def test_process_lighting_method_4_on_lightin_section(self) -> None: lighting_systems.remove(e) # add method 1 lighting system - method_1_ls = etree.XML(''' + method_1_ls = etree.XML(""" - ''') + """) lighting_systems.append(method_1_ls) # add sections to lighting system - linked_sections = self.bp.xp(method_1_ls, './/LinkedPremises/Section')[0] + linked_sections = self.bp.xp(method_1_ls, ".//LinkedPremises/Section")[0] linked_sections.append(linked_section_with_floor_area_percentage) # Action # diff --git a/tests/test_buildingsync_asset_extractor.py b/tests/test_buildingsync_asset_extractor.py index 97f70f8..f95d8b5 100644 --- a/tests/test_buildingsync_asset_extractor.py +++ b/tests/test_buildingsync_asset_extractor.py @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************************************* """ + import unittest from pathlib import Path @@ -44,7 +45,7 @@ EMPTY_ASSET: Asset = Asset(name="", value=None) -hvac_system_1 = etree.XML(''' +hvac_system_1 = etree.XML(""" @@ -61,9 +62,9 @@ -''') +""") -hvac_system_2 = etree.XML(''' +hvac_system_2 = etree.XML(""" @@ -80,40 +81,40 @@ -''') +""") class TestBSyncProcessor(unittest.TestCase): def setUp(self) -> None: - self.testfile = Path(__file__).parent / 'files' / 'completetest.xml' - self.no_ns_testfile = Path(__file__).parent / 'files' / 'testfile2.xml' - self.output_dir = Path(__file__).parent / 'output' - self.out_file = 'testoutput.json' - self.out_file_2 = 'testoutput_2.json' - self.test_assets_file = Path(__file__).parent / 'files' / 'test_asset_defs.json' + self.testfile = Path(__file__).parent / "files" / "completetest.xml" + self.no_ns_testfile = Path(__file__).parent / "files" / "testfile2.xml" + self.output_dir = Path(__file__).parent / "output" + self.out_file = "testoutput.json" + self.out_file_2 = "testoutput_2.json" + self.test_assets_file = Path(__file__).parent / "files" / "test_asset_defs.json" self.num_assets_to_extract = 31 self.num_sections_in_testfile = 3 # create output dir self.output_dir.mkdir(parents=True, exist_ok=True) - print("TESTFILE: {}".format(self.testfile)) + print(f"TESTFILE: {self.testfile}") self.bp = BSyncProcessor(self.testfile) def test_initialize_and_parse(self) -> None: ns = self.bp.get_namespaces() - print('namespaces: {}'.format(ns)) - self.assertIn('auc', ns) + print(f"namespaces: {ns}") + assert "auc" in ns doc = self.bp.get_doc() - self.assertIsNotNone(doc) + assert doc is not None def test_return_asset_definitions(self) -> None: self.bp.set_asset_defs_file(self.test_assets_file) defs = self.bp.get_asset_defs() - self.assertEqual(len(defs), self.num_assets_to_extract - 10) + assert len(defs) == self.num_assets_to_extract - 10 - def test_extract(self) -> None: + def test_extract(self) -> None: # noqa: PLR0915 filename = self.output_dir / self.out_file_2 if filename.exists(): filename.unlink() @@ -121,114 +122,144 @@ def test_extract(self) -> None: self.bp.extract() sections = self.bp.get_sections() # print("SECTIONS: EMPTY_ASSET".format(sections)) - self.assertEqual(len(sections), self.num_sections_in_testfile) + assert len(sections) == self.num_sections_in_testfile assets = self.bp.get_assets() - print("ASSETS: {}".format(assets)) - self.assertEqual(len(assets), self.num_assets_to_extract) + print(f"ASSETS: {assets}") + assert len(assets) == self.num_assets_to_extract # test that assets of each type were calculated # CUSTOM - LSE: Asset = next((item for item in assets if item.name == "Lighting System Efficiency"), EMPTY_ASSET) - self.assertTrue(isinstance(LSE.value, float)) - self.assertLessEqual(LSE.value, 2) - LSEU: Asset = next((item for item in assets if item.name == "Lighting System Efficiency Units"), EMPTY_ASSET) - self.assertEqual(LSEU.value, 'W/ft2') - - HSE: Asset = next((item for item in assets if item.name == "Heating System Efficiency"), EMPTY_ASSET) - self.assertTrue(isinstance(HSE.value, float)) - self.assertLess(HSE.value, 70) - self.assertGreater(HSE.value, 69) - HSEU: Asset = next((item for item in assets if item.name == "Heating System Efficiency Units"), EMPTY_ASSET) - self.assertEqual(HSEU.value, 'Thermal Efficiency') - - HSAA: Asset = next((item for item in assets if item.name == "Heating System Average Age"), EMPTY_ASSET) - self.assertTrue(isinstance(HSAA.value, str)) - self.assertEqual(HSAA.value, '2010') - - HFT: Asset = next((item for item in assets if item.name == "Heating Fuel Type"), EMPTY_ASSET) - self.assertTrue(isinstance(HFT.value, str)) - self.assertEqual(HFT.value, 'Fuel oil no 1') - - CSE: Asset = next((item for item in assets if item.name == "Cooling System Efficiency"), EMPTY_ASSET) - self.assertTrue(isinstance(CSE.value, float)) - self.assertEqual(CSE.value, 3.0) - CSEU: Asset = next((item for item in assets if item.name == "Cooling System Efficiency Units"), EMPTY_ASSET) - self.assertEqual(CSEU.value, 'COP') - - WHE: Asset = next((item for item in assets if item.name == "Hot Water System Efficiency"), EMPTY_ASSET) - self.assertEqual(WHE.value, 'mixed') - - WHFT: Asset = next((item for item in assets if item.name == "Hot Water System Fuel Type"), EMPTY_ASSET) - self.assertEqual(WHFT.value, 'mixed') - - HEP: Asset = next((item for item in assets if item.name == "Heating Electrification Potential"), EMPTY_ASSET) - self.assertEqual(HEP.value, 202200.0) - - HEPU: Asset = next((item for item in assets if item.name == "Heating Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(HEPU.value, "kBtu/hr") - - DHW: Asset = next((item for item in assets if item.name == "Domestic HotWater System Electrification Potential"), EMPTY_ASSET) - self.assertEqual(DHW.value, 0) - - DHWU: Asset = next(( - item for item in assets if item.name == "Domestic HotWater System Electrification Potential Units"), - EMPTY_ASSET + lighting_system_efficiency: Asset = next((item for item in assets if item.name == "Lighting System Efficiency"), EMPTY_ASSET) + assert isinstance(lighting_system_efficiency.value, float) + assert lighting_system_efficiency.value <= 2 + lighting_system_efficiency_units: Asset = next( + (item for item in assets if item.name == "Lighting System Efficiency Units"), + EMPTY_ASSET, + ) + assert lighting_system_efficiency_units.value == "W/ft2" + + heating_system_efficiency: Asset = next((item for item in assets if item.name == "Heating System Efficiency"), EMPTY_ASSET) + assert isinstance(heating_system_efficiency.value, float) + assert heating_system_efficiency.value < 70 + assert heating_system_efficiency.value > 69 + heating_system_efficiency_units: Asset = next( + (item for item in assets if item.name == "Heating System Efficiency Units"), + EMPTY_ASSET, + ) + assert heating_system_efficiency_units.value == "Thermal Efficiency" + + heating_system_average_age: Asset = next((item for item in assets if item.name == "Heating System Average Age"), EMPTY_ASSET) + assert isinstance(heating_system_average_age.value, str) + assert heating_system_average_age.value == "2010" + + heating_fuel_type: Asset = next((item for item in assets if item.name == "Heating Fuel Type"), EMPTY_ASSET) + assert isinstance(heating_fuel_type.value, str) + assert heating_fuel_type.value == "Fuel oil no 1" + + cooling_system_efficiency: Asset = next((item for item in assets if item.name == "Cooling System Efficiency"), EMPTY_ASSET) + assert isinstance(cooling_system_efficiency.value, float) + assert cooling_system_efficiency.value == 3.0 + cooling_system_efficiency_units: Asset = next( + (item for item in assets if item.name == "Cooling System Efficiency Units"), + EMPTY_ASSET, + ) + assert cooling_system_efficiency_units.value == "COP" + + hot_water_system_efficiency: Asset = next((item for item in assets if item.name == "Hot Water System Efficiency"), EMPTY_ASSET) + assert hot_water_system_efficiency.value == "mixed" + + how_water_system_fuel_type: Asset = next((item for item in assets if item.name == "Hot Water System Fuel Type"), EMPTY_ASSET) + assert how_water_system_fuel_type.value == "mixed" + + heating_electrification_potential: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential"), + EMPTY_ASSET, + ) + assert heating_electrification_potential.value == 202200.0 + + heating_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential Units"), + EMPTY_ASSET, + ) + assert heating_electrification_potential_units.value == "kBtu/hr" + + domestic_hot_water: Asset = next( + (item for item in assets if item.name == "Domestic HotWater System Electrification Potential"), + EMPTY_ASSET, ) - self.assertEqual(DHWU.value, None) + assert domestic_hot_water.value == 0 - CEP: Asset = next((item for item in assets if item.name == "Cooling Electrification Potential"), EMPTY_ASSET) - self.assertEqual(CEP.value, 1000.0) + domestic_hot_water_units: Asset = next( + (item for item in assets if item.name == "Domestic HotWater System Electrification Potential Units"), + EMPTY_ASSET, + ) + assert domestic_hot_water_units.value is None - CEPU: Asset = next((item for item in assets if item.name == "Cooling Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(CEPU.value, "kBtu/hr") + cooling_electrification_potential: Asset = next( + (item for item in assets if item.name == "Cooling Electrification Potential"), + EMPTY_ASSET, + ) + assert cooling_electrification_potential.value == 1000.0 - CSEP: Asset = next((item for item in assets if item.name == "Cooking System Electrification Potential"), EMPTY_ASSET) - self.assertEqual(CSEP.value, 2400.0) + cooling_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Cooling Electrification Potential Units"), + EMPTY_ASSET, + ) + assert cooling_electrification_potential_units.value == "kBtu/hr" - CSEPU: Asset = next((item for item in assets if item.name == "Cooking System Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(CSEPU.value, "kBtu/hr") + cooking_system_electrification_potential: Asset = next( + (item for item in assets if item.name == "Cooking System Electrification Potential"), + EMPTY_ASSET, + ) + assert cooking_system_electrification_potential.value == 2400.0 + + cooking_system_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Cooking System Electrification Potential Units"), + EMPTY_ASSET, + ) + assert cooking_system_electrification_potential_units.value == "kBtu/hr" # count - cnt: Asset = next((item for item in assets if item.name == "Number of Lighting Systems"), EMPTY_ASSET) - self.assertTrue(isinstance(cnt.value, int)) - self.assertEqual(cnt.value, 2) + lighting_systems: Asset = next((item for item in assets if item.name == "Number of Lighting Systems"), EMPTY_ASSET) + assert isinstance(lighting_systems.value, int) + assert lighting_systems.value == 2 # avg_sqft # TODO: need to get better testfile with this information in it # avgHeat = next((item for item in assets if item.name == "Average Heating Setpoint"), None) # self.assertEqual(avgHeat.value, 71.5) # age - age: Asset = next((item for item in assets if item.name == "Heating System Oldest"), EMPTY_ASSET) - self.assertTrue(isinstance(age.value, str)) - self.assertEqual(age.value, '2010') + heating_system_oldest: Asset = next((item for item in assets if item.name == "Heating System Oldest"), EMPTY_ASSET) + assert isinstance(heating_system_oldest.value, str) + assert heating_system_oldest.value == "2010" filename = self.output_dir / self.out_file_2 self.bp.save(filename) def test_extract_data(self) -> None: - self.testfile = Path(__file__).parent / 'files' / 'testfile.xml' - with open(self.testfile, mode='rb') as file: + self.testfile = Path(__file__).parent / "files" / "testfile.xml" + with open(self.testfile, mode="rb") as file: file_data = file.read() self.bp2 = BSyncProcessor(data=file_data) self.bp2.set_asset_defs_file(self.test_assets_file) self.bp2.extract() sections = self.bp2.get_sections() - self.assertEqual(len(sections), self.num_sections_in_testfile) + assert len(sections) == self.num_sections_in_testfile assets = self.bp2.get_assets() - self.assertEqual(len(assets), self.num_assets_to_extract) + assert len(assets) == self.num_assets_to_extract # test 1 asset age: Asset = next((item for item in assets if item.name == "Heating System Oldest"), EMPTY_ASSET) - self.assertEqual(age.value, '2010') + assert age.value == "2010" def test_set_asset_defs(self) -> None: self.bp.set_asset_defs_file(self.test_assets_file) self.bp.extract() assets = self.bp.get_assets() - self.assertEqual(len(assets), self.num_assets_to_extract) + assert len(assets) == self.num_assets_to_extract def test_extract_no_ns(self) -> None: # test that assets from files without a namespace prefix can be extracted @@ -236,15 +267,15 @@ def test_extract_no_ns(self) -> None: self.bp2.set_asset_defs_file(self.test_assets_file) self.bp2.extract() sections = self.bp2.get_sections() - self.assertEqual(len(sections), self.num_sections_in_testfile) + assert len(sections) == self.num_sections_in_testfile assets = self.bp2.get_assets() - self.assertEqual(len(assets), self.num_assets_to_extract) + assert len(assets) == self.num_assets_to_extract # test that assets of each type were calculated # num cnt: Asset = next((item for item in assets if item.name == "Number of Lighting Systems"), EMPTY_ASSET) - self.assertTrue(isinstance(cnt.value, int)) - self.assertEqual(cnt.value, 2) + assert isinstance(cnt.value, int) + assert cnt.value == 2 # avg_sqft # TODO: need to get better testfile with this information in it # avgHeat = next((item for item in assets if item.name == "Average Heating Setpoint"), None) @@ -252,33 +283,36 @@ def test_extract_no_ns(self) -> None: # age age: Asset = next((item for item in assets if item.name == "Heating System Oldest"), EMPTY_ASSET) - self.assertEqual(age.value, '2010') + assert age.value == "2010" def test_save(self) -> None: filename = self.output_dir / self.out_file if filename.exists(): filename.unlink() self.bp.save(filename) - self.assertTrue(filename.exists()) + assert filename.exists() class TestElectrificationPotential(unittest.TestCase): def setUp(self) -> None: - self.testfile = Path(__file__).parent / 'files' / 'completetest.xml' - print("TESTFILE: {}".format(self.testfile)) + self.testfile = Path(__file__).parent / "files" / "completetest.xml" + print(f"TESTFILE: {self.testfile}") self.bp = BSyncProcessor(self.testfile) # only try and get heating EP - self.heating_source_path = "/BuildingSync/Facilities/Facility/Systems/" \ - "HVACSystems/HVACSystem/HeatingAndCoolingSystems/HeatingSources/HeatingSource" - self.bp.asset_defs = [AssetDef( - parent_path=self.heating_source_path, - key="PrimaryFuel", - name="ElectrificationPotential", - export_name="Heating Electrification Potential", - type="custom", - export_units=True, - )] + self.heating_source_path = ( + "/BuildingSync/Facilities/Facility/Systems/HVACSystems/HVACSystem/HeatingAndCoolingSystems/HeatingSources/HeatingSource" + ) + self.bp.asset_defs = [ + AssetDef( + parent_path=self.heating_source_path, + key="PrimaryFuel", + name="ElectrificationPotential", + export_name="Heating Electrification Potential", + type="custom", + export_units=True, + ), + ] # get hvac_systems and clear it hvac_systems_path = "/BuildingSync/Facilities/Facility/Systems/HVACSystems" @@ -297,10 +331,16 @@ def test_extract_heating_source_electrification_potential(self) -> None: # ASSERT assert len(assets) == 2 - HEP: Asset = next((item for item in assets if item.name == "Heating Electrification Potential"), EMPTY_ASSET) - self.assertEqual(HEP.value, 6.0) - HEPU: Asset = next((item for item in assets if item.name == "Heating Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(HEPU.value, "kBtu/hr") + heating_electrification_potential: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential"), + EMPTY_ASSET, + ) + assert heating_electrification_potential.value == 6.0 + heating_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential Units"), + EMPTY_ASSET, + ) + assert heating_electrification_potential_units.value == "kBtu/hr" def test_extract_heating_source_electrification_potential_different_export_unit(self) -> None: # SET UP @@ -316,14 +356,20 @@ def test_extract_heating_source_electrification_potential_different_export_unit( # ASSERT assert len(assets) == 2 - HEP: Asset = next((item for item in assets if item.name == "Heating Electrification Potential"), EMPTY_ASSET) - assert HEP.value == pytest.approx(1758.42642) - HEPU: Asset = next((item for item in assets if item.name == "Heating Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(HEPU.value, "W") + heating_electrification_potential: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential"), + EMPTY_ASSET, + ) + assert heating_electrification_potential.value == pytest.approx(1758.42642) + heating_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential Units"), + EMPTY_ASSET, + ) + assert heating_electrification_potential_units.value == "W" def test_extract_heating_source_electrification_potential_different_units(self) -> None: - # add in new ones with multipule heating sources. - hvac_system_1 = etree.XML(''' + # add in new ones with multiple heating sources. + hvac_system_1 = etree.XML(""" @@ -342,7 +388,7 @@ def test_extract_heating_source_electrification_potential_different_units(self) - ''') + """) self.hvac_systems.append(hvac_system_1) # ACTION @@ -351,10 +397,16 @@ def test_extract_heating_source_electrification_potential_different_units(self) # ASSERT assert len(assets) == 2 - HEP: Asset = next((item for item in assets if item.name == "Heating Electrification Potential"), EMPTY_ASSET) - assert HEP.value == pytest.approx(1.00682) - HEPU: Asset = next((item for item in assets if item.name == "Heating Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(HEPU.value, "kBtu/hr") + heating_electrification_potential: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential"), + EMPTY_ASSET, + ) + assert heating_electrification_potential.value == pytest.approx(1.00682) + heating_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential Units"), + EMPTY_ASSET, + ) + assert heating_electrification_potential_units.value == "kBtu/hr" def test_extract_heating_source_electrification_no_heating_sources(self) -> None: # ACTION @@ -363,15 +415,21 @@ def test_extract_heating_source_electrification_no_heating_sources(self) -> None # ASSERT assert len(assets) == 2 - HEP: Asset = next((item for item in assets if item.name == "Heating Electrification Potential"), EMPTY_ASSET) - self.assertEqual(HEP.value, None) - HEPU: Asset = next((item for item in assets if item.name == "Heating Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(HEPU.value, None) + heating_electrification_potential: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential"), + EMPTY_ASSET, + ) + assert heating_electrification_potential.value is None + heating_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential Units"), + EMPTY_ASSET, + ) + assert heating_electrification_potential_units.value is None def test_extract_heating_source_electrification_all_electric_heating_sources(self) -> None: self.bp.asset_defs[0].units = "kBtu/hr" - hvac_system_1 = etree.XML(''' + hvac_system_1 = etree.XML(""" @@ -390,7 +448,7 @@ def test_extract_heating_source_electrification_all_electric_heating_sources(sel - ''') + """) self.hvac_systems.append(hvac_system_1) # ACTION @@ -399,7 +457,13 @@ def test_extract_heating_source_electrification_all_electric_heating_sources(sel # ASSERT assert len(assets) == 2 - HEP: Asset = next((item for item in assets if item.name == "Heating Electrification Potential"), EMPTY_ASSET) - self.assertEqual(HEP.value, 0) - HEPU: Asset = next((item for item in assets if item.name == "Heating Electrification Potential Units"), EMPTY_ASSET) - self.assertEqual(HEPU.value, "kBtu/hr") + heating_electrification_potential: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential"), + EMPTY_ASSET, + ) + assert heating_electrification_potential.value == 0 + heating_electrification_potential_units: Asset = next( + (item for item in assets if item.name == "Heating Electrification Potential Units"), + EMPTY_ASSET, + ) + assert heating_electrification_potential_units.value == "kBtu/hr" diff --git a/tests/test_formatters.py b/tests/test_formatters.py index 774469c..d5521ab 100644 --- a/tests/test_formatters.py +++ b/tests/test_formatters.py @@ -15,9 +15,9 @@ def setUp(self) -> None: def test_format_80_percent_results(self) -> None: # SET UP - system_datas = \ - [SystemData(value='Fuel oil no 1', cap='20.0', cap_units='kBtu/hr')] + \ - [SystemData(value='Natural gas', cap='10.0', cap_units='kBtu/hr')] * 8 + system_datas = [SystemData(value="Fuel oil no 1", cap="20.0", cap_units="kBtu/hr")] + [ + SystemData(value="Natural gas", cap="10.0", cap_units="kBtu/hr"), + ] * 8 # ACTION self.formatter.format_80_percent_results("Heating Fuel Type", system_datas, None) @@ -28,9 +28,10 @@ def test_format_80_percent_results(self) -> None: def test_format_80_percent_results_different_units(self) -> None: # SET UP - system_datas = \ - [SystemData(value='Fuel oil no 1', cap='1', cap_units='Btu/hr')] + \ - [SystemData(value='Natural gas', cap='1', cap_units='kBtu/hr')] + system_datas = [ + SystemData(value="Fuel oil no 1", cap="1", cap_units="Btu/hr"), + SystemData(value="Natural gas", cap="1", cap_units="kBtu/hr"), + ] # ACTION self.formatter.format_80_percent_results("Heating Fuel Type", system_datas, None) @@ -41,9 +42,7 @@ def test_format_80_percent_results_different_units(self) -> None: def test_format_80_percent_results_use_sqft(self) -> None: # SET UP - system_datas = \ - [SystemData(value='Fuel oil no 1', sqft=20)] + \ - [SystemData(value='Natural gas', sqft=80)] + system_datas = [SystemData(value="Fuel oil no 1", sqft=20), SystemData(value="Natural gas", sqft=80)] # ACTION self.formatter.format_80_percent_results("Heating Fuel Type", system_datas, None) @@ -55,9 +54,9 @@ def test_format_80_percent_results_use_sqft(self) -> None: def test_format_custom_avg_results(self) -> None: # SET UP system_datas = [ - SystemData(value='1.0', cap='3.0', cap_units='kBtu/hr'), - SystemData(value='1.0', cap='2.0', cap_units='kBtu/hr'), - SystemData(value='3.0', cap='5.0', cap_units='kBtu/hr') + SystemData(value="1.0", cap="3.0", cap_units="kBtu/hr"), + SystemData(value="1.0", cap="2.0", cap_units="kBtu/hr"), + SystemData(value="3.0", cap="5.0", cap_units="kBtu/hr"), ] # ACTION @@ -70,9 +69,9 @@ def test_format_custom_avg_results(self) -> None: def test_format_custom_avg_different_units(self) -> None: # SET UP system_datas = [ - SystemData(value='1.0', cap='3.0', cap_units='kBtu/hr'), - SystemData(value='1.0', cap='2.0', cap_units='kBtu/hr'), - SystemData(value='3.0', cap='5.0', cap_units='W') + SystemData(value="1.0", cap="3.0", cap_units="kBtu/hr"), + SystemData(value="1.0", cap="2.0", cap_units="kBtu/hr"), + SystemData(value="3.0", cap="5.0", cap_units="W"), ] # ACTION