Skip to content
Draft
23 changes: 15 additions & 8 deletions tools/abel/abel.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,28 @@


def main(parsed_args: ParseArgs) -> None:

print(
'\nHow would you like to use ABEL?\n'
+ '1: Modify a spreadsheet/building config for an existing building\n'
+ '2: Create a spreadsheet for a new building\n'
+ '3: Split a building config\n'
+ '1: Create a building config yaml file from a spreadsheet.\n'
+ '2: Create a spreadsheet from a building config.\n'
+ 'q: quit\n'
)
function_choice = input('Please select an option: ')
new_workflow = Workflow(parsed_args)
new_workflow = Workflow(
parsed_args.credential,
parsed_args.modified_types_filepath,
parsed_args.output_dir
)
if function_choice == '1':
new_workflow.UpdateWorkflow()
new_workflow.SpreadsheetWorkflow(
parsed_args.spreadsheet_id,
parsed_args.building_config,
parsed_args.subscription,
parsed_args.timeout
)
elif function_choice == '2':
new_workflow.InitWorkflow()
elif function_choice == '3':
new_workflow.SplitWorkflow()
new_workflow.ConfigWorkflow(parsed_args.building_config)
elif function_choice == 'q':
print('Bye bye')
sys.exit()
Expand Down
107 changes: 43 additions & 64 deletions tools/abel/model/export_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.
"""Helper module for exporting a valid Building Configuration or spreadsheet."""

import enum
from typing import Any, Dict, List, Optional

# pylint: disable=g-importing-member
Expand All @@ -25,7 +26,6 @@
from model.constants import CONFIG_CODE
from model.constants import CONFIG_CONNECTIONS
from model.constants import CONFIG_ETAG
from model.constants import CONFIG_INITIALIZE
from model.constants import CONFIG_LINKS
from model.constants import CONFIG_METADATA
from model.constants import CONFIG_OPERATION
Expand All @@ -51,6 +51,13 @@
from model.model_error import SpreadsheetAuthorizationError
from validate.field_translation import FieldTranslation

class ConfigMode(enum.Enum):

# config mode for a building config being updated.
UPDATE = 'UPDATE'

# config mode for a building being initialized.
INITIALIZE = 'INITIALIZE'

class GoogleSheetExport(object):
"""Class to help write ABEL data types to a Google Sheets spreadsheet.
Expand Down Expand Up @@ -112,8 +119,8 @@ def __init__(self, model: Model):
"""
self.model = model

def ExportUpdateBuildingConfiguration(
self, filepath: str, operations: List[EntityOperation]
def ExportBuildingConfiguration(
self, filepath: str, operations: List[EntityOperation] = None
) -> Dict[str, Any]:
"""Exports a building Config under the UPDATE operation.

Expand All @@ -126,22 +133,33 @@ def ExportUpdateBuildingConfiguration(
Dictionary mapping of a building config under the update operation.
"""
site = self.model.site
entity_yaml_dict = {CONFIG_METADATA: {CONFIG_OPERATION: 'UPDATE'}}
for operation in operations:
entity = operation.entity
config_mode = ConfigMode.UPDATE
operations_map = {}
if not site.etag:
config_mode = ConfigMode.INITIALIZE
entity_yaml_dict = {CONFIG_METADATA: {CONFIG_OPERATION: config_mode.value}}
if not operations:
iterator = [self.model.guid_to_entity_map.GetEntityByGuid(entity_guid)
for entity_guid in site.entities]
else:
iterator = [operation.entity for operation in operations]
operations_map = {operation.entity.bc_guid: operation for operation in
operations}
for entity in iterator:
operation = operations_map.get(entity.bc_guid)
if isinstance(entity, ReportingEntity):
entity_yaml_dict.update(
{
entity.bc_guid: self._GetReportingEntityBuildingConfigBlock(
entity, operation
entity, config_mode, operation
)
}
)
elif isinstance(entity, VirtualEntity):
entity_yaml_dict.update(
{
entity.bc_guid: self._GetVirtualEntityBuildingConfigBlock(
entity, operation
entity, config_mode, operation
)
}
)
Expand All @@ -151,10 +169,11 @@ def ExportUpdateBuildingConfiguration(
site.guid: {
CONFIG_CODE: site.code,
CONFIG_TYPE: site.namespace + '/' + site.type_name,
CONFIG_ETAG: site.etag,
}
}
)
if site.etag:
entity_yaml_dict.get(site.guid).update({CONFIG_ETAG: site.etag})
try:
with open(filepath, WRITE, encoding=UTF_8) as file:
for key, value in entity_yaml_dict.items():
Expand All @@ -165,56 +184,6 @@ def ExportUpdateBuildingConfiguration(
return entity_yaml_dict

# TODO(b/233756557) Allow user to set config_metadata operation.
def ExportInitBuildingConfiguration(self, filepath: str) -> Dict[str, Any]:
"""Exports an ABEL concrete model graph to a Building Config file.

Args:
filepath: Absolute export path for a Building Config.

Returns:
A dictionary model of the exported building config.

Raises:
PermissionError: When ABEL is denied access to filepath.
"""
site = self.model.site
entity_yaml_dict = {CONFIG_METADATA: {CONFIG_OPERATION: CONFIG_INITIALIZE}}
for entity_guid in site.entities:
entity = self.model.guid_to_entity_map.GetEntityByGuid(entity_guid)
if isinstance(entity, ReportingEntity):
entity_yaml_dict.update(
{
entity.bc_guid: self._GetReportingEntityBuildingConfigBlock(
entity=entity,
operation=None,
)
}
)
elif isinstance(entity, VirtualEntity):
entity_yaml_dict.update(
{
entity.bc_guid: self._GetVirtualEntityBuildingConfigBlock(
entity=entity, operation=None
)
}
)

entity_yaml_dict.update(
{
site.guid: {
CONFIG_CODE: site.code,
CONFIG_TYPE: site.namespace + '/' + site.type_name,
}
}
)
try:
with open(filepath, WRITE, encoding=UTF_8) as file:
for key, value in entity_yaml_dict.items():
file.write(as_document({key: value}).as_yaml())
file.write('\n')
except PermissionError:
print(f'Permission denied when writing to {filepath}')
return entity_yaml_dict

def _AddOperationToBlock(
self, operation: EntityOperation
Expand All @@ -229,13 +198,18 @@ def _AddOperationToBlock(
return update_dict

def _GetReportingEntityBuildingConfigBlock(
self, entity: ReportingEntity, operation: Optional[EntityOperation]
self,
entity: ReportingEntity,
config_mode: ConfigMode,
operation: Optional[EntityOperation]
) -> Dict[str, object]:
"""Returns a Building Config formatted reporting entity block dictionary.

Args:
entity: A ReportingEntity instance.
operation:
operation: The operation acting on the entity...
config_mode: ConfigMode instance representing a building config's metadata
config mode.

Returns:
A dictionary in Building Config format ready to be parsed into yaml.
Expand Down Expand Up @@ -273,12 +247,16 @@ def _GetReportingEntityBuildingConfigBlock(
reporting_entity_yaml.update(
{CONFIG_TYPE: entity.namespace.value + '/' + str(entity.type_name)}
)
if operation:
if operation and not operation.operation == EntityOperationType.EXPORT and \
config_mode == ConfigMode.UPDATE:
reporting_entity_yaml.update(self._AddOperationToBlock(operation))
return reporting_entity_yaml

def _GetVirtualEntityBuildingConfigBlock(
self, entity: VirtualEntity, operation: Optional[EntityOperation]
self,
entity: VirtualEntity,
config_mode: ConfigMode,
operation: Optional[EntityOperation]
) -> Dict[str, object]:
"""Returns a Building Config formatted virtual entity block dictionary.

Expand All @@ -298,7 +276,8 @@ def _GetVirtualEntityBuildingConfigBlock(
virtual_entity_yaml.update(
{CONFIG_TYPE: entity.namespace.value + '/' + str(entity.type_name)}
)
if operation:
if operation and not operation.operation == EntityOperationType.EXPORT and \
config_mode == ConfigMode.UPDATE:
virtual_entity_yaml.update(self._AddOperationToBlock(operation))
return virtual_entity_yaml

Expand Down
9 changes: 6 additions & 3 deletions tools/abel/model/from_spreadsheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ def LoadStatesFromSpreadsheet(
state_entry[REPORTING_ENTITY_GUID] = guid_to_entity_map.GetEntityGuidByCode(
state_entry[REPORTING_ENTITY_CODE]
)
states.append(State.FromDict(states_dict=state_entry))
new_state = State.FromDict(states_dict=state_entry)
states.append(new_state)

return states

Expand Down Expand Up @@ -172,7 +173,7 @@ def LoadConnectionsFromSpreadsheet(
def LoadOperationsFromSpreadsheet(
entity_entries: Dict[str, str], guid_to_entity_map: GuidToEntityMap
) -> List[EntityOperation]:
"""loads a list of entity dicitionary mappings into EntityOperation instances.
"""loads a list of entity dictionary mappings into EntityOperation instances.

Args:
entity_entries: A list of Python Dictionaries mapping entity attributes
Expand All @@ -192,8 +193,10 @@ def LoadOperationsFromSpreadsheet(
new_entity.bc_guid = str(uuid.uuid4())
guid_to_entity_map.AddEntity(new_entity)
operation = entity_entry.get(OPERATION)
if not operation:
if not operation and new_entity.etag:
operation = EntityOperationType.EXPORT
elif not operation and not new_entity.etag:
operation = EntityOperationType.ADD
operation_instance = EntityOperation(
entity=new_entity, operation=EntityOperationType(operation)
)
Expand Down
4 changes: 3 additions & 1 deletion tools/abel/model/model_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"""Helper module for concrete model construction."""

import datetime
import uuid
from typing import Dict, List, Optional

# pylint: disable=g-importing-member
Expand Down Expand Up @@ -214,8 +215,9 @@ def Build(self) -> ...:
# For each entity, Add connections where entity is the source
for guid in self.site.entities:
entity = self.guid_to_entity_map.GetEntityByGuid(guid)
guid = uuid.UUID(guid)
for connection in self.connections:
if connection.target_entity_guid == guid:
if uuid.UUID(connection.target_entity_guid) == guid:
entity.AddConnection(connection)
# For each field in the model
for field in self.fields:
Expand Down
8 changes: 8 additions & 0 deletions tools/abel/model/model_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ def DetermineEntityOperations(
operation which is being performed on it.
"""
operations = []
if not current_model:
for entity in updated_model.entities:
if not entity.etag:
operations.append(entity_operation.EntityOperation(
entity,
operation=entity_enumerations.EntityOperationType.ADD
))
return operations
for import_entity in updated_model.entities:
if import_entity.bc_guid not in set(
entity.bc_guid for entity in current_model.entities
Expand Down
Loading