Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,27 @@
* The `zepben.auth` dependency has been incorporated into the SDK with the following package change:
* `zepben.auth` -> `zepben.ewb.auth`. You can also import these directly from `zepben.ewb`.
* `SqliteTable` now subclasses `SqlTable`.
* The following CIM fields have been made nullable. Note if previously accessing these fields you will now have to handle them potentially being null.
* `Analog.positiveFlowIn`
* `Document.*`
* `EnergyConsumer.grounded`
* `EnergySource.isExternalGrid`
* `IdentifiedObject.name`
* `IdentifiedObject.description`
* `IdentifiedObject.numDiagramObjects`
* `Meter.companyMeterId`
* `NameType.description`
* `Pole.classification`
* `PowerSystemResource.numControls`
* `RegulatingCondEq.controlEnabled`
* `ShuntCompensator.grounded`
* `StreetAddress.postalCode`
* `StreetAddress.poBox`
* `StreetDetail.*`
* `SynchronousMachine.earthing`
* `TapChanger.controlEnabled`
* `TransformerEnd.grounded`
* `UsagePoint.isVirtual`

### New Features
* Created a new `SqlTable` that doesn't support creating schema creation statements by default.
Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
description = "Python SDK for interacting with the Energy Workbench platform"
readme = {file = "README.md", content-type = "text/markdown"}
license = "MPL-2.0"
requires-python = '>=3.9,<3.13'
requires-python = '>=3.10,<3.13'
authors = [
{name = "Kurt Greaves", email = "kurt.greaves@zepben.com"},
{name = "Max Chesterfield", email = "max.chesterfield@zepben.com"}
]
dependencies = [
"zepben.protobuf==1.0.0b1",
"zepben.protobuf==1.0.0",
"typing_extensions==4.12.2",
"requests>=2.26.0, <3.0.0",
"urllib3>=1.26.6, <1.27.0",
Expand All @@ -32,7 +32,6 @@
]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down
14 changes: 7 additions & 7 deletions src/zepben/ewb/database/sqlite/common/base_cim_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ def _load_document(self, document: Document, table: TableDocuments, result_set:
:return: True if the `Document` was successfully read from the database and added to the service.
:raises SQLException: For any errors encountered reading from the database.
"""
document.title = result_set.get_string(table.title.query_index, on_none="")
document.title = result_set.get_string(table.title.query_index, on_none=None)
document.created_date_time = result_set.get_instant(table.created_date_time.query_index, on_none=None)
document.author_name = result_set.get_string(table.author_name.query_index, on_none="")
document.type = result_set.get_string(table.type.query_index, on_none="")
document.status = result_set.get_string(table.status.query_index, on_none="")
document.comment = result_set.get_string(table.comment.query_index, on_none="")
document.author_name = result_set.get_string(table.author_name.query_index, on_none=None)
document.type = result_set.get_string(table.type.query_index, on_none=None)
document.status = result_set.get_string(table.status.query_index, on_none=None)
document.comment = result_set.get_string(table.comment.query_index, on_none=None)

return self._load_identified_object(document, table, result_set)

Expand Down Expand Up @@ -111,8 +111,8 @@ def _load_identified_object(identified_object: IdentifiedObject, table: TableIde
:return: True if the `IdentifiedObject` was successfully read from the database and added to the service.
:raises SQLException: For any errors encountered reading from the database.
"""
identified_object.name = result_set.get_string(table.name_.query_index, on_none="")
identified_object.description = result_set.get_string(table.description.query_index, on_none="")
identified_object.name = result_set.get_string(table.name_.query_index, on_none=None)
identified_object.description = result_set.get_string(table.description.query_index, on_none=None)
# Currently unused
# identified_object.num_diagram_objects = result_set.get_int(table.num_diagram_objects.query_index)

Expand Down
4 changes: 2 additions & 2 deletions src/zepben/ewb/database/sqlite/common/base_database_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from contextlib import closing
from pathlib import Path
from sqlite3 import Connection, Cursor, OperationalError
from typing import Callable, Union
from typing import Callable, Union, Optional

from zepben.ewb.database.sqlite.common.base_database_tables import BaseDatabaseTables
from zepben.ewb.database.sqlite.common.base_service_writer import BaseServiceWriter
Expand Down Expand Up @@ -66,7 +66,7 @@ def __init__(
Provider of the connection to the specified database.
"""

self._save_connection: Connection
self._save_connection: Optional[Connection] = None
self._has_been_used: bool = False

def save(self) -> bool:
Expand Down
40 changes: 20 additions & 20 deletions src/zepben/ewb/database/sqlite/network/network_cim_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -1070,22 +1070,22 @@ def load_position_point(self, table: TablePositionPoints, result_set: ResultSet,

def _load_street_address(self, table: TableStreetAddresses, result_set: ResultSet) -> StreetAddress:
return StreetAddress(
result_set.get_string(table.postal_code.query_index, on_none=""),
result_set.get_string(table.postal_code.query_index, on_none=None),
self._load_town_detail(table, result_set),
result_set.get_string(table.po_box.query_index, on_none=""),
result_set.get_string(table.po_box.query_index, on_none=None),
self._load_street_detail(table, result_set)
)

@staticmethod
def _load_street_detail(table: TableStreetAddresses, result_set: ResultSet) -> Optional[StreetDetail]:
sd = StreetDetail(
result_set.get_string(table.building_name.query_index, on_none=""),
result_set.get_string(table.floor_identification.query_index, on_none=""),
result_set.get_string(table.street_name.query_index, on_none=""),
result_set.get_string(table.number.query_index, on_none=""),
result_set.get_string(table.suite_number.query_index, on_none=""),
result_set.get_string(table.type.query_index, on_none=""),
result_set.get_string(table.display_address.query_index, on_none="")
result_set.get_string(table.building_name.query_index, on_none=None),
result_set.get_string(table.floor_identification.query_index, on_none=None),
result_set.get_string(table.street_name.query_index, on_none=None),
result_set.get_string(table.number.query_index, on_none=None),
result_set.get_string(table.suite_number.query_index, on_none=None),
result_set.get_string(table.type.query_index, on_none=None),
result_set.get_string(table.display_address.query_index, on_none=None)
)

return sd if not sd.all_fields_empty() else None
Expand Down Expand Up @@ -1178,7 +1178,7 @@ def load_pole(self, table: TablePoles, result_set: ResultSet, set_identifier: Ca
"""
pole = Pole(mrid=set_identifier(result_set.get_string(table.mrid.query_index)))

pole.classification = result_set.get_string(table.classification.query_index, on_none="")
pole.classification = result_set.get_string(table.classification.query_index, on_none=None)

return self._load_structure(pole, table, result_set) and self._add_or_throw(pole)

Expand Down Expand Up @@ -1232,7 +1232,7 @@ def load_usage_point(self, table: TableUsagePoints, result_set: ResultSet, set_i
result_set.get_string(table.location_mrid.query_index, on_none=None),
Location
)
usage_point.is_virtual = result_set.get_boolean(table.is_virtual.query_index)
usage_point.is_virtual = result_set.get_boolean(table.is_virtual.query_index, on_none=None)
usage_point.connection_category = result_set.get_string(table.connection_category.query_index, on_none=None)
usage_point.rated_power = result_set.get_int(table.rated_power.query_index, on_none=None)
usage_point.approved_inverter_capacity = result_set.get_int(table.approved_inverter_capacity.query_index, on_none=None)
Expand Down Expand Up @@ -1472,7 +1472,7 @@ def _load_power_system_resource(self, power_system_resource: PowerSystemResource
result_set.get_string(table.location_mrid.query_index, on_none=None),
Location
)
power_system_resource.num_controls = result_set.get_int(table.num_controls.query_index)
power_system_resource.num_controls = result_set.get_int(table.num_controls.query_index, on_none=None)

return self._load_identified_object(power_system_resource, table, result_set)

Expand Down Expand Up @@ -1686,7 +1686,7 @@ def load_analog(self, table: TableAnalogs, result_set: ResultSet, set_identifier
"""
meas = Analog(mrid=set_identifier(result_set.get_string(table.mrid.query_index)))

meas.positive_flow_in = result_set.get_boolean(table.positive_flow_in.query_index)
meas.positive_flow_in = result_set.get_boolean(table.positive_flow_in.query_index, on_none=None)

return self._load_measurement(meas, table, result_set) and self._add_or_throw(meas)

Expand Down Expand Up @@ -1956,7 +1956,7 @@ def load_energy_consumer(self, table: TableEnergyConsumers, result_set: ResultSe
energy_consumer = EnergyConsumer(mrid=set_identifier(result_set.get_string(table.mrid.query_index)))

energy_consumer.customer_count = result_set.get_int(table.customer_count.query_index, on_none=None)
energy_consumer.grounded = result_set.get_boolean(table.grounded.query_index)
energy_consumer.grounded = result_set.get_boolean(table.grounded.query_index, on_none=None)
energy_consumer.p = result_set.get_float(table.p.query_index, on_none=None)
energy_consumer.q = result_set.get_float(table.q.query_index, on_none=None)
energy_consumer.p_fixed = result_set.get_float(table.p_fixed.query_index, on_none=None)
Expand Down Expand Up @@ -2018,7 +2018,7 @@ def load_energy_source(self, table: TableEnergySources, result_set: ResultSet, s
energy_source.x = result_set.get_float(table.x.query_index, on_none=None)
energy_source.x0 = result_set.get_float(table.x0.query_index, on_none=None)
energy_source.xn = result_set.get_float(table.xn.query_index, on_none=None)
energy_source.is_external_grid = result_set.get_boolean(table.is_external_grid.query_index)
energy_source.is_external_grid = result_set.get_boolean(table.is_external_grid.query_index, on_none=None)
energy_source.r_min = result_set.get_float(table.r_min.query_index, on_none=None)
energy_source.rn_min = result_set.get_float(table.rn_min.query_index, on_none=None)
energy_source.r0_min = result_set.get_float(table.r0_min.query_index, on_none=None)
Expand Down Expand Up @@ -2488,7 +2488,7 @@ def load_recloser(self, table: TableReclosers, result_set: ResultSet, set_identi
return self._load_protected_switch(recloser, table, result_set) and self._add_or_throw(recloser)

def _load_regulating_cond_eq(self, regulating_cond_eq: RegulatingCondEq, table: TableRegulatingCondEq, result_set: ResultSet) -> bool:
regulating_cond_eq.control_enabled = result_set.get_boolean(table.control_enabled.query_index)
regulating_cond_eq.control_enabled = result_set.get_boolean(table.control_enabled.query_index, on_none=None)
# We use a resolver here because there is an ordering conflict between terminals, RegulatingCondEq, and RegulatingControls
# We check this resolver has actually been resolved in the postLoad of the database read and throw there if it hasn't.
self._service.resolve_or_defer_reference(
Expand Down Expand Up @@ -2556,7 +2556,7 @@ def _load_shunt_compensator(self, shunt_compensator: ShuntCompensator, table: Ta
ShuntCompensatorInfo
)

shunt_compensator.grounded = result_set.get_boolean(table.grounded.query_index)
shunt_compensator.grounded = result_set.get_boolean(table.grounded.query_index, on_none=None)
shunt_compensator.nom_u = result_set.get_int(table.nom_u.query_index, on_none=None)
shunt_compensator.phase_connection = PhaseShuntConnectionKind[result_set.get_string(table.phase_connection.query_index)]
shunt_compensator.sections = result_set.get_float(table.sections.query_index, on_none=None)
Expand Down Expand Up @@ -2610,7 +2610,7 @@ def load_synchronous_machine(self, table: TableSynchronousMachines, result_set:

synchronous_machine.base_q = result_set.get_float(table.base_q.query_index, on_none=None)
synchronous_machine.condenser_p = result_set.get_int(table.condenser_p.query_index, on_none=None)
synchronous_machine.earthing = result_set.get_boolean(table.earthing.query_index)
synchronous_machine.earthing = result_set.get_boolean(table.earthing.query_index, on_none=None)
synchronous_machine.earthing_star_point_r = result_set.get_float(table.earthing_star_point_r.query_index, on_none=None)
synchronous_machine.earthing_star_point_x = result_set.get_float(table.earthing_star_point_x.query_index, on_none=None)
synchronous_machine.ikk = result_set.get_float(table.ikk.query_index, on_none=None)
Expand All @@ -2633,7 +2633,7 @@ def load_synchronous_machine(self, table: TableSynchronousMachines, result_set:
return self._load_rotating_machine(synchronous_machine, table, result_set) and self._add_or_throw(synchronous_machine)

def _load_tap_changer(self, tap_changer: TapChanger, table: TableTapChangers, result_set: ResultSet) -> bool:
tap_changer.control_enabled = result_set.get_boolean(table.control_enabled.query_index)
tap_changer.control_enabled = result_set.get_boolean(table.control_enabled.query_index, on_none=None)
tap_changer.high_step = result_set.get_int(table.high_step.query_index, on_none=None)
tap_changer.low_step = result_set.get_int(table.low_step.query_index, on_none=None)
tap_changer.neutral_step = result_set.get_int(table.neutral_step.query_index, on_none=None)
Expand Down Expand Up @@ -2681,7 +2681,7 @@ def _load_transformer_end(self, transformer_end: TransformerEnd, table: TableTra
result_set.get_string(table.base_voltage_mrid.query_index, on_none=None),
BaseVoltage
)
transformer_end.grounded = result_set.get_boolean(table.grounded.query_index)
transformer_end.grounded = result_set.get_boolean(table.grounded.query_index, on_none=None)
transformer_end.r_ground = result_set.get_float(table.r_ground.query_index, on_none=None)
transformer_end.x_ground = result_set.get_float(table.x_ground.query_index, on_none=None)
transformer_end.star_impedance = self._ensure_get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,11 @@ def head_conducting_equipment_mrid(feeder: Feeder) -> Optional[str]:
feeder_start_points = set(map(head_conducting_equipment_mrid, self.service.objects(Feeder)))

def has_been_assigned_to_feeder(energy_source: EnergySource) -> bool:
return energy_source.is_external_grid \
and self._is_on_feeder(energy_source) \
return (
energy_source.is_external_grid
and self._is_on_feeder(energy_source)
and feeder_start_points.isdisjoint({it.to_equip.mrid for it in connected_equipment(energy_source) if it.to_equip})
)

for es in self.service.objects(EnergySource):
if has_been_assigned_to_feeder(es):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class TableDocuments(TableIdentifiedObjects, ABC):

def __init__(self):
super().__init__()
self.title: Column = self._create_column("title", "TEXT", Nullable.NOT_NULL)
self.title: Column = self._create_column("title", "TEXT", Nullable.NULL)
self.created_date_time: Column = self._create_column("created_date_time", "TEXT", Nullable.NULL)
self.author_name: Column = self._create_column("author_name", "TEXT", Nullable.NOT_NULL)
self.type: Column = self._create_column("type", "TEXT", Nullable.NOT_NULL)
self.status: Column = self._create_column("status", "TEXT", Nullable.NOT_NULL)
self.comment: Column = self._create_column("comment", "TEXT", Nullable.NOT_NULL)
self.author_name: Column = self._create_column("author_name", "TEXT", Nullable.NULL)
self.type: Column = self._create_column("type", "TEXT", Nullable.NULL)
self.status: Column = self._create_column("status", "TEXT", Nullable.NULL)
self.comment: Column = self._create_column("comment", "TEXT", Nullable.NULL)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class TableStreetAddresses(TableTownDetails, ABC):

def __init__(self):
super().__init__()
self.postal_code: Column = self._create_column("postal_code", "TEXT", Nullable.NOT_NULL)
self.postal_code: Column = self._create_column("postal_code", "TEXT", Nullable.NULL)
self.po_box: Column = self._create_column("po_box", "TEXT", Nullable.NULL)
self.building_name: Column = self._create_column("building_name", "TEXT", Nullable.NULL)
self.floor_identification: Column = self._create_column("floor_identification", "TEXT", Nullable.NULL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TablePoles(TableStructures):

def __init__(self):
super().__init__()
self.classification: Column = self._create_column("classification", "TEXT", Nullable.NOT_NULL)
self.classification: Column = self._create_column("classification", "TEXT", Nullable.NULL)

@property
def name(self) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class TableIdentifiedObjects(SqliteTable, ABC):

def __init__(self):
self.mrid: Column = self._create_column("mrid", "TEXT", Nullable.NOT_NULL)
self.name_: Column = self._create_column("name", "TEXT", Nullable.NOT_NULL)
self.description: Column = self._create_column("description", "TEXT", Nullable.NOT_NULL)
self.num_diagram_objects: Column = self._create_column("num_diagram_objects", "INTEGER", Nullable.NOT_NULL)
self.name_: Column = self._create_column("name", "TEXT", Nullable.NULL)
self.description: Column = self._create_column("description", "TEXT", Nullable.NULL)
self.num_diagram_objects: Column = self._create_column("num_diagram_objects", "INTEGER", Nullable.NULL)

@property
def unique_index_columns(self) -> Generator[List[Column], None, None]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ class TablePowerSystemResources(TableIdentifiedObjects, ABC):
def __init__(self):
super().__init__()
self.location_mrid: Column = self._create_column("location_mrid", "TEXT", Nullable.NULL)
self.num_controls: Column = self._create_column("num_controls", "INTEGER", Nullable.NOT_NULL)
self.num_controls: Column = self._create_column("num_controls", "INTEGER", Nullable.NULL)
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TableAnalogs(TableMeasurements):

def __init__(self):
super().__init__()
self.positive_flow_in: Column = self._create_column("positive_flow_in", "BOOLEAN", Nullable.NOT_NULL)
self.positive_flow_in: Column = self._create_column("positive_flow_in", "BOOLEAN", Nullable.NULL)

@property
def name(self) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class TableEnergyConsumers(TableEnergyConnections):
def __init__(self):
super().__init__()
self.customer_count: Column = self._create_column("customer_count", "INTEGER", Nullable.NULL)
self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NOT_NULL)
self.grounded: Column = self._create_column("grounded", "BOOLEAN", Nullable.NULL)
self.p: Column = self._create_column("p", "NUMBER", Nullable.NULL)
self.q: Column = self._create_column("q", "NUMBER", Nullable.NULL)
self.p_fixed: Column = self._create_column("p_fixed", "NUMBER", Nullable.NULL)
Expand Down
Loading