diff --git a/docs/docs/creating_and_uploading_studies.mdx b/docs/docs/creating_and_uploading_studies.mdx index 2cd4485..97f1976 100644 --- a/docs/docs/creating_and_uploading_studies.mdx +++ b/docs/docs/creating_and_uploading_studies.mdx @@ -17,16 +17,17 @@ Hence, you should ensure that both the servers are running for this example. Gui ## Getting Started -Ensure that you have installed the required dependencies and imported the libraries. For this example, we will use `json`, `geojson`, `zepben.eas` and `zepben.evolve` libraries. +Ensure that you have installed the required dependencies and imported the libraries. For this example, we will use `json`, `geojson`, `zepben.eas` and `zepben.ewb` libraries. Information about connecting to the Energy Workbench Server can be found in the [Connecting to the EWB Server](connecting-to-ewb.mdx) tutorial. ```python import json from geojson import Feature, LineString, FeatureCollection, Point -from zepben.eas import Study, Result, GeoJsonOverlay, EasClient -from zepben.evolve import connect_with_token, NetworkConsumerClient, AcLineSegment, EnergyConsumer -from zepben.protobuf.nc.nc_requests_pb2 import INCLUDE_ENERGIZED_LV_FEEDERS +from zepben.eas import StudyInput, StudyResultInput, GeoJsonOverlayInput, Mutation +from zepben.ewb import AcLineSegment, EnergyConsumer, connect_with_token, NetworkConsumerClient, IncludedEnergizedContainers + +from zepben.examples.utils import eas_client_from_config ``` ## Connecting to the EWB gRPC Service @@ -35,18 +36,30 @@ Here, we connect to the EWB Server, using the `connect_with_token` function. ```python with open("../config.json") as f: - c = json.loads(f.read()) + _config = json.loads(f.read()) + c_ewb = _config["ewb"] + c_eas = _config["eas"] -channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) +channel = connect_with_token(**c_ewb) ``` -You will need to replace the values in the config.json file with the appropriate configuration for your environment. The config file should look like this: +You will need to replace the values in the config.json file with the appropriate configuration for your environment. The config file has separate `ewb` and `eas` sections - each section's keys are passed straight through as kwargs to the matching client, so they must match its constructor args. It should look like this: ```json { - "host": "EWB Hostname", - "access_token": "your_access_token", - "rpc_port": 1234 + "ewb": { + "host": "EWB Hostname", + "rpc_port": 1234, + "ca_filename": null, + "access_token": "your_access_token" + }, + "eas": { + "host": "EAS Hostname", + "port": 1234, + "protocol": "https", + "ca_filename": null, + "access_token": "your_access_token" + } } ``` More information about connecting to the EWB Server using different methods can be found in the [Connecting to the EWB Server](connecting-to-ewb.mdx) tutorial. @@ -54,13 +67,13 @@ More information about connecting to the EWB Server using different methods can You will need to specify the mRID of the feeder that you want to fetch, ensuring that the feeder with the specified mRID exists. Then, create a `NetworkConsumerClient` and retrieve the network service. The `get_equipment_container` method is used to fetch information about the specified feeder (feeder_mrid). -The `INCLUDE_ENERGIZED_LV_FEEDERS` flag indicates that the client should include information about energized LV (Low Voltage) feeders in the response. +The `IncludedEnergizedContainers.LV_FEEDERS` flag indicates that the client should include information about energized LV (Low Voltage) feeders in the response. ```python feeder_mrid = "LV007" grpc_client = NetworkConsumerClient(grpc_channel) -await grpc_client.get_equipment_container(feeder_mrid, include_energized_containers=INCLUDE_ENERGIZED_LV_FEEDERS) +await grpc_client.get_equipment_container(feeder_mrid, include_energized_containers=IncludedEnergizedContainers.LV_FEEDERS) network = grpc_client.service ``` @@ -70,7 +83,7 @@ We use the `EasClient` to connect to the Evolve App Server. `EasClient` is a cla For the examples in this tutorial, we will use the following code to connect to the EAS. ```python -eas_client = EasClient(host=c["host"], port=c["rpc_port"], protocol="https", access_token=c["access_token"]) +eas_client = eas_client_from_config(c_eas, asynchronous=True) ``` - **host** - The domain of the Evolve App Server, e.g. "evolve.local" @@ -78,7 +91,7 @@ eas_client = EasClient(host=c["host"], port=c["rpc_port"], protocol="https", acc - **access_token** - The personal access token you generated from the UI. - **protocol** - The protocol to use for connecting to EAS. Either http or https. -Note: Ensure that the values for host, port, and token are correctly provided in the config.json file. +Note: Ensure that the values for host, port, and token are correctly provided in the `eas` section of the config.json file. ## Create Results for Energy Consumers Density Example @@ -107,7 +120,8 @@ ec_result = StudyResultInput( geoJsonOverlay=GeoJsonOverlayInput( data=FeatureCollection(ec_geojson), styles=["ec-heatmap"] # Select which Mapbox layers to show for this result - ) + ), + sections=[] ) ``` @@ -159,7 +173,8 @@ lv_lines_result = StudyResultInput( geoJsonOverlay=GeoJsonOverlayInput( data=FeatureCollection(lv_lines_geojson), styles=["lv-lines", "lv-lengths"] # Select which Mapbox layers to show for this result - ) + ), + sections=[] ) ``` @@ -228,7 +243,7 @@ The following styles are used for "lv_lines" and "lv_lengths". Note that you wil ## Create and Upload Studies Once you have created the Results for the studies, the next step is to create and upload the study on EAS. -A data class (`Study`) represents an Evolve App Server study. The user need to provide information such as name, descriptions, tags, results, and styles to successfully create a study. +A data class (`StudyInput`) represents an Evolve App Server study. The user need to provide information such as name, descriptions, tags, results, and styles to successfully create a study. ```python study = StudyInput( @@ -253,7 +268,9 @@ Note: Each layer may have an entry in the legend via the metadata["zb:legend"] f The final step after creating the study is to upload it on the EAS and close the EAS client, as follows. ```python -await eas_client.upload_study(study) +response = await eas_client.mutation(Mutation.add_studies([study])) +print(response) + await eas_client.close() ``` diff --git a/docs/docs/network_model_ingest.mdx b/docs/docs/network_model_ingest.mdx index 52e25be..ee59cd9 100644 --- a/docs/docs/network_model_ingest.mdx +++ b/docs/docs/network_model_ingest.mdx @@ -15,7 +15,7 @@ Folder naming and internal file formatting is customer-specific - Zepben will pr ## Part 2: Running the ingestion kick-off script -Once your model folder is in place, run the example script to trigger the ingest. It first connects to the Evolve App Server using an `EasClient`, built from the same `config.json` values (`eas_host`, `eas_port`, `eas_protocol`, `access_token`, etc.) used throughout the other EAS examples. See [Creating and Uploading Studies](creating_and_uploading_studies.mdx) for a breakdown of these connection values. +Once your model folder is in place, run the example script to trigger the ingest. It first connects to the Evolve App Server using an `EasClient`, built from the `eas` section of `config.json` (`host`, `port`, `protocol`, `access_token`, etc.) used throughout the other EAS examples. See [Creating and Uploading Studies](creating_and_uploading_studies.mdx) for a breakdown of these connection values. It then triggers the ingest via `Mutation.execute_ingestor`, passing an `IngestorConfigInput` with `key="dataStorePath"` and `value` set to your folder name from Part 1. `run_config` is a list, so more key/value pairs can be added if Zepben provides other ingestor configuration options for your environment. diff --git a/src/zepben/examples/__init__.py b/src/zepben/examples/__init__.py index 2db2a76..b207483 100644 --- a/src/zepben/examples/__init__.py +++ b/src/zepben/examples/__init__.py @@ -5,5 +5,6 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. from pathlib import Path -# root dir of repo if cloned -CONFIG_DIR = f"{Path(__file__).parent.parent.parent.parent}/" +# Directory this package lives in, i.e. src/zepben/examples, which is where config.json +# is expected to live (see utils.py::get_client and the various scripts that open "config.json"). +CONFIG_DIR = f"{Path(__file__).parent}/" diff --git a/src/zepben/examples/all_ratings_csv.py b/src/zepben/examples/all_ratings_csv.py index 6bee9fa..fc37759 100644 --- a/src/zepben/examples/all_ratings_csv.py +++ b/src/zepben/examples/all_ratings_csv.py @@ -30,10 +30,9 @@ class EquipmentWithRating: def _get_client(): with open('config.json') as f: - c = json.load(f) + c = json.load(f)['ewb'] - # Connect to server - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + channel = connect_with_token(**c) return NetworkConsumerClient(channel) diff --git a/src/zepben/examples/cim/extract_hv_customers.py b/src/zepben/examples/cim/extract_hv_customers.py index fa7b8b3..13e6ccb 100644 --- a/src/zepben/examples/cim/extract_hv_customers.py +++ b/src/zepben/examples/cim/extract_hv_customers.py @@ -11,11 +11,11 @@ from zepben.examples import CONFIG_DIR with open(f"{CONFIG_DIR}/config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] async def extract_hv_customers_for_feeder(feeder_mrid: str): - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"], ca_filename=c["ca_path"]) + channel = connect_with_token(**c) network_client = NetworkConsumerClient(channel=channel) network = network_client.service diff --git a/src/zepben/examples/cim/extract_lv_substations.py b/src/zepben/examples/cim/extract_lv_substations.py index 8f8c4a0..2f2b6e9 100644 --- a/src/zepben/examples/cim/extract_lv_substations.py +++ b/src/zepben/examples/cim/extract_lv_substations.py @@ -11,11 +11,11 @@ from zepben.examples import CONFIG_DIR with open(f"{CONFIG_DIR}/config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] async def extract_lv_substations_for_feeder(feeder_mrid: str): - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"], ca_filename=c["ca_path"]) + channel = connect_with_token(**c) network_client = NetworkConsumerClient(channel=channel) network = network_client.service diff --git a/src/zepben/examples/cim/extract_per_phase_wire_info.py b/src/zepben/examples/cim/extract_per_phase_wire_info.py index 27dc801..45cb420 100644 --- a/src/zepben/examples/cim/extract_per_phase_wire_info.py +++ b/src/zepben/examples/cim/extract_per_phase_wire_info.py @@ -11,11 +11,11 @@ from zepben.examples import CONFIG_DIR with open(f"{CONFIG_DIR}/config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] async def extract_wire_info_per_phase(feeder_mrid: str): - channel = connect_with_token(host=c["host"], rpc_port=c["rpc_port"], access_token=c["access_token"], ca_filename=c["ca_path"]) + channel = connect_with_token(**c) network_client = NetworkConsumerClient(channel=channel) network = network_client.service (await network_client.get_equipment_container(feeder_mrid, diff --git a/src/zepben/examples/connecting_to_grpc_service.py b/src/zepben/examples/connecting_to_grpc_service.py index 3122df8..be4c357 100644 --- a/src/zepben/examples/connecting_to_grpc_service.py +++ b/src/zepben/examples/connecting_to_grpc_service.py @@ -114,14 +114,10 @@ async def connect_using_token(): from zepben.ewb import connect_with_token, NetworkConsumerClient with open("config.json") as f: - c = json.load(f) + c = json.load(f)["ewb"] print("Connecting to EWB..") - channel = connect_with_token( - host=c["host"], - access_token=c["access_token"], - rpc_port=c["rpc_port"] - ) + channel = connect_with_token(**c) client = NetworkConsumerClient(channel) print("Connection established..") print("Printing network hierarchy..") diff --git a/src/zepben/examples/current_state_manipulations.py b/src/zepben/examples/current_state_manipulations.py index 4cd3cc1..b12d973 100644 --- a/src/zepben/examples/current_state_manipulations.py +++ b/src/zepben/examples/current_state_manipulations.py @@ -246,7 +246,7 @@ async def main(): # noinspection PyTypeChecker with open('config.json') as f: - config = json.load(f) + config = json.load(f)['ewb'] async with connect_with_token(**config) as secure_channel: await run_simple(NetworkConsumerClient(secure_channel)) await run_swap_feeder(NetworkConsumerClient(secure_channel)) diff --git a/src/zepben/examples/dsub_from_nmi.py b/src/zepben/examples/dsub_from_nmi.py index c7e5c0b..83a5ff3 100644 --- a/src/zepben/examples/dsub_from_nmi.py +++ b/src/zepben/examples/dsub_from_nmi.py @@ -20,7 +20,7 @@ with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] def _trace(start_item, results, stop_condition): @@ -44,7 +44,7 @@ def step_action(step: NetworkTraceStep, context: StepContext): async def main(mrid: str, feeder_mrid: str): - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + channel = connect_with_token(**c) client = NetworkConsumerClient(channel) await client.get_equipment_container(feeder_mrid, include_energized_containers=IncludedEnergizedContainers.LV_FEEDERS) network = client.service diff --git a/src/zepben/examples/element_network_hierarchy.py b/src/zepben/examples/element_network_hierarchy.py index 5082ecb..34ab139 100644 --- a/src/zepben/examples/element_network_hierarchy.py +++ b/src/zepben/examples/element_network_hierarchy.py @@ -6,8 +6,9 @@ """ For every piece of `ConductingEquipment` downstream of a feeder, records a row with its equipment type, -nominal voltage, length (where applicable), and the nearest upstream fuse, distribution transformer, -regulator and breaker mrid — so an `element_id` can be related back to its measurement zones. +nominal voltage, length (where applicable), and the nearest upstream protection device (fuse or LV +circuit breaker), distribution transformer, regulator and breaker mrid — so an `element_id` can be +related back to its measurement zones. """ import asyncio import json @@ -24,7 +25,7 @@ def _get_client() -> NetworkConsumerClient: with open('config.json') as f: - config = json.load(f) + config = json.load(f)['ewb'] return NetworkConsumerClient(connect_with_token(**config)) @@ -35,7 +36,7 @@ def _build_row(equip: ConductingEquipment, upstream: dict) -> dict: 'element_type': type(equip).__name__, 'nominal_voltage': equip.base_voltage_value, 'length': getattr(equip, 'length', None) if isinstance(equip, Conductor) else None, - 'upstream_fuse_mrid': getattr(upstream.get('upstream_fuse'), 'mrid', None), + 'upstream_protection_device_mrid': getattr(upstream.get('upstream_protection_device'), 'mrid', None), 'distribution_power_transformer_mrid': getattr(upstream.get('distribution_power_transformer'), 'mrid', None), 'regulator_mrid': getattr(upstream.get('regulator'), 'mrid', None), 'breaker_mrid': getattr(upstream.get('breaker'), 'mrid', None), @@ -63,8 +64,10 @@ def compute_next_value(self, next_item: NetworkTraceStep, current_item: NetworkT equip = next_item.path.to_equipment if isinstance(equip, Breaker): upstream['breaker'] = equip + if equip.base_voltage_value is not None and equip.base_voltage_value <= 1000: + upstream['upstream_protection_device'] = equip elif isinstance(equip, Fuse): - upstream['upstream_fuse'] = equip + upstream['upstream_protection_device'] = equip elif isinstance(equip, PowerTransformer): if equip.function == TransformerFunctionKind.distributionTransformer: upstream['distribution_power_transformer'] = equip diff --git a/src/zepben/examples/energy_consumer_device_hierarchy.py b/src/zepben/examples/energy_consumer_device_hierarchy.py index d39dfe0..02b60af 100644 --- a/src/zepben/examples/energy_consumer_device_hierarchy.py +++ b/src/zepben/examples/energy_consumer_device_hierarchy.py @@ -32,9 +32,8 @@ class EnergyConsumerDeviceHierarchy: def _get_client(): with open('config.json') as f: - config = json.load(f) + config = json.load(f)['ewb'] - # Connect to server channel = connect_with_token(**config) return NetworkConsumerClient(channel) diff --git a/src/zepben/examples/export_open_dss_model.py b/src/zepben/examples/export_open_dss_model.py index 2fcfb21..c3a9b9e 100644 --- a/src/zepben/examples/export_open_dss_model.py +++ b/src/zepben/examples/export_open_dss_model.py @@ -14,8 +14,10 @@ from time import sleep import requests +from zepben.examples.utils import eas_client_from_config + with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["eas"] async def get_opendss_model(eas_client, model_id: int): @@ -104,12 +106,7 @@ def download_generated_model(eas_client: EasClient, output_file_name: str, model async def open_dss_export(export_file_name: str): - eas_client = EasClient( - host=c["host"], - port=c["rpc_port"], - access_token=c["access_token"], - asynchronous=True - ) + eas_client = eas_client_from_config(c, asynchronous=True) # Run an opendss export print("Sending OpenDss model export request to EAS") diff --git a/src/zepben/examples/fetching_network_hierarchy.py b/src/zepben/examples/fetching_network_hierarchy.py index 9e504ca..19fe3bb 100644 --- a/src/zepben/examples/fetching_network_hierarchy.py +++ b/src/zepben/examples/fetching_network_hierarchy.py @@ -9,13 +9,13 @@ from zepben.ewb import connect_with_token, NetworkConsumerClient with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] async def main(): # See connecting_to_grpc_service.py for examples of each connect function print("Connecting to EWB..") - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + channel = connect_with_token(**c) client = NetworkConsumerClient(channel) print("Connection established..") # Fetch network hierarchy diff --git a/src/zepben/examples/fetching_network_model.py b/src/zepben/examples/fetching_network_model.py index 5196926..b798013 100644 --- a/src/zepben/examples/fetching_network_model.py +++ b/src/zepben/examples/fetching_network_model.py @@ -17,8 +17,8 @@ async def main(): # See connecting_to_grpc_service.py for examples of each connect function print("Connecting to EWB..") with open("config.json") as f: - c = json.loads(f.read()) - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + c = json.loads(f.read())["ewb"] + channel = connect_with_token(**c) feeder_mrid = "WD24" print(f"Fetching {feeder_mrid}") # Note you should create a new client for each Feeder you retrieve diff --git a/src/zepben/examples/find_isolation_section_from_equipment.py b/src/zepben/examples/find_isolation_section_from_equipment.py index 784dc1e..030aa2c 100644 --- a/src/zepben/examples/find_isolation_section_from_equipment.py +++ b/src/zepben/examples/find_isolation_section_from_equipment.py @@ -21,13 +21,9 @@ async def main(conductor_mrid: str, feeder_mrid: str): with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] - channel = connect_with_token( - host=c["host"], - access_token=c["access_token"], - rpc_port=c["rpc_port"] - ) + channel = connect_with_token(**c) client = NetworkConsumerClient(channel) await client.get_equipment_container( feeder_mrid, include_energized_containers=IncludedEnergizedContainers.LV_FEEDERS diff --git a/src/zepben/examples/id_csv_generator.py b/src/zepben/examples/id_csv_generator.py index 0664611..da2aeb5 100644 --- a/src/zepben/examples/id_csv_generator.py +++ b/src/zepben/examples/id_csv_generator.py @@ -16,8 +16,8 @@ from zepben.ewb import NetworkConsumerClient, connect_with_token, ConductingEquipment, Feeder, IncludedEnergizedContainers -with open("./config.json") as f: - c = json.loads(f.read()) +with open("config.json") as f: + c = json.loads(f.read())["ewb"] """ This is a basic example that shows how to export a CSV of all the conducting equipment in a feeder. @@ -26,7 +26,7 @@ async def connect(): - channel = connect_with_token(host=c["host"], rpc_port=c["rpc_port"], access_token=c["access_token"], ca_filename=c["ca_path"]) + channel = connect_with_token(**c) network_client = NetworkConsumerClient(channel=channel) network_hierarchy = (await network_client.get_network_hierarchy()).throw_on_error().value diff --git a/src/zepben/examples/isolation_equipment_between_nodes.py b/src/zepben/examples/isolation_equipment_between_nodes.py index 5b2be0c..864097e 100644 --- a/src/zepben/examples/isolation_equipment_between_nodes.py +++ b/src/zepben/examples/isolation_equipment_between_nodes.py @@ -22,13 +22,9 @@ async def main(mrids: Tuple[str, str], io_type: Type[ProtectedSwitch], feeder_mrid): with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] - channel = connect_with_token( - host=c["host"], - access_token=c["access_token"], - rpc_port=c["rpc_port"] - ) + channel = connect_with_token(**c) client = NetworkConsumerClient(channel) await client.get_equipment_container( feeder_mrid, diff --git a/src/zepben/examples/list_feeder_meter_ids.py b/src/zepben/examples/list_feeder_meter_ids.py new file mode 100644 index 0000000..2ae42d5 --- /dev/null +++ b/src/zepben/examples/list_feeder_meter_ids.py @@ -0,0 +1,66 @@ +# Copyright 2025 Zeppelin Bend Pty Ltd +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import asyncio +import csv +import json +import os + +from zepben.ewb import connect_with_token, NetworkConsumerClient, Meter, IncludedEnergizedContainers + + +def _get_client() -> NetworkConsumerClient: + with open('config.json') as f: + config = json.load(f)['ewb'] + + channel = connect_with_token(**config) + return NetworkConsumerClient(channel) + + +async def get_feeder_meter_ids(feeder_name_or_mrid: str, client: NetworkConsumerClient = None) -> list[str]: + """Fetch all Meters under the feeder identified by name or mrid, return their `name` (company_meter_id).""" + client = client or _get_client() + + hierarchy_response = await client.get_network_hierarchy() + hierarchy_response.throw_on_error() + hierarchy = hierarchy_response.result + feeder = hierarchy.feeders.get(feeder_name_or_mrid) + if feeder is None: + feeder = next((f for f in hierarchy.feeders.values() if f.name == feeder_name_or_mrid), None) + if feeder is None: + raise ValueError(f"No feeder found with mrid/name '{feeder_name_or_mrid}'") + + (await client.get_equipment_container( + feeder.mrid, + include_energized_containers=IncludedEnergizedContainers.LV_FEEDERS + )).throw_on_error() + + def _meter_id(meter: Meter) -> str: + # `name` holds the meter id when populated; otherwise it's embedded in the mrid as "-mt". + if meter.name: + return meter.name + return meter.mrid[:-3] if meter.mrid.endswith("-mt") else meter.mrid + + meter_ids = sorted({_meter_id(meter) for meter in client.service.objects(Meter)}) + return meter_ids + + +def write_csv(meter_ids: list[str], feeder_name_or_mrid: str) -> str: + os.makedirs("csvs", exist_ok=True) + path = f"csvs/{feeder_name_or_mrid}_meter_ids.csv" + with open(path, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(["meter_id"]) + for meter_id in meter_ids: + writer.writerow([meter_id]) + return path + + +if __name__ == "__main__": + FEEDER_NAME_OR_MRID = "" + ids = asyncio.run(get_feeder_meter_ids(FEEDER_NAME_OR_MRID)) + csv_path = write_csv(ids, FEEDER_NAME_OR_MRID) + print(f"Found {len(ids)} meter ids for feeder {FEEDER_NAME_OR_MRID}. Written to {csv_path}") diff --git a/src/zepben/examples/request_feeder_load_analysis_study.py b/src/zepben/examples/request_feeder_load_analysis_study.py index 089e8b5..42ecbe8 100644 --- a/src/zepben/examples/request_feeder_load_analysis_study.py +++ b/src/zepben/examples/request_feeder_load_analysis_study.py @@ -7,24 +7,18 @@ import json import sys -from zepben.eas import FeederLoadAnalysisInput, EasClient, Mutation +from zepben.eas import FeederLoadAnalysisInput, Mutation + +from zepben.examples.utils import eas_client_from_config with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["eas"] async def main(argv): # See connecting_to_grpc_service.py for examples of each connect function print("Connecting to EAS..") - eas_client = EasClient( - host=c["eas_host"], - port=c["eas_port"], - protocol=c["eas_protocol"], - access_token=c["access_token"], - verify_certificate=c.get("verify_certificate", True), - ca_filename=c["ca_path"], - asynchronous=True - ) + eas_client = eas_client_from_config(c, asynchronous=True) print("Connection established..") # Fire off a feeder load analysis study feeder_load_analysis_token = await eas_client.mutation( diff --git a/src/zepben/examples/request_network_model_ingest.py b/src/zepben/examples/request_network_model_ingest.py index 3d30914..51d5579 100644 --- a/src/zepben/examples/request_network_model_ingest.py +++ b/src/zepben/examples/request_network_model_ingest.py @@ -7,27 +7,21 @@ import json import sys -from zepben.eas import FeederLoadAnalysisInput, EasClient, Mutation, IngestorConfigInput +from zepben.eas import FeederLoadAnalysisInput, Mutation, IngestorConfigInput + +from zepben.examples.utils import eas_client_from_config # This example kicks off an ingest of a network model that has already been uploaded to blob storage. # See docs/docs/network_model_ingest.mdx for a full walkthrough, including how to prepare the model # files in blob storage before running this script. with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["eas"] async def main(argv): print("Connecting to EAS..") - eas_client = EasClient( - host=c["eas_host"], - port=c["eas_port"], - protocol=c["eas_protocol"], - access_token=c["access_token"], - verify_certificate=c.get("verify_certificate", True), - ca_filename=c["ca_path"], - asynchronous=True - ) + eas_client = eas_client_from_config(c, asynchronous=True) print("Connection established..") # Kick off a network model ingest. # diff --git a/src/zepben/examples/sample_auth_config.json b/src/zepben/examples/sample_auth_config.json deleted file mode 100644 index 824a871..0000000 --- a/src/zepben/examples/sample_auth_config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "eas_server": { - "host": "", - "port": 1234, - "protocol": "https", - "client_id": "", - "access_token": "", - "verify_certificate": false, - "ca_filename": null - } -} \ No newline at end of file diff --git a/src/zepben/examples/sample_config.json b/src/zepben/examples/sample_config.json index 751bdec..f925a00 100644 --- a/src/zepben/examples/sample_config.json +++ b/src/zepben/examples/sample_config.json @@ -1,5 +1,16 @@ { - "host": "hostname", - "rpc_port": 1234, - "access_token": "your_access_token" -} \ No newline at end of file + "_comment": "Copy this file to config.json (git-ignored) in the same directory and fill in real values. 'ewb' is used by EWB/NetworkConsumerClient examples (connect_with_token); 'eas' is used by EAS/EasClient examples. Each section's keys are passed straight through as **kwargs to that client, so the keys must match its constructor args. Do not add 'asynchronous' here - it's a per-script call choice, not a connection setting, and most scripts hardcode it anyway.", + "ewb": { + "host": "", + "rpc_port": 443, + "ca_filename": null, + "access_token": "" + }, + "eas": { + "host": "", + "port": 443, + "protocol": "https", + "ca_filename": null, + "access_token": "" + } +} diff --git a/src/zepben/examples/studies/creating_and_uploading_study.py b/src/zepben/examples/studies/creating_and_uploading_study.py index e8ec479..c0da45b 100644 --- a/src/zepben/examples/studies/creating_and_uploading_study.py +++ b/src/zepben/examples/studies/creating_and_uploading_study.py @@ -7,8 +7,10 @@ import json from geojson import Feature, LineString, FeatureCollection, Point -from zepben.eas import StudyInput, StudyResultInput, GeoJsonOverlayInput, EasClient, Mutation +from zepben.eas import StudyInput, StudyResultInput, GeoJsonOverlayInput, Mutation from zepben.ewb import AcLineSegment, EnergyConsumer, connect_with_token, NetworkConsumerClient, IncludedEnergizedContainers + +from zepben.examples.utils import eas_client_from_config # A study is a geographical visualisation of data that is drawn on top of the network. # This data is typically the result of a load flow simulation. # Each study may contain multiple results: different visualisations that the user may switch between. @@ -18,17 +20,15 @@ with open("../config.json") as f: - c = json.loads(f.read()) + _config = json.loads(f.read()) + c_ewb = _config["ewb"] + c_eas = _config["eas"] async def main(): # Fetch network model from Energy Workbench's gRPC service (see ../connecting_to_grpc_service.py for examples on different connection functions) print("Connecting to EWB..") - grpc_channel = connect_with_token( - host=c["host"], - access_token=c["access_token"], - rpc_port=c["rpc_port"] - ) + grpc_channel = connect_with_token(**c_ewb) feeder_mrid = "WD24" grpc_client = NetworkConsumerClient(grpc_channel) @@ -91,13 +91,7 @@ async def main(): ) print("Study created..") print("Connecting to EAS..") - eas_client = EasClient( - host=c["host"], - port=c["rpc_port"], - protocol="https", - access_token=c["access_token"], - asynchronous=True - ) + eas_client = eas_client_from_config(c_eas, asynchronous=True) print("Connection established..") diff --git a/src/zepben/examples/studies/suspect_end_of_line.py b/src/zepben/examples/studies/suspect_end_of_line.py index 85189a6..a805c56 100644 --- a/src/zepben/examples/studies/suspect_end_of_line.py +++ b/src/zepben/examples/studies/suspect_end_of_line.py @@ -17,9 +17,13 @@ NetworkConsumerClient, PhaseCode, PowerElectronicsConnection, Feeder, PowerSystemResource, Location, \ connect_with_token, NetworkTraceStep, Tracing, downstream, upstream, IncludedEnergizedContainers +from zepben.examples.utils import eas_client_from_config + with open("../config.json") as f: - c = json.loads(f.read()) + _config = json.loads(f.read()) + c_ewb = _config["ewb"] + c_eas = _config["eas"] def chunk(it, size): @@ -32,7 +36,7 @@ async def main(): zone_mrids = ["MTN"] print(f"Start time: {datetime.now()}") - rpc_channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + rpc_channel = connect_with_token(**c_ewb) client = NetworkConsumerClient(rpc_channel) hierarchy = (await client.get_network_hierarchy()).throw_on_error() substations = hierarchy.value.substations @@ -54,7 +58,7 @@ async def main(): all_traced_equipment = [] transformer_to_suspect_end = dict() - rpc_channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + rpc_channel = connect_with_token(**c_ewb) print(f"Processing feeders {', '.join(feeders)}") for feeder_mrid in feeders: futures.append(asyncio.ensure_future(fetch_feeder_and_trace(feeder_mrid, rpc_channel))) @@ -67,7 +71,7 @@ async def main(): print(f"Created Study for {len(feeder_mrids)} feeders") - eas_client = EasClient(host=c["host"], port=c["rpc_port"], protocol="https", access_token=c["access_token"], asynchronous=True) + eas_client = eas_client_from_config(c_eas, asynchronous=True) print(f"Uploading Study for {', '.join(feeders)} ...") await upload_suspect_end_of_line_study( diff --git a/src/zepben/examples/test.ipynb b/src/zepben/examples/test.ipynb index 4976b42..b9713ed 100644 --- a/src/zepben/examples/test.ipynb +++ b/src/zepben/examples/test.ipynb @@ -16,13 +16,13 @@ "from zepben.evolve import connect_with_token, NetworkConsumerClient\n", "\n", "with open(\"config.json\") as f:\n", - " c = json.loads(f.read())\n", + " c = json.loads(f.read())[\"ewb\"]\n", "\n", "\n", "async def main():\n", " # See connecting_to_grpc_service.py for examples of each connect function\n", " print(\"Connecting to EWB..\")\n", - " channel = connect_with_token(host=c[\"host\"], access_token=c[\"access_token\"], rpc_port=c[\"rpc_port\"])\n", + " channel = connect_with_token(**c)\n", " client = NetworkConsumerClient(channel)\n", " print(\"Connection established..\")\n", " # Fetch network hierarchy\n", @@ -50,4 +50,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/src/zepben/examples/tracing_conductor_type_by_lv_circuit.py b/src/zepben/examples/tracing_conductor_type_by_lv_circuit.py index 247cd8b..4d5792c 100644 --- a/src/zepben/examples/tracing_conductor_type_by_lv_circuit.py +++ b/src/zepben/examples/tracing_conductor_type_by_lv_circuit.py @@ -18,10 +18,10 @@ async def main(): with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] print("Connecting to Server") - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + channel = connect_with_token(**c) client = NetworkConsumerClient(channel) result = (await client.get_network_hierarchy()).throw_on_error().result diff --git a/src/zepben/examples/tracing_example.py b/src/zepben/examples/tracing_example.py index 6bb85b7..8a71cd0 100644 --- a/src/zepben/examples/tracing_example.py +++ b/src/zepben/examples/tracing_example.py @@ -17,10 +17,10 @@ async def main(): with open("config.json") as f: - c = json.loads(f.read()) + c = json.loads(f.read())["ewb"] print("Connecting to Server") - channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + channel = connect_with_token(**c) client = NetworkConsumerClient(channel) result = (await client.get_network_hierarchy()).throw_on_error().result diff --git a/src/zepben/examples/tx_id_to_name.py b/src/zepben/examples/tx_id_to_name.py index 05ca357..40dc824 100644 --- a/src/zepben/examples/tx_id_to_name.py +++ b/src/zepben/examples/tx_id_to_name.py @@ -18,12 +18,12 @@ OUTPUT_FILE = "transformer_id_mapping.csv" HEADER = True -with open("./config.json") as f: - c = json.loads(f.read()) +with open("config.json") as f: + c = json.loads(f.read())["ewb"] async def connect(): - channel = connect_with_token(host=c["host"], rpc_port=c["rpc_port"], access_token=c["access_token"], ca_filename=c["ca_path"]) + channel = connect_with_token(**c) network_client = NetworkConsumerClient(channel=channel) if os.path.exists(OUTPUT_FILE): diff --git a/src/zepben/examples/utils.py b/src/zepben/examples/utils.py index d5e0e5e..e64c8ba 100644 --- a/src/zepben/examples/utils.py +++ b/src/zepben/examples/utils.py @@ -13,7 +13,7 @@ logger = logging.getLogger() -# Default config dir is where the sample_auth_config sits. +# Default config dir is where config.json sits. def get_config_dir(argv): return argv[1] if len(argv) > 1 else "." @@ -25,16 +25,12 @@ def read_json_config(config_file_path: str) -> Dict: return config_dict +def eas_client_from_config(config: Dict, asynchronous: bool) -> EasClient: + config = {k: v for k, v in config.items() if k != "asynchronous"} + return EasClient(**config, asynchronous=asynchronous) + + def get_client(config_dir, async_=True): - # Change sample_auth_config.json to any other file name - auth_config = read_json_config(f"{config_dir}/sample_auth_config.json") - - return EasClient( - host=auth_config["eas_server"]["host"], - port=auth_config["eas_server"]["port"], - protocol=auth_config["eas_server"]["protocol"], - access_token=auth_config["eas_server"]["access_token"], - verify_certificate=auth_config["eas_server"].get("verify_certificate", True), - ca_filename=auth_config["eas_server"].get("ca_filename"), - asynchronous=async_ - ) + config = read_json_config(f"{config_dir}/config.json")["eas"] + config.update({"asynchronous": async_}) + return EasClient(**config)