Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abel modify workflows #1251

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
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
Loading