zepben.ewb 1.0.0b9__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. zepben/ewb/auth/client/zepben_token_fetcher.py +62 -42
  2. zepben/ewb/database/sqlite/common/base_cim_reader.py +7 -7
  3. zepben/ewb/database/sqlite/common/base_database_writer.py +2 -2
  4. zepben/ewb/database/sqlite/network/network_cim_reader.py +20 -20
  5. zepben/ewb/database/sqlite/network/network_database_reader.py +4 -2
  6. zepben/ewb/database/sqlite/tables/iec61968/common/table_documents.py +5 -5
  7. zepben/ewb/database/sqlite/tables/iec61968/common/table_street_addresses.py +1 -1
  8. zepben/ewb/database/sqlite/tables/iec61968/infiec61968/infassets/table_poles.py +1 -1
  9. zepben/ewb/database/sqlite/tables/iec61970/base/core/table_identified_objects.py +3 -3
  10. zepben/ewb/database/sqlite/tables/iec61970/base/core/table_power_system_resources.py +1 -1
  11. zepben/ewb/database/sqlite/tables/iec61970/base/meas/table_analogs.py +1 -1
  12. zepben/ewb/database/sqlite/tables/iec61970/base/wires/table_energy_consumers.py +1 -1
  13. zepben/ewb/database/sqlite/tables/iec61970/base/wires/table_energy_sources.py +1 -1
  14. zepben/ewb/database/sqlite/tables/iec61970/base/wires/table_regulating_cond_eq.py +1 -1
  15. zepben/ewb/database/sqlite/tables/iec61970/base/wires/table_shunt_compensators.py +1 -1
  16. zepben/ewb/database/sqlite/tables/iec61970/base/wires/table_tap_changers.py +1 -1
  17. zepben/ewb/database/sqlite/tables/iec61970/base/wires/table_transformer_ends.py +1 -1
  18. zepben/ewb/model/cim/iec61968/common/document.py +5 -5
  19. zepben/ewb/model/cim/iec61968/common/street_address.py +2 -2
  20. zepben/ewb/model/cim/iec61968/common/street_detail.py +8 -7
  21. zepben/ewb/model/cim/iec61968/infiec61968/infassets/pole.py +1 -1
  22. zepben/ewb/model/cim/iec61968/metering/meter.py +4 -2
  23. zepben/ewb/model/cim/iec61968/metering/usage_point.py +1 -1
  24. zepben/ewb/model/cim/iec61970/base/core/curve.py +2 -1
  25. zepben/ewb/model/cim/iec61970/base/core/identified_object.py +8 -3
  26. zepben/ewb/model/cim/iec61970/base/core/name_type.py +2 -2
  27. zepben/ewb/model/cim/iec61970/base/core/power_system_resource.py +2 -2
  28. zepben/ewb/model/cim/iec61970/base/meas/analog.py +3 -1
  29. zepben/ewb/model/cim/iec61970/base/wires/energy_consumer.py +1 -1
  30. zepben/ewb/model/cim/iec61970/base/wires/energy_source.py +1 -1
  31. zepben/ewb/model/cim/iec61970/base/wires/regulating_cond_eq.py +1 -1
  32. zepben/ewb/model/cim/iec61970/base/wires/shunt_compensator.py +1 -1
  33. zepben/ewb/model/cim/iec61970/base/wires/synchronous_machine.py +1 -1
  34. zepben/ewb/model/cim/iec61970/base/wires/tap_changer.py +1 -1
  35. zepben/ewb/model/cim/iec61970/base/wires/transformer_end.py +1 -1
  36. zepben/ewb/model/cim/iec61970/base/wires/transformer_star_impedance.py +4 -4
  37. zepben/ewb/services/common/translator/base_cim2proto.py +21 -9
  38. zepben/ewb/services/common/translator/base_proto2cim.py +13 -8
  39. zepben/ewb/services/customer/translator/customer_cim2proto.py +10 -10
  40. zepben/ewb/services/customer/translator/customer_proto2cim.py +9 -9
  41. zepben/ewb/services/diagram/translator/diagram_cim2proto.py +8 -8
  42. zepben/ewb/services/diagram/translator/diagram_proto2cim.py +8 -8
  43. zepben/ewb/services/network/tracing/phases/phase_inferrer.py +2 -2
  44. zepben/ewb/services/network/translator/network_cim2proto.py +451 -322
  45. zepben/ewb/services/network/translator/network_proto2cim.py +305 -287
  46. {zepben_ewb-1.0.0b9.dist-info → zepben_ewb-1.0.1.dist-info}/METADATA +4 -6
  47. {zepben_ewb-1.0.0b9.dist-info → zepben_ewb-1.0.1.dist-info}/RECORD +50 -50
  48. {zepben_ewb-1.0.0b9.dist-info → zepben_ewb-1.0.1.dist-info}/WHEEL +0 -0
  49. {zepben_ewb-1.0.0b9.dist-info → zepben_ewb-1.0.1.dist-info}/licenses/LICENSE +0 -0
  50. {zepben_ewb-1.0.0b9.dist-info → zepben_ewb-1.0.1.dist-info}/top_level.txt +0 -0
@@ -7,12 +7,13 @@
7
7
  __all__ = ["ZepbenTokenFetcher", "create_token_fetcher", "get_token_fetcher", "create_token_fetcher_managed_identity"]
8
8
 
9
9
  import warnings
10
+ from dataclasses import dataclass, Field, field, InitVar
10
11
  from datetime import datetime
11
- from typing import Optional, Union, Callable, Dict
12
+ from typing import Optional, Callable
12
13
 
13
14
  import jwt
14
15
  import requests
15
- from dataclassy import dataclass
16
+ from requests import Response
16
17
  from urllib3.exceptions import InsecureRequestWarning
17
18
 
18
19
  from zepben.ewb.auth.common.auth_exception import AuthException
@@ -21,11 +22,15 @@ from zepben.ewb.auth.common.auth_method import AuthMethod
21
22
  from zepben.ewb.auth.common.auth_provider_config import AuthProviderConfig, create_auth_provider_config, fetch_provider_details
22
23
 
23
24
 
24
- def _fetch_token_generator(is_entraid: bool, use_identity: bool, identity_url: Optional[str] = None) -> Callable[
25
- [Dict, Dict, str, bool, bool], requests.Response]:
25
+ def _fetch_token_generator(
26
+ is_entraid: bool,
27
+ use_identity: bool,
28
+ identity_url: Optional[str] = None
29
+ ) -> Callable[[dict, dict, str, Optional[bool], Optional[bool]], Response]:
30
+
26
31
  def post(
27
- refresh_request_data: Dict,
28
- token_request_data: Dict,
32
+ refresh_request_data: dict,
33
+ token_request_data: dict,
29
34
  token_endpoint: str,
30
35
  refresh: bool,
31
36
  verify: bool
@@ -48,7 +53,14 @@ def _fetch_token_generator(is_entraid: bool, use_identity: bool, identity_url: O
48
53
  verify=verify
49
54
  )
50
55
 
51
- def _get_token_response(refresh_request_data: Dict, token_request_data: Dict, token_endpoint: str, refresh: bool, verify: bool) -> requests.Response:
56
+ def _get_token_response(
57
+ refresh_request_data: dict,
58
+ token_request_data: dict,
59
+ token_endpoint: str,
60
+ refresh: bool,
61
+ verify: bool
62
+ ) -> requests.Response:
63
+
52
64
  refresh = not is_entraid and refresh # At the moment Azure auth doesn't support refresh tokens. So we always force new tokens.
53
65
 
54
66
  return post(
@@ -59,53 +71,59 @@ def _fetch_token_generator(is_entraid: bool, use_identity: bool, identity_url: O
59
71
  verify
60
72
  )
61
73
 
62
- def _get_token_response_from_identity(refresh_request_data: Dict, token_request_data: Dict, token_endpoint: str, refresh: bool = False,
63
- verify: bool = False) -> requests.Response:
74
+ def _get_token_response_from_identity(
75
+ refresh_request_data: dict,
76
+ token_request_data: dict,
77
+ token_endpoint: str,
78
+ refresh: Optional[bool] = False,
79
+ verify: Optional[bool] = False
80
+ ) -> requests.Response:
81
+
64
82
  return requests.get(identity_url, headers={"Metadata": "true"}, verify=verify)
65
83
 
66
84
  if use_identity:
67
85
  if not identity_url:
68
- raise ValueError("Misconfiguration dectected - if use_identity is true, identity_url must also be provided. This is a bug, contact Zepben.")
86
+ raise ValueError("Misconfiguration detected - if use_identity is true, identity_url must also be provided. This is a bug, contact Zepben.")
69
87
  return _get_token_response_from_identity
70
88
  else:
71
89
  return _get_token_response
72
90
 
73
91
 
74
- @dataclass
75
- class ZepbenTokenFetcher(object):
92
+ @dataclass(init=True, repr=True, eq=True)
93
+ class ZepbenTokenFetcher:
76
94
  """
77
95
  Fetches access tokens from an authentication provider using the OAuth 2.0 protocol.
78
- """
79
96
 
80
- auth_method: AuthMethod = AuthMethod.OAUTH
81
- """ Deprecated. Kept for backwards compatibility, but this is now unused. """
97
+ :param audience: Audience to use when requesting tokens
98
+ :param token_endpoint: The domain of the token issuer.
99
+ :param token_request_data: Data to pass in token requests.
100
+ :param refresh_request_data: Data to pass in refresh token requests.
101
+ :param verify: Passed through to requests.post(). When this is a boolean, it determines whether to verify the HTTPS
102
+ certificate of the OAUTH service or not. When this is a string, it is used as the filename of the certificate
103
+ truststore to use when verifying the OAUTH service.
104
+ :param auth_method: Deprecated. Kept for backwards compatibility, but this is now unused.
105
+ """
82
106
 
83
107
  audience: str
84
- """ Audience to use when requesting tokens """
85
-
86
- token_endpoint: str
87
- """ The domain of the token issuer. """
88
-
89
- token_request_data = {}
90
- """ Data to pass in token requests. """
91
-
92
- refresh_request_data = {}
93
- """ Data to pass in refresh token requests. """
108
+ issuer: Optional[str] = None
109
+ token_endpoint: Optional[str] = None
110
+ token_request_data: Optional[dict] = field(default_factory=dict)
111
+ refresh_request_data: Optional[dict] = field(default_factory=dict)
112
+ verify: Optional[bool | str] = None
113
+ auth_method: Optional[AuthMethod] = None
94
114
 
95
- verify: Union[bool, str] = True
96
- """
97
- Passed through to requests.post(). When this is a boolean, it determines whether or not to verify the HTTPS certificate of the OAUTH service.
98
- When this is a string, it is used as the filename of the certificate truststore to use when verifying the OAUTH service.
99
- """
115
+ _request_token: InitVar[Callable[[dict, dict, str, Optional[bool], Optional[bool]], requests.Response]] = None
100
116
 
101
- _request_token: Callable[[Dict, Dict, str, bool, bool], requests.Response] = _fetch_token_generator(False, False)
117
+ _access_token: Optional[str] = None
118
+ _refresh_token: Optional[str] = None
119
+ _token_expiry: Optional[datetime] = datetime.min
120
+ token_type: Optional[str] = None
102
121
 
103
- _access_token = None
104
- _refresh_token = None
105
- _token_expiry = datetime.min
106
- _token_type = None
122
+ def __post_init__(self, _request_token):
123
+ if _request_token is None:
124
+ _request_token = _fetch_token_generator(False, False)
125
+ self._request_token = _request_token
107
126
 
108
- def __init__(self):
109
127
  self.token_request_data["audience"] = self.audience
110
128
  self.refresh_request_data["audience"] = self.audience
111
129
 
@@ -134,7 +152,7 @@ class ZepbenTokenFetcher(object):
134
152
 
135
153
  return f"{self._token_type} {self._access_token}"
136
154
 
137
- def _fetch_token(self, refresh: bool = False):
155
+ def _fetch_token(self, refresh: Optional[bool] = False):
138
156
  if refresh:
139
157
  self.refresh_request_data["refresh_token"] = self._refresh_token
140
158
 
@@ -174,11 +192,11 @@ class ZepbenTokenFetcher(object):
174
192
 
175
193
  def create_token_fetcher(
176
194
  conf_address: str,
177
- verify_conf: Union[bool, str] = True,
178
- verify_auth: Union[bool, str] = True,
179
- auth_type_field: str = "authType",
180
- audience_field: str = "audience",
181
- issuer_field: str = "issuer",
195
+ verify_conf: Optional[bool | str] = True,
196
+ verify_auth: Optional[bool | str] = True,
197
+ auth_type_field: Optional[str] = "authType",
198
+ audience_field: Optional[str] = "audience",
199
+ issuer_field: Optional[str] = "issuer",
182
200
  ) -> Optional[ZepbenTokenFetcher]:
183
201
  """
184
202
  Helper method to fetch auth related configuration from `conf_address` and create a :class:`ZepbenTokenFetcher`
@@ -194,6 +212,7 @@ def create_token_fetcher(
194
212
 
195
213
  :returns: A :class:`ZepbenTokenFetcher` if the server reported authentication was configured, otherwise None.
196
214
  """
215
+
197
216
  with warnings.catch_warnings():
198
217
  if not verify_conf:
199
218
  warnings.filterwarnings("ignore", category=InsecureRequestWarning)
@@ -264,6 +283,7 @@ def create_token_fetcher_managed_identity(identity_url: str, verify_auth: bool)
264
283
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=5ffcfee6-34cd-4c5c-bb7e-c5261d739341"
265
284
  :param verify_auth: Whether to verify certificates for the identity_url. Only applies for https URLs.
266
285
  """
286
+
267
287
  return ZepbenTokenFetcher(
268
288
  audience="",
269
289
  issuer="",
@@ -53,12 +53,12 @@ class BaseCimReader(ABC):
53
53
  :return: True if the `Document` was successfully read from the database and added to the service.
54
54
  :raises SQLException: For any errors encountered reading from the database.
55
55
  """
56
- document.title = result_set.get_string(table.title.query_index, on_none="")
56
+ document.title = result_set.get_string(table.title.query_index, on_none=None)
57
57
  document.created_date_time = result_set.get_instant(table.created_date_time.query_index, on_none=None)
58
- document.author_name = result_set.get_string(table.author_name.query_index, on_none="")
59
- document.type = result_set.get_string(table.type.query_index, on_none="")
60
- document.status = result_set.get_string(table.status.query_index, on_none="")
61
- document.comment = result_set.get_string(table.comment.query_index, on_none="")
58
+ document.author_name = result_set.get_string(table.author_name.query_index, on_none=None)
59
+ document.type = result_set.get_string(table.type.query_index, on_none=None)
60
+ document.status = result_set.get_string(table.status.query_index, on_none=None)
61
+ document.comment = result_set.get_string(table.comment.query_index, on_none=None)
62
62
 
63
63
  return self._load_identified_object(document, table, result_set)
64
64
 
@@ -111,8 +111,8 @@ class BaseCimReader(ABC):
111
111
  :return: True if the `IdentifiedObject` was successfully read from the database and added to the service.
112
112
  :raises SQLException: For any errors encountered reading from the database.
113
113
  """
114
- identified_object.name = result_set.get_string(table.name_.query_index, on_none="")
115
- identified_object.description = result_set.get_string(table.description.query_index, on_none="")
114
+ identified_object.name = result_set.get_string(table.name_.query_index, on_none=None)
115
+ identified_object.description = result_set.get_string(table.description.query_index, on_none=None)
116
116
  # Currently unused
117
117
  # identified_object.num_diagram_objects = result_set.get_int(table.num_diagram_objects.query_index)
118
118
 
@@ -11,7 +11,7 @@ from abc import ABC
11
11
  from contextlib import closing
12
12
  from pathlib import Path
13
13
  from sqlite3 import Connection, Cursor, OperationalError
14
- from typing import Callable, Union
14
+ from typing import Callable, Union, Optional
15
15
 
16
16
  from zepben.ewb.database.sqlite.common.base_database_tables import BaseDatabaseTables
17
17
  from zepben.ewb.database.sqlite.common.base_service_writer import BaseServiceWriter
@@ -66,7 +66,7 @@ class BaseDatabaseWriter(ABC):
66
66
  Provider of the connection to the specified database.
67
67
  """
68
68
 
69
- self._save_connection: Connection
69
+ self._save_connection: Optional[Connection] = None
70
70
  self._has_been_used: bool = False
71
71
 
72
72
  def save(self) -> bool:
@@ -1070,22 +1070,22 @@ class NetworkCimReader(BaseCimReader):
1070
1070
 
1071
1071
  def _load_street_address(self, table: TableStreetAddresses, result_set: ResultSet) -> StreetAddress:
1072
1072
  return StreetAddress(
1073
- result_set.get_string(table.postal_code.query_index, on_none=""),
1073
+ result_set.get_string(table.postal_code.query_index, on_none=None),
1074
1074
  self._load_town_detail(table, result_set),
1075
- result_set.get_string(table.po_box.query_index, on_none=""),
1075
+ result_set.get_string(table.po_box.query_index, on_none=None),
1076
1076
  self._load_street_detail(table, result_set)
1077
1077
  )
1078
1078
 
1079
1079
  @staticmethod
1080
1080
  def _load_street_detail(table: TableStreetAddresses, result_set: ResultSet) -> Optional[StreetDetail]:
1081
1081
  sd = StreetDetail(
1082
- result_set.get_string(table.building_name.query_index, on_none=""),
1083
- result_set.get_string(table.floor_identification.query_index, on_none=""),
1084
- result_set.get_string(table.street_name.query_index, on_none=""),
1085
- result_set.get_string(table.number.query_index, on_none=""),
1086
- result_set.get_string(table.suite_number.query_index, on_none=""),
1087
- result_set.get_string(table.type.query_index, on_none=""),
1088
- result_set.get_string(table.display_address.query_index, on_none="")
1082
+ result_set.get_string(table.building_name.query_index, on_none=None),
1083
+ result_set.get_string(table.floor_identification.query_index, on_none=None),
1084
+ result_set.get_string(table.street_name.query_index, on_none=None),
1085
+ result_set.get_string(table.number.query_index, on_none=None),
1086
+ result_set.get_string(table.suite_number.query_index, on_none=None),
1087
+ result_set.get_string(table.type.query_index, on_none=None),
1088
+ result_set.get_string(table.display_address.query_index, on_none=None)
1089
1089
  )
1090
1090
 
1091
1091
  return sd if not sd.all_fields_empty() else None
@@ -1178,7 +1178,7 @@ class NetworkCimReader(BaseCimReader):
1178
1178
  """
1179
1179
  pole = Pole(mrid=set_identifier(result_set.get_string(table.mrid.query_index)))
1180
1180
 
1181
- pole.classification = result_set.get_string(table.classification.query_index, on_none="")
1181
+ pole.classification = result_set.get_string(table.classification.query_index, on_none=None)
1182
1182
 
1183
1183
  return self._load_structure(pole, table, result_set) and self._add_or_throw(pole)
1184
1184
 
@@ -1232,7 +1232,7 @@ class NetworkCimReader(BaseCimReader):
1232
1232
  result_set.get_string(table.location_mrid.query_index, on_none=None),
1233
1233
  Location
1234
1234
  )
1235
- usage_point.is_virtual = result_set.get_boolean(table.is_virtual.query_index)
1235
+ usage_point.is_virtual = result_set.get_boolean(table.is_virtual.query_index, on_none=None)
1236
1236
  usage_point.connection_category = result_set.get_string(table.connection_category.query_index, on_none=None)
1237
1237
  usage_point.rated_power = result_set.get_int(table.rated_power.query_index, on_none=None)
1238
1238
  usage_point.approved_inverter_capacity = result_set.get_int(table.approved_inverter_capacity.query_index, on_none=None)
@@ -1472,7 +1472,7 @@ class NetworkCimReader(BaseCimReader):
1472
1472
  result_set.get_string(table.location_mrid.query_index, on_none=None),
1473
1473
  Location
1474
1474
  )
1475
- power_system_resource.num_controls = result_set.get_int(table.num_controls.query_index)
1475
+ power_system_resource.num_controls = result_set.get_int(table.num_controls.query_index, on_none=None)
1476
1476
 
1477
1477
  return self._load_identified_object(power_system_resource, table, result_set)
1478
1478
 
@@ -1686,7 +1686,7 @@ class NetworkCimReader(BaseCimReader):
1686
1686
  """
1687
1687
  meas = Analog(mrid=set_identifier(result_set.get_string(table.mrid.query_index)))
1688
1688
 
1689
- meas.positive_flow_in = result_set.get_boolean(table.positive_flow_in.query_index)
1689
+ meas.positive_flow_in = result_set.get_boolean(table.positive_flow_in.query_index, on_none=None)
1690
1690
 
1691
1691
  return self._load_measurement(meas, table, result_set) and self._add_or_throw(meas)
1692
1692
 
@@ -1956,7 +1956,7 @@ class NetworkCimReader(BaseCimReader):
1956
1956
  energy_consumer = EnergyConsumer(mrid=set_identifier(result_set.get_string(table.mrid.query_index)))
1957
1957
 
1958
1958
  energy_consumer.customer_count = result_set.get_int(table.customer_count.query_index, on_none=None)
1959
- energy_consumer.grounded = result_set.get_boolean(table.grounded.query_index)
1959
+ energy_consumer.grounded = result_set.get_boolean(table.grounded.query_index, on_none=None)
1960
1960
  energy_consumer.p = result_set.get_float(table.p.query_index, on_none=None)
1961
1961
  energy_consumer.q = result_set.get_float(table.q.query_index, on_none=None)
1962
1962
  energy_consumer.p_fixed = result_set.get_float(table.p_fixed.query_index, on_none=None)
@@ -2018,7 +2018,7 @@ class NetworkCimReader(BaseCimReader):
2018
2018
  energy_source.x = result_set.get_float(table.x.query_index, on_none=None)
2019
2019
  energy_source.x0 = result_set.get_float(table.x0.query_index, on_none=None)
2020
2020
  energy_source.xn = result_set.get_float(table.xn.query_index, on_none=None)
2021
- energy_source.is_external_grid = result_set.get_boolean(table.is_external_grid.query_index)
2021
+ energy_source.is_external_grid = result_set.get_boolean(table.is_external_grid.query_index, on_none=None)
2022
2022
  energy_source.r_min = result_set.get_float(table.r_min.query_index, on_none=None)
2023
2023
  energy_source.rn_min = result_set.get_float(table.rn_min.query_index, on_none=None)
2024
2024
  energy_source.r0_min = result_set.get_float(table.r0_min.query_index, on_none=None)
@@ -2488,7 +2488,7 @@ class NetworkCimReader(BaseCimReader):
2488
2488
  return self._load_protected_switch(recloser, table, result_set) and self._add_or_throw(recloser)
2489
2489
 
2490
2490
  def _load_regulating_cond_eq(self, regulating_cond_eq: RegulatingCondEq, table: TableRegulatingCondEq, result_set: ResultSet) -> bool:
2491
- regulating_cond_eq.control_enabled = result_set.get_boolean(table.control_enabled.query_index)
2491
+ regulating_cond_eq.control_enabled = result_set.get_boolean(table.control_enabled.query_index, on_none=None)
2492
2492
  # We use a resolver here because there is an ordering conflict between terminals, RegulatingCondEq, and RegulatingControls
2493
2493
  # We check this resolver has actually been resolved in the postLoad of the database read and throw there if it hasn't.
2494
2494
  self._service.resolve_or_defer_reference(
@@ -2556,7 +2556,7 @@ class NetworkCimReader(BaseCimReader):
2556
2556
  ShuntCompensatorInfo
2557
2557
  )
2558
2558
 
2559
- shunt_compensator.grounded = result_set.get_boolean(table.grounded.query_index)
2559
+ shunt_compensator.grounded = result_set.get_boolean(table.grounded.query_index, on_none=None)
2560
2560
  shunt_compensator.nom_u = result_set.get_int(table.nom_u.query_index, on_none=None)
2561
2561
  shunt_compensator.phase_connection = PhaseShuntConnectionKind[result_set.get_string(table.phase_connection.query_index)]
2562
2562
  shunt_compensator.sections = result_set.get_float(table.sections.query_index, on_none=None)
@@ -2610,7 +2610,7 @@ class NetworkCimReader(BaseCimReader):
2610
2610
 
2611
2611
  synchronous_machine.base_q = result_set.get_float(table.base_q.query_index, on_none=None)
2612
2612
  synchronous_machine.condenser_p = result_set.get_int(table.condenser_p.query_index, on_none=None)
2613
- synchronous_machine.earthing = result_set.get_boolean(table.earthing.query_index)
2613
+ synchronous_machine.earthing = result_set.get_boolean(table.earthing.query_index, on_none=None)
2614
2614
  synchronous_machine.earthing_star_point_r = result_set.get_float(table.earthing_star_point_r.query_index, on_none=None)
2615
2615
  synchronous_machine.earthing_star_point_x = result_set.get_float(table.earthing_star_point_x.query_index, on_none=None)
2616
2616
  synchronous_machine.ikk = result_set.get_float(table.ikk.query_index, on_none=None)
@@ -2633,7 +2633,7 @@ class NetworkCimReader(BaseCimReader):
2633
2633
  return self._load_rotating_machine(synchronous_machine, table, result_set) and self._add_or_throw(synchronous_machine)
2634
2634
 
2635
2635
  def _load_tap_changer(self, tap_changer: TapChanger, table: TableTapChangers, result_set: ResultSet) -> bool:
2636
- tap_changer.control_enabled = result_set.get_boolean(table.control_enabled.query_index)
2636
+ tap_changer.control_enabled = result_set.get_boolean(table.control_enabled.query_index, on_none=None)
2637
2637
  tap_changer.high_step = result_set.get_int(table.high_step.query_index, on_none=None)
2638
2638
  tap_changer.low_step = result_set.get_int(table.low_step.query_index, on_none=None)
2639
2639
  tap_changer.neutral_step = result_set.get_int(table.neutral_step.query_index, on_none=None)
@@ -2681,7 +2681,7 @@ class NetworkCimReader(BaseCimReader):
2681
2681
  result_set.get_string(table.base_voltage_mrid.query_index, on_none=None),
2682
2682
  BaseVoltage
2683
2683
  )
2684
- transformer_end.grounded = result_set.get_boolean(table.grounded.query_index)
2684
+ transformer_end.grounded = result_set.get_boolean(table.grounded.query_index, on_none=None)
2685
2685
  transformer_end.r_ground = result_set.get_float(table.r_ground.query_index, on_none=None)
2686
2686
  transformer_end.x_ground = result_set.get_float(table.x_ground.query_index, on_none=None)
2687
2687
  transformer_end.star_impedance = self._ensure_get(
@@ -154,9 +154,11 @@ class NetworkDatabaseReader(BaseDatabaseReader):
154
154
  feeder_start_points = set(map(head_conducting_equipment_mrid, self.service.objects(Feeder)))
155
155
 
156
156
  def has_been_assigned_to_feeder(energy_source: EnergySource) -> bool:
157
- return energy_source.is_external_grid \
158
- and self._is_on_feeder(energy_source) \
157
+ return (
158
+ energy_source.is_external_grid
159
+ and self._is_on_feeder(energy_source)
159
160
  and feeder_start_points.isdisjoint({it.to_equip.mrid for it in connected_equipment(energy_source) if it.to_equip})
161
+ )
160
162
 
161
163
  for es in self.service.objects(EnergySource):
162
164
  if has_been_assigned_to_feeder(es):
@@ -15,9 +15,9 @@ class TableDocuments(TableIdentifiedObjects, ABC):
15
15
 
16
16
  def __init__(self):
17
17
  super().__init__()
18
- self.title: Column = self._create_column("title", "TEXT", Nullable.NOT_NULL)
18
+ self.title: Column = self._create_column("title", "TEXT", Nullable.NULL)
19
19
  self.created_date_time: Column = self._create_column("created_date_time", "TEXT", Nullable.NULL)
20
- self.author_name: Column = self._create_column("author_name", "TEXT", Nullable.NOT_NULL)
21
- self.type: Column = self._create_column("type", "TEXT", Nullable.NOT_NULL)
22
- self.status: Column = self._create_column("status", "TEXT", Nullable.NOT_NULL)
23
- self.comment: Column = self._create_column("comment", "TEXT", Nullable.NOT_NULL)
20
+ self.author_name: Column = self._create_column("author_name", "TEXT", Nullable.NULL)
21
+ self.type: Column = self._create_column("type", "TEXT", Nullable.NULL)
22
+ self.status: Column = self._create_column("status", "TEXT", Nullable.NULL)
23
+ self.comment: Column = self._create_column("comment", "TEXT", Nullable.NULL)
@@ -15,7 +15,7 @@ class TableStreetAddresses(TableTownDetails, ABC):
15
15
 
16
16
  def __init__(self):
17
17
  super().__init__()
18
- self.postal_code: Column = self._create_column("postal_code", "TEXT", Nullable.NOT_NULL)
18
+ self.postal_code: Column = self._create_column("postal_code", "TEXT", Nullable.NULL)
19
19
  self.po_box: Column = self._create_column("po_box", "TEXT", Nullable.NULL)
20
20
  self.building_name: Column = self._create_column("building_name", "TEXT", Nullable.NULL)
21
21
  self.floor_identification: Column = self._create_column("floor_identification", "TEXT", Nullable.NULL)
@@ -13,7 +13,7 @@ class TablePoles(TableStructures):
13
13
 
14
14
  def __init__(self):
15
15
  super().__init__()
16
- self.classification: Column = self._create_column("classification", "TEXT", Nullable.NOT_NULL)
16
+ self.classification: Column = self._create_column("classification", "TEXT", Nullable.NULL)
17
17
 
18
18
  @property
19
19
  def name(self) -> str:
@@ -16,9 +16,9 @@ class TableIdentifiedObjects(SqliteTable, ABC):
16
16
 
17
17
  def __init__(self):
18
18
  self.mrid: Column = self._create_column("mrid", "TEXT", Nullable.NOT_NULL)
19
- self.name_: Column = self._create_column("name", "TEXT", Nullable.NOT_NULL)
20
- self.description: Column = self._create_column("description", "TEXT", Nullable.NOT_NULL)
21
- self.num_diagram_objects: Column = self._create_column("num_diagram_objects", "INTEGER", Nullable.NOT_NULL)
19
+ self.name_: Column = self._create_column("name", "TEXT", Nullable.NULL)
20
+ self.description: Column = self._create_column("description", "TEXT", Nullable.NULL)
21
+ self.num_diagram_objects: Column = self._create_column("num_diagram_objects", "INTEGER", Nullable.NULL)
22
22
 
23
23
  @property
24
24
  def unique_index_columns(self) -> Generator[List[Column], None, None]:
@@ -16,4 +16,4 @@ class TablePowerSystemResources(TableIdentifiedObjects, ABC):
16
16
  def __init__(self):
17
17
  super().__init__()
18
18
  self.location_mrid: Column = self._create_column("location_mrid", "TEXT", Nullable.NULL)
19
- self.num_controls: Column = self._create_column("num_controls", "INTEGER", Nullable.NOT_NULL)
19
+ self.num_controls: Column = self._create_column("num_controls", "INTEGER", Nullable.NULL)
@@ -13,7 +13,7 @@ class TableAnalogs(TableMeasurements):
13
13
 
14
14
  def __init__(self):
15
15
  super().__init__()
16
- self.positive_flow_in: Column = self._create_column("positive_flow_in", "BOOLEAN", Nullable.NOT_NULL)
16
+ self.positive_flow_in: Column = self._create_column("positive_flow_in", "BOOLEAN", Nullable.NULL)
17
17
 
18
18
  @property
19
19
  def name(self) -> str:
@@ -14,7 +14,7 @@ class TableEnergyConsumers(TableEnergyConnections):
14
14
  def __init__(self):
15
15
  super().__init__()
16
16
  self.customer_count: Column = self._create_column("customer_count", "INTEGER", Nullable.NULL)
17
- self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NOT_NULL)
17
+ self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NULL)
18
18
  self.p: Column = self._create_column("p", "NUMBER", Nullable.NULL)
19
19
  self.q: Column = self._create_column("q", "NUMBER", Nullable.NULL)
20
20
  self.p_fixed: Column = self._create_column("p_fixed", "NUMBER", Nullable.NULL)
@@ -25,7 +25,7 @@ class TableEnergySources(TableEnergyConnections):
25
25
  self.x: Column = self._create_column("x", "NUMBER", Nullable.NULL)
26
26
  self.x0: Column = self._create_column("x0", "NUMBER", Nullable.NULL)
27
27
  self.xn: Column = self._create_column("xn", "NUMBER", Nullable.NULL)
28
- self.is_external_grid: Column = self._create_column("is_external_grid", "BOOLEAN", Nullable.NOT_NULL)
28
+ self.is_external_grid: Column = self._create_column("is_external_grid", "BOOLEAN", Nullable.NULL)
29
29
  self.r_min: Column = self._create_column("r_min", "NUMBER", Nullable.NULL)
30
30
  self.rn_min: Column = self._create_column("rn_min", "NUMBER", Nullable.NULL)
31
31
  self.r0_min: Column = self._create_column("r0_min", "NUMBER", Nullable.NULL)
@@ -15,5 +15,5 @@ class TableRegulatingCondEq(TableEnergyConnections, ABC):
15
15
 
16
16
  def __init__(self):
17
17
  super().__init__()
18
- self.control_enabled: Column = self._create_column("control_enabled", "BOOLEAN", Nullable.NOT_NULL)
18
+ self.control_enabled: Column = self._create_column("control_enabled", "BOOLEAN", Nullable.NULL)
19
19
  self.regulating_control_mrid: Column = self._create_column("regulating_control_mrid", "TEXT", Nullable.NULL)
@@ -16,7 +16,7 @@ class TableShuntCompensators(TableRegulatingCondEq, ABC):
16
16
  def __init__(self):
17
17
  super().__init__()
18
18
  self.shunt_compensator_info_mrid: Column = self._create_column("shunt_compensator_info_mrid", "TEXT", Nullable.NULL)
19
- self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NOT_NULL)
19
+ self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NULL)
20
20
  self.nom_u: Column = self._create_column("nom_u", "INTEGER", Nullable.NULL)
21
21
  self.phase_connection: Column = self._create_column("phase_connection", "TEXT", Nullable.NOT_NULL)
22
22
  self.sections: Column = self._create_column("sections", "NUMBER", Nullable.NULL)
@@ -15,7 +15,7 @@ class TableTapChangers(TablePowerSystemResources, ABC):
15
15
 
16
16
  def __init__(self):
17
17
  super().__init__()
18
- self.control_enabled: Column = self._create_column("control_enabled", "BOOLEAN", Nullable.NOT_NULL)
18
+ self.control_enabled: Column = self._create_column("control_enabled", "BOOLEAN", Nullable.NULL)
19
19
  self.high_step: Column = self._create_column("high_step", "INTEGER", Nullable.NULL)
20
20
  self.low_step: Column = self._create_column("low_step", "INTEGER", Nullable.NULL)
21
21
  self.neutral_step: Column = self._create_column("neutral_step", "INTEGER", Nullable.NULL)
@@ -19,7 +19,7 @@ class TableTransformerEnds(TableIdentifiedObjects, ABC):
19
19
  self.end_number: Column = self._create_column("end_number", "INTEGER", Nullable.NOT_NULL)
20
20
  self.terminal_mrid: Column = self._create_column("terminal_mrid", "TEXT", Nullable.NULL)
21
21
  self.base_voltage_mrid: Column = self._create_column("base_voltage_mrid", "TEXT", Nullable.NULL)
22
- self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NOT_NULL)
22
+ self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NULL)
23
23
  self.r_ground: Column = self._create_column("r_ground", "NUMBER", Nullable.NULL)
24
24
  self.x_ground: Column = self._create_column("x_ground", "NUMBER", Nullable.NULL)
25
25
  self.star_impedance_mrid: Column = self._create_column("star_impedance_mrid", "TEXT", Nullable.NULL)
@@ -16,21 +16,21 @@ class Document(IdentifiedObject):
16
16
  Parent class for different groupings of information collected and managed as a part of a business process.
17
17
  It will frequently contain references to other objects, such as assets, people and power system resources.
18
18
  """
19
- title: str = ""
19
+ title: Optional[str] = None
20
20
  """Document title."""
21
21
 
22
22
  created_date_time: Optional[datetime] = None
23
23
  """Date and time that this document was created."""
24
24
 
25
- author_name: str = ""
25
+ author_name: Optional[str] = None
26
26
  """Name of the author of this document."""
27
27
 
28
- type: str = ""
28
+ type: Optional[str] = None
29
29
  """Utility-specific classification of this document, according to its corporate standards, practices,
30
30
  and existing IT systems (e.g., for management of assets, maintenance, work, outage, customers, etc.)."""
31
31
 
32
- status: str = ""
32
+ status: Optional[str] = None
33
33
  """Status of subject matter (e.g., Agreement, Work) this document represents."""
34
34
 
35
- comment: str = ""
35
+ comment: Optional[str] = None
36
36
  """Free text comment"""
@@ -18,11 +18,11 @@ class StreetAddress(object):
18
18
  General purpose street and postal address information.
19
19
  """
20
20
 
21
- postal_code: str = ""
21
+ postal_code: Optional[str] = None
22
22
  """Postal code for the address."""
23
23
  town_detail: Optional[TownDetail] = None
24
24
  """Optional `TownDetail` for this address."""
25
- po_box: str = ""
25
+ po_box: Optional[str] = None
26
26
  """Post office box for the address."""
27
27
  street_detail: Optional[StreetDetail] = None
28
28
  """Optional `StreetDetail` for this address."""
@@ -6,6 +6,7 @@
6
6
  __all__ = ["StreetDetail"]
7
7
 
8
8
  from dataclasses import dataclass
9
+ from typing import Optional
9
10
 
10
11
 
11
12
  @dataclass
@@ -14,22 +15,22 @@ class StreetDetail(object):
14
15
  Street details, in the context of address.
15
16
  """
16
17
 
17
- building_name: str = ""
18
+ building_name: Optional[str] = None
18
19
  """
19
20
  (if applicable) In certain cases the physical location of the place of interest does not have a direct point of entry from the street,
20
21
  but may be located inside a larger structure such as a building, complex, office block, apartment, etc.
21
22
  """
22
- floor_identification: str = ""
23
+ floor_identification: Optional[str] = None
23
24
  """The identification by name or number, expressed as text, of the floor in the building as part of this address."""
24
- name: str = ""
25
+ name: Optional[str] = None
25
26
  """Name of the street."""
26
- number: str = ""
27
+ number: Optional[str] = None
27
28
  """Designator of the specific location on the street."""
28
- suite_number: str = ""
29
+ suite_number: Optional[str] = None
29
30
  """Number of the apartment or suite."""
30
- type: str = ""
31
+ type: Optional[str] = None
31
32
  """Type of street. Examples include: street, circle, boulevard, avenue, road, drive, etc."""
32
- display_address: str = ""
33
+ display_address: Optional[str] = None
33
34
  """The address as it should be displayed to a user."""
34
35
 
35
36
  def all_fields_empty(self):
@@ -19,7 +19,7 @@ if TYPE_CHECKING:
19
19
  class Pole(Structure):
20
20
  """A Pole Asset"""
21
21
 
22
- classification: str = ""
22
+ classification: Optional[str] = None
23
23
  """Pole class: 1, 2, 3, 4, 5, 6, 7, H1, H2, Other, Unknown."""
24
24
 
25
25
  _streetlights: Optional[List[Streetlight]] = None
@@ -5,6 +5,8 @@
5
5
 
6
6
  __all__ = ["Meter"]
7
7
 
8
+ from typing import Optional
9
+
8
10
  from zepben.ewb.model.cim.iec61968.metering.end_device import EndDevice
9
11
 
10
12
 
@@ -14,12 +16,12 @@ class Meter(EndDevice):
14
16
  """
15
17
 
16
18
  @property
17
- def company_meter_id(self):
19
+ def company_meter_id(self) -> Optional[str]:
18
20
  """ Returns this `Meter`s ID. Currently stored in `IdentifiedObject.name` """
19
21
  return self.name
20
22
 
21
23
  @company_meter_id.setter
22
- def company_meter_id(self, meter_id):
24
+ def company_meter_id(self, meter_id: Optional[str]):
23
25
  """
24
26
  `meter_id` The ID to set for this Meter. Will use `IdentifiedObject.name` as a backing field.
25
27
  """
@@ -28,7 +28,7 @@ class UsagePoint(IdentifiedObject):
28
28
  usage_point_location: Optional[Location] = None
29
29
  """Service `zepben.ewb.model.cim.iec61968.common.location.Location` where the service delivered by this `UsagePoint` is consumed."""
30
30
 
31
- is_virtual: bool = False
31
+ is_virtual: Optional[bool] = None
32
32
  """
33
33
  If true, this usage point is virtual, i.e., no physical location exists in the network where a meter could be located to
34
34
  collect the meter readings. For example, one may define a virtual usage point to serve as an aggregation of usage for all