Querying¶
We assume that you have generated a SDK for the Windmill
model and have a client ready to go.
Querying is a different way of retrieving data than doing .list()
, .retrieve()
, search()
in that it supports retrieving nested data structures.
SDKs generated by pygen
have two ways of querying which we will refer to as Python querying and GraphQL qurying. In this section, we will go through both of them,
and list limitations as well as when to use one over the other.
from windmill import WindmillClient
client = WindmillClient.from_toml("config.toml")
Comparing: Python and GraphQL based Querying¶
Python Based Querying¶
Advantages
- Discovarability through the IDE by using available docstrings.
- The returning data classes have required/optional set as in the data model, which are useful for static type checking and IDE auto complete.
Limitations
- You can only query along one chain of edges. For example, if we start from
Windmill
above we can either go to blades or metmast, not both. - You can only include direct relations at the end of the chain of edges. In the example above, we cannot get a windmill with rotor object, and blades with sensor positions.
GraphQL Based Querying¶
Advantages
- Flexiblity in querying. You can retrieve anything you can write up as a graphql query.
Limitations
- Difficult to write the querying, typically you would use the CDF UI to create the query.
- The returning data class has all properties as optional and is thus less structured.
Summary¶
GraphQL based querying is more flexible, but gives you less structure on the returning data classes. In addition, the query will have to be created outside of your IDE which requires context switching.
Python Based Querying¶
In Python based Querying, we chain together python methods call do filter (select) what instances we want to get. We finish with a .query()
which executes the query.
Querying along Edges¶
pygen
supports quering nodes via edges. This includes handeling circular dependencies.
For example, if we want to list the all the blades with sensor positoins we can do the followind query:
blades = client.blade(limit=5).sensor_positions(limit=-1).query()
blades[0].dump()
{'space': 'windmill-instances', 'externalId': 'blade:1', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'is_damaged': False, 'name': 'A', 'sensor_positions': [{'space': 'windmill-instances', 'externalId': 'sensorposition:1', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': 'V52-WindTurbine.MxA6', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 19.6}, {'space': 'windmill-instances', 'externalId': 'sensorposition:2', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA3_O', 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA3', 'flapwise_bend_mom': 'V52-WindTurbine.MxA3', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA3_O', 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.6}, {'space': 'windmill-instances', 'externalId': 'sensorposition:3', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA4cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA4_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA4_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA4', 'flapwise_bend_mom': 'V52-WindTurbine.MxA4', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA4cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA4_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA4_Ocr', 'position': 6.7}, {'space': 'windmill-instances', 'externalId': 'sensorposition:4', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': 'V52-WindTurbine.MxA5', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 13.1}, {'space': 'windmill-instances', 'externalId': 'sensorposition:5', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA1cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA1_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA1_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA1', 'flapwise_bend_mom': 'V52-WindTurbine.MxA1', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA1cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA1_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA1_Ocr', 'position': 1.4}, {'space': 'windmill-instances', 'externalId': 'sensorposition:6', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA2cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA2_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA2_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA2', 'flapwise_bend_mom': 'V52-WindTurbine.MxA2', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA2cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA2_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA2_Ocr', 'position': 1.5}]}
Call Explaination
The call client.blade(limit=5).sensor_positions(limit=-1).query()
requires some extra explaination.
- The first call,
.blade(limit=5)
, filters which blades that we want. - In the second call,
.sensor_positions(limit=-1)
, we specify that we want all sensor position edges connected to the blades we found in the first call. - The last call
.query()
tellspygen
to execute the query.
We can build arbitrary long queries this way and we always finish with .query()
to execute the query. For example, we can take all windmills at the 'Hornsea 1' windfarm and include all blades and sensor position connected to these blades.
windmills_at_hornsea_with_blades_and_sensors = client.windmill(windfarm="Hornsea 1").blades(limit=-1).sensor_positions(limit=-1).query()
windmills_at_hornsea_with_blades_and_sensors
space | external_id | blades | capacity | metmast | nacelle | name | rotor | windfarm | node_type | data_record | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | windmill-instances | hornsea_1_mill_3 | [{'space': 'windmill-instances', 'external_id'... | 7.0 | None | nacellewrite:1 | hornsea_1_mill_3 | rotorwrite:1 | Hornsea 1 | None | {'version': 2, 'last_updated_time': 2024-02-10... |
1 | windmill-instances | hornsea_1_mill_2 | [{'space': 'windmill-instances', 'external_id'... | 7.0 | None | nacellewrite:2 | hornsea_1_mill_2 | rotorwrite:2 | Hornsea 1 | None | {'version': 2, 'last_updated_time': 2024-02-10... |
2 | windmill-instances | hornsea_1_mill_1 | [{'space': 'windmill-instances', 'external_id'... | 7.0 | None | nacellewrite:3 | hornsea_1_mill_1 | rotorwrite:3 | Hornsea 1 | None | {'version': 2, 'last_updated_time': 2024-02-10... |
3 | windmill-instances | hornsea_1_mill_4 | [{'space': 'windmill-instances', 'external_id'... | 7.0 | None | nacellewrite:4 | hornsea_1_mill_4 | rotorwrite:4 | Hornsea 1 | None | {'version': 2, 'last_updated_time': 2024-02-10... |
4 | windmill-instances | hornsea_1_mill_5 | [{'space': 'windmill-instances', 'external_id'... | 7.0 | None | nacellewrite:5 | hornsea_1_mill_5 | rotorwrite:5 | Hornsea 1 | None | {'version': 2, 'last_updated_time': 2024-02-10... |
for windmill in windmills_at_hornsea_with_blades_and_sensors:
print(f"{windmill.name} has {len(windmill.blades)} blades")
for blade in windmill.blades:
print(f"{blade.external_id} is connected to {windmill.name} and has sensors: {', '.join([sensor.external_id for sensor in blade.sensor_positions])}")
print("\n")
hornsea_1_mill_3 has 6 blades blade:1 is connected to hornsea_1_mill_3 and has sensors: sensorposition:1, sensorposition:2, sensorposition:3, sensorposition:4, sensorposition:5, sensorposition:6 blade:2 is connected to hornsea_1_mill_3 and has sensors: sensorposition:7, sensorposition:8, sensorposition:9, sensorposition:10, sensorposition:11, sensorposition:12 blade:3 is connected to hornsea_1_mill_3 and has sensors: sensorposition:13, sensorposition:14, sensorposition:15, sensorposition:16, sensorposition:17, sensorposition:18 bladewrite:1 is connected to hornsea_1_mill_3 and has sensors: sensorpositionwrite:1, sensorpositionwrite:2, sensorpositionwrite:3, sensorpositionwrite:4, sensorpositionwrite:5, sensorpositionwrite:6 bladewrite:2 is connected to hornsea_1_mill_3 and has sensors: sensorpositionwrite:7, sensorpositionwrite:8, sensorpositionwrite:9, sensorpositionwrite:10, sensorpositionwrite:11, sensorpositionwrite:12 bladewrite:3 is connected to hornsea_1_mill_3 and has sensors: sensorpositionwrite:13, sensorpositionwrite:14, sensorpositionwrite:15, sensorpositionwrite:16, sensorpositionwrite:17, sensorpositionwrite:18 hornsea_1_mill_2 has 6 blades blade:4 is connected to hornsea_1_mill_2 and has sensors: sensorposition:19, sensorposition:20, sensorposition:21, sensorposition:22, sensorposition:23, sensorposition:24 blade:5 is connected to hornsea_1_mill_2 and has sensors: sensorposition:25, sensorposition:26, sensorposition:27, sensorposition:28, sensorposition:29, sensorposition:30 blade:6 is connected to hornsea_1_mill_2 and has sensors: sensorposition:31, sensorposition:32, sensorposition:33, sensorposition:34, sensorposition:35, sensorposition:36 bladewrite:4 is connected to hornsea_1_mill_2 and has sensors: sensorpositionwrite:19, sensorpositionwrite:20, sensorpositionwrite:21, sensorpositionwrite:22, sensorpositionwrite:23, sensorpositionwrite:24 bladewrite:5 is connected to hornsea_1_mill_2 and has sensors: sensorpositionwrite:25, sensorpositionwrite:26, sensorpositionwrite:27, sensorpositionwrite:28, sensorpositionwrite:29, sensorpositionwrite:30 bladewrite:6 is connected to hornsea_1_mill_2 and has sensors: sensorpositionwrite:31, sensorpositionwrite:32, sensorpositionwrite:33, sensorpositionwrite:34, sensorpositionwrite:35, sensorpositionwrite:36 hornsea_1_mill_1 has 6 blades blade:7 is connected to hornsea_1_mill_1 and has sensors: sensorposition:37, sensorposition:38, sensorposition:39, sensorposition:40, sensorposition:41, sensorposition:42 blade:8 is connected to hornsea_1_mill_1 and has sensors: sensorposition:43, sensorposition:44, sensorposition:45, sensorposition:46, sensorposition:47, sensorposition:48 blade:9 is connected to hornsea_1_mill_1 and has sensors: sensorposition:49, sensorposition:50, sensorposition:51, sensorposition:52, sensorposition:53, sensorposition:54 bladewrite:7 is connected to hornsea_1_mill_1 and has sensors: sensorpositionwrite:37, sensorpositionwrite:38, sensorpositionwrite:39, sensorpositionwrite:40, sensorpositionwrite:41, sensorpositionwrite:42 bladewrite:8 is connected to hornsea_1_mill_1 and has sensors: sensorpositionwrite:43, sensorpositionwrite:44, sensorpositionwrite:45, sensorpositionwrite:46, sensorpositionwrite:47, sensorpositionwrite:48 bladewrite:9 is connected to hornsea_1_mill_1 and has sensors: sensorpositionwrite:49, sensorpositionwrite:50, sensorpositionwrite:51, sensorpositionwrite:52, sensorpositionwrite:53, sensorpositionwrite:54 hornsea_1_mill_4 has 3 blades bladewrite:10 is connected to hornsea_1_mill_4 and has sensors: sensorpositionwrite:55, sensorpositionwrite:56, sensorpositionwrite:57, sensorpositionwrite:58, sensorpositionwrite:59, sensorpositionwrite:60 bladewrite:11 is connected to hornsea_1_mill_4 and has sensors: sensorpositionwrite:61, sensorpositionwrite:62, sensorpositionwrite:63, sensorpositionwrite:64, sensorpositionwrite:65, sensorpositionwrite:66 bladewrite:12 is connected to hornsea_1_mill_4 and has sensors: sensorpositionwrite:67, sensorpositionwrite:68, sensorpositionwrite:69, sensorpositionwrite:70, sensorpositionwrite:71, sensorpositionwrite:72 hornsea_1_mill_5 has 3 blades bladewrite:13 is connected to hornsea_1_mill_5 and has sensors: sensorpositionwrite:73, sensorpositionwrite:74, sensorpositionwrite:75, sensorpositionwrite:76, sensorpositionwrite:77, sensorpositionwrite:78 bladewrite:14 is connected to hornsea_1_mill_5 and has sensors: sensorpositionwrite:79, sensorpositionwrite:80, sensorpositionwrite:81, sensorpositionwrite:82, sensorpositionwrite:83, sensorpositionwrite:84 bladewrite:15 is connected to hornsea_1_mill_5 and has sensors: sensorpositionwrite:85, sensorpositionwrite:86, sensorpositionwrite:87, sensorpositionwrite:88, sensorpositionwrite:89, sensorpositionwrite:90
We can dig into the nested structure of one of them by dumping it do a dictinary.
windmills_at_hornsea_with_blades_and_sensors[1].dump()
{'space': 'windmill-instances', 'externalId': 'hornsea_1_mill_2', 'data_record': {'version': 2, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'blades': [{'space': 'windmill-instances', 'externalId': 'blade:4', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'is_damaged': True, 'name': 'B', 'sensor_positions': [{'space': 'windmill-instances', 'externalId': 'sensorposition:19', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 13.1}, {'space': 'windmill-instances', 'externalId': 'sensorposition:20', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyB2cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB2', 'flapwise_bend_mom': 'V52-WindTurbine.MxB2', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxB2cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.5}, {'space': 'windmill-instances', 'externalId': 'sensorposition:21', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 19.6}, {'space': 'windmill-instances', 'externalId': 'sensorposition:22', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyB3cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB3', 'flapwise_bend_mom': 'V52-WindTurbine.MxB3', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxB3cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.6}, {'space': 'windmill-instances', 'externalId': 'sensorposition:23', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB4', 'flapwise_bend_mom': 'V52-WindTurbine.MxB4', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.7}, {'space': 'windmill-instances', 'externalId': 'sensorposition:24', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyB1cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB1', 'flapwise_bend_mom': 'V52-WindTurbine.MxB1', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxB1cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.4}]}, {'space': 'windmill-instances', 'externalId': 'blade:5', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'is_damaged': False, 'name': 'C', 'sensor_positions': [{'space': 'windmill-instances', 'externalId': 'sensorposition:25', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 19.6}, {'space': 'windmill-instances', 'externalId': 'sensorposition:26', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC2cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC2', 'flapwise_bend_mom': 'V52-WindTurbine.MxC2', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC2cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.5}, {'space': 'windmill-instances', 'externalId': 'sensorposition:27', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 13.1}, {'space': 'windmill-instances', 'externalId': 'sensorposition:28', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC4cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC4', 'flapwise_bend_mom': 'V52-WindTurbine.MxC4', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC4cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.7}, {'space': 'windmill-instances', 'externalId': 'sensorposition:29', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC1cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC1', 'flapwise_bend_mom': 'V52-WindTurbine.MxC1', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC1cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.4}, {'space': 'windmill-instances', 'externalId': 'sensorposition:30', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC3cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC3', 'flapwise_bend_mom': 'V52-WindTurbine.MxC3', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC3cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.6}]}, {'space': 'windmill-instances', 'externalId': 'blade:6', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'is_damaged': False, 'name': 'A', 'sensor_positions': [{'space': 'windmill-instances', 'externalId': 'sensorposition:31', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA1cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA1_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA1_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA1', 'flapwise_bend_mom': 'V52-WindTurbine.MxA1', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA1cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA1_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA1_Ocr', 'position': 1.4}, {'space': 'windmill-instances', 'externalId': 'sensorposition:32', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': 'V52-WindTurbine.MxA6', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 19.6}, {'space': 'windmill-instances', 'externalId': 'sensorposition:33', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA3_O', 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA3', 'flapwise_bend_mom': 'V52-WindTurbine.MxA3', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA3_O', 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.6}, {'space': 'windmill-instances', 'externalId': 'sensorposition:34', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA2cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA2_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA2_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA2', 'flapwise_bend_mom': 'V52-WindTurbine.MxA2', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA2cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA2_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA2_Ocr', 'position': 1.5}, {'space': 'windmill-instances', 'externalId': 'sensorposition:35', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA4cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA4_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA4_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA4', 'flapwise_bend_mom': 'V52-WindTurbine.MxA4', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA4cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA4_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA4_Ocr', 'position': 6.7}, {'space': 'windmill-instances', 'externalId': 'sensorposition:36', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2023, 12, 25, 7, 47, 50, 40000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': 'V52-WindTurbine.MxA5', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 13.1}]}, {'space': 'windmill-instances', 'externalId': 'bladewrite:4', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'is_damaged': True, 'name': 'B', 'sensor_positions': [{'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:19', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 13.1}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:20', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyB2cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB2', 'flapwise_bend_mom': 'V52-WindTurbine.MxB2', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxB2cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.5}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:21', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 19.6}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:22', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyB3cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB3', 'flapwise_bend_mom': 'V52-WindTurbine.MxB3', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxB3cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.6}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:23', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB4', 'flapwise_bend_mom': 'V52-WindTurbine.MxB4', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.7}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:24', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyB1cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyB1', 'flapwise_bend_mom': 'V52-WindTurbine.MxB1', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxB1cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.4}]}, {'space': 'windmill-instances', 'externalId': 'bladewrite:5', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'is_damaged': False, 'name': 'C', 'sensor_positions': [{'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:25', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 19.6}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:26', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC2cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC2', 'flapwise_bend_mom': 'V52-WindTurbine.MxC2', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC2cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.5}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:27', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': None, 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 13.1}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:28', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC4cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC4', 'flapwise_bend_mom': 'V52-WindTurbine.MxC4', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC4cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.7}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:29', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC1cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC1', 'flapwise_bend_mom': 'V52-WindTurbine.MxC1', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC1cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 1.4}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:30', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyC3cr', 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyC3', 'flapwise_bend_mom': 'V52-WindTurbine.MxC3', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxC3cr', 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.6}]}, {'space': 'windmill-instances', 'externalId': 'bladewrite:6', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'is_damaged': False, 'name': 'A', 'sensor_positions': [{'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:31', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA1cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA1_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA1_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA1', 'flapwise_bend_mom': 'V52-WindTurbine.MxA1', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA1cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA1_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA1_Ocr', 'position': 1.4}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:32', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': 'V52-WindTurbine.MxA6', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 19.6}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:33', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA3_O', 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA3', 'flapwise_bend_mom': 'V52-WindTurbine.MxA3', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA3_O', 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 6.6}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:34', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA2cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA2_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA2_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA2', 'flapwise_bend_mom': 'V52-WindTurbine.MxA2', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA2cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA2_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA2_Ocr', 'position': 1.5}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:35', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MyA4cr', 'edgewise_bend_mom_offset': 'V52-WindTurbine.MyA4_O', 'edgewise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MyA4_Ocr', 'edgewisewise_bend_mom': 'V52-WindTurbine.MyA4', 'flapwise_bend_mom': 'V52-WindTurbine.MxA4', 'flapwise_bend_mom_crosstalk_corrected': 'V52-WindTurbine.MxA4cr', 'flapwise_bend_mom_offset': 'V52-WindTurbine.MxA4_O', 'flapwise_bend_mom_offset_crosstalk_corrected': 'V52-WindTurbine.MxA4_Ocr', 'position': 6.7}, {'space': 'windmill-instances', 'externalId': 'sensorpositionwrite:36', 'data_record': {'version': 1, 'last_updated_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'created_time': datetime.datetime(2024, 2, 10, 9, 30, 16, 893000, tzinfo=TzInfo(UTC)), 'deleted_time': None}, 'node_type': None, 'edgewise_bend_mom_crosstalk_corrected': None, 'edgewise_bend_mom_offset': None, 'edgewise_bend_mom_offset_crosstalk_corrected': None, 'edgewisewise_bend_mom': None, 'flapwise_bend_mom': 'V52-WindTurbine.MxA5', 'flapwise_bend_mom_crosstalk_corrected': None, 'flapwise_bend_mom_offset': None, 'flapwise_bend_mom_offset_crosstalk_corrected': None, 'position': 13.1}]}], 'capacity': 7.0, 'metmast': None, 'nacelle': 'nacellewrite:2', 'name': 'hornsea_1_mill_2', 'rotor': 'rotorwrite:2', 'windfarm': 'Hornsea 1'}
Notice that when we query we can easily get very large structures. So we should always use query with filtering options to only query the specific part of the data you have an interesst.
As a final example, lets get all windmill
s with damaged blades:
windmills = client.windmill(limit=-1).blades(is_damaged=True, limit=-1).query()
for windmill in windmills:
print(f"{windmill.external_id} has {len(windmill.blades or [])} damaged blades")
hornsea_1_mill_3 has 0 damaged blades hornsea_1_mill_2 has 2 damaged blades hornsea_1_mill_1 has 2 damaged blades hornsea_1_mill_4 has 2 damaged blades hornsea_1_mill_5 has 0 damaged blades windmill:demo has 1 damaged blades
Querying with Direct Relations¶
We can include the direct relations by specifing them on the .query()
call
windmills = client.windmill(limit=2).query(retrieve_nacelle=True, retrieve_rotor=True)
windmills[0].nacelle
value | |
---|---|
space | windmill-instances |
external_id | nacellewrite:1 |
data_record | {'version': 2, 'last_updated_time': 2024-02-10... |
node_type | None |
acc_from_back_side_x | V52-WindTurbine.Acc1N |
acc_from_back_side_y | V52-WindTurbine.Acc2N |
acc_from_back_side_z | V52-WindTurbine.Acc3N |
gearbox | gearboxwrite:1 |
generator | generatorwrite:1 |
high_speed_shaft | highspeedshaftwrite:1 |
main_shaft | mainshaftwrite:1 |
power_inverter | powerinverterwrite:1 |
yaw_direction | V52-WindTurbine.yaw |
yaw_error | V52-WindTurbine.YawErr |
GraphQL based Querying¶
When querying with GraphQL we must include __typename
of the top level items as this is used by pygen
to understand how to pase the object.
The querying method is available on the top level client as this is not particular to any of the data types in your data model
my_query = """{
listWindmill(first:1){
items{
__typename
name
capacity
nacelle{
externalId
}
rotor{
externalId
}
blades{
items{
name
is_damaged
}
}
}
}
}"""
result = client.graphql_query(my_query)
result
space | external_id | blades | capacity | metmast | nacelle | name | rotor | windfarm | data_record | |
---|---|---|---|---|---|---|---|---|---|---|
0 | None | None | [{'space': None, 'external_id': None, 'data_re... | 7.0 | None | {'space': None, 'external_id': 'nacellewrite:1... | hornsea_1_mill_3 | {'space': None, 'external_id': 'rotorwrite:1',... | None | None |
result[0].model_dump(exclude_none=True)
{'blades': [{'is_damaged': False, 'name': 'A'}, {'is_damaged': False, 'name': 'B'}, {'is_damaged': False, 'name': 'C'}, {'is_damaged': False, 'name': 'A'}, {'is_damaged': False, 'name': 'B'}, {'is_damaged': False, 'name': 'C'}], 'capacity': 7.0, 'nacelle': {'external_id': 'nacellewrite:1'}, 'name': 'hornsea_1_mill_3', 'rotor': {'external_id': 'rotorwrite:1'}}
turbine = result[0]
turbine.nacelle.external_id
'nacellewrite:1'
Pitfalls¶
If you forget to include __typename
on the top level item pygen
will raise a Runtime
error
my_invalid_query = """
{
listWindmill(first:1){
items{
name
}
}
}
"""
client.graphql_query(my_invalid_query)
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[23], line 1 ----> 1 client.graphql_query(my_invalid_query) File ~\Projects\internal\pygen\examples\windmill\_api_client.py:192, in WindmillClient.graphql_query(self, query, variables) 190 data_model_id = dm.DataModelId("power-models", "Windmill", "1") 191 result = self._client.data_modeling.graphql.query(data_model_id, query, variables) --> 192 return GraphQLQueryResponse(data_model_id).parse(result) File ~\Projects\internal\pygen\examples\windmill\_api\_core.py:768, in GraphQLQueryResponse.parse(self, response) 766 raise RuntimeError(response["errors"]) 767 _, data = list(response.items())[0] --> 768 self._parse_item(data) 769 return self._output File ~\Projects\internal\pygen\examples\windmill\_api\_core.py:774, in GraphQLQueryResponse._parse_item(self, data) 772 if "items" in data: 773 for item in data["items"]: --> 774 self._parse_item(item) 775 elif "__typename" in data: 776 try: File ~\Projects\internal\pygen\examples\windmill\_api\_core.py:783, in GraphQLQueryResponse._parse_item(self, data) 781 self._output.append(item) 782 else: --> 783 raise RuntimeError("Missing '__typename' in GraphQL response. Cannot determine the type of the response.") RuntimeError: Missing '__typename' in GraphQL response. Cannot determine the type of the response.
Data Classes¶
When you call .list()
, .retrieve()
and .search(),
pygen` returns ther read format of data classes. This read format matches the type/view required/optional properties.
When you do the graphql_query
above all properties are optional as pygen
cannot know which objects you included in your query, thus pygen
uses a special GraphQL format of the
data class it is returning
my_query = """{
listWindmill(first:1){
items{
name
__typename
}
}
}"""
result = client.graphql_query(my_query)
type(result)
windmill.data_classes._core.GraphQLList
type(result[0])
windmill.data_classes._windmill.WindmillGraphQL
This data class can be converted to a regular write or read format by calling the as_write
and as_read
call.
Warning If you have not included all required properties in the your GraphQL query, pygen
will raise an ValueError
when you do this call.
result[0].as_read()
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[29], line 1 ----> 1 windmill_read = result[0].as_read() File ~\Projects\internal\pygen\examples\windmill\data_classes\_windmill.py:104, in WindmillGraphQL.as_read(self) 102 """Convert this GraphQL format of windmill to the reading format.""" 103 if self.data_record is None: --> 104 raise ValueError("This object cannot be converted to a read format because it lacks a data record.") 105 return Windmill( 106 space=self.space, 107 external_id=self.external_id, (...) 121 windfarm=self.windfarm, 122 ) ValueError: This object cannot be converted to a read format because it lacks a data record.
my_query = """{
listWindmill(first:1){
items{
name
space
externalId
createdTime
lastUpdatedTime
__typename
}
}
}"""
result = client.graphql_query(my_query)
windmill_read = result[0].as_read()
windmill_read
value | |
---|---|
space | windmill-instances |
external_id | hornsea_1_mill_3 |
data_record | {'version': 0, 'last_updated_time': 2024-02-10... |
node_type | None |
blades | [] |
capacity | None |
metmast | [] |
nacelle | None |
name | hornsea_1_mill_3 |
rotor | None |
windfarm | None |
type(windmill_read)
windmill.data_classes._windmill.Windmill
windmill_write = result[0].as_write()
windmill_write
value | |
---|---|
space | windmill-instances |
external_id | hornsea_1_mill_3 |
data_record | {'existing_version': 0} |
node_type | None |
blades | [] |
capacity | None |
metmast | [] |
nacelle | None |
name | hornsea_1_mill_3 |
rotor | None |
windfarm | None |
type(windmill_write)
windmill.data_classes._windmill.WindmillWrite
Paging¶
If we include a pageInfo
in our query this will be available directly on the result returned from the .graphql_query
method
my_query = """
{
listWindmill{
items{
__typename
name
}
pageInfo{
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}"""
result = client.graphql_query(my_query)
result.page_info.has_next_page
True
result.page_info.end_cursor[:20]
'Z0FBQUFBQm1HV1hDRTE3'
Next section: Creating and Deleting