# coding=utf-8
"""Utilities to check whether there are any duplicate values in a list of ids."""
import collections
[docs]
def check_duplicate_identifiers(
objects_to_check, raise_exception=True, obj_name='', detailed=False,
code='000000', extension='Core', error_type='Duplicate Object Identifier'):
"""Check whether there are duplicated identifiers across a list of objects.
Args:
objects_to_check: A list of honeybee objects across which duplicate
identifiers will be checked.
raise_exception: Boolean to note whether an exception should be raised if
duplicated identifiers are found. (Default: True).
obj_name: An optional name for the object to be included in the error
message. Fro example, 'Room', 'Face', 'Aperture'.
detailed: Boolean for whether the returned object is a detailed list of
dicts with error info or a string with a message. (Default: False).
code: Text for the error code. (Default: 0000).
extension: Text for the name of the Honeybee extension for which duplicate
identifiers are being evaluated. (Default: Core).
error_type: Text for the type of error. This should be directly linked
to the error code and should simply be a human-readable version of
the error code. (Default: Unknown Error).
Returns:
A message string indicating the duplicated identifiers (if detailed is False)
or a list of dictionaries with information about the duplicated identifiers
(if detailed is True). This string (or list) will be empty if no duplicates
were found.
"""
detailed = False if raise_exception else detailed
obj_id_iter = (obj.identifier for obj in objects_to_check)
dup = [t for t, c in collections.Counter(obj_id_iter).items() if c > 1]
if len(dup) != 0:
if detailed:
# find the object display names
dis_names = []
for obj_id in dup:
dis_name = None
for obj in objects_to_check:
if obj.identifier == obj_id:
dis_name = obj.display_name
dis_names.append(dis_name)
err_list = []
for dup_id, dis_name in zip(dup, dis_names):
msg = 'There is a duplicated {} identifier: {}'.format(obj_name, dup_id)
dup_dict = {
'type': 'ValidationError',
'code': code,
'error_type': error_type,
'extension_type': extension,
'element_type': obj_name,
'element_id': [dup_id],
'message': msg
}
if dis_name is not None:
dup_dict['element_name'] = [dis_name]
err_list.append(dup_dict)
return err_list
msg = 'The following duplicated {} identifiers were found:\n{}'.format(
obj_name, '\n'.join(dup))
if raise_exception:
raise ValueError(msg)
return msg
return [] if detailed else ''
[docs]
def check_duplicate_identifiers_parent(
objects_to_check, raise_exception=True, obj_name='', detailed=False,
code='000000', extension='Core', error_type='Duplicate Object Identifier'):
"""Check whether there are duplicated identifiers across a list of objects.
The error message will include the identifiers of top-level parents in order
to make it easier to find the duplicated objects in the model.
Args:
objects_to_check: A list of honeybee objects across which duplicate
identifiers will be checked. These objects must have the ability to
have parents for this method to run correctly.
raise_exception: Boolean to note whether an exception should be raised if
duplicated identifiers are found. (Default: True).
obj_name: An optional name for the object to be included in the error
message. For example, 'Room', 'Face', 'Aperture'.
detailed: Boolean for whether the returned object is a detailed list of
dicts with error info or a string with a message. (Default: False).
code: Text for the error code. (Default: 0000).
extension: Text for the name of the Honeybee extension for which duplicate
identifiers are being evaluated. (Default: Core).
error_type: Text for the type of error. This should be directly linked
to the error code and should simply be a human-readable version of
the error code. (Default: Unknown Error).
Returns:
A message string indicating the duplicated identifiers (if detailed is False)
or a list of dictionaries with information about the duplicated identifiers
(if detailed is True). This string (or list) will be empty if no duplicates
were found.
"""
detailed = False if raise_exception else detailed
obj_id_iter = (obj.identifier for obj in objects_to_check)
dup = [t for t, c in collections.Counter(obj_id_iter).items() if c > 1]
if len(dup) != 0:
# find the relevant top-level parents
top_par, dis_names = [], []
for obj_id in dup:
rel_parents, dis_name = [], None
for obj in objects_to_check:
if obj.identifier == obj_id:
dis_name = obj.display_name
if obj.has_parent:
try:
par_obj = obj.top_level_parent
except AttributeError:
par_obj = obj.parent
rel_parents.append(par_obj)
top_par.append(rel_parents)
dis_names.append(dis_name)
# if a detailed dictionary is requested, then create it
if detailed:
err_list = []
for dup_id, dis_name, rel_par in zip(dup, dis_names, top_par):
dup_dict = {
'type': 'ValidationError',
'code': code,
'error_type': error_type,
'extension_type': extension,
'element_type': obj_name,
'element_id': [dup_id]
}
if dis_name is not None:
dup_dict['element_name'] = [dis_name]
msg = 'There is a duplicated {} identifier: {}'.format(obj_name, dup_id)
if len(rel_par) != 0:
dup_dict['top_parents'] = []
msg += '\n Relevant Top-Level Parents:\n'
for par_o in rel_par:
par_dict = {
'parent_type': par_o.__class__.__name__,
'id': par_o.identifier,
'name': par_o.display_name
}
dup_dict['top_parents'].append(par_dict)
msg += ' {} "{}"\n'.format(
par_o.__class__.__name__, par_o.full_id)
dup_dict['message'] = msg
err_list.append(dup_dict)
return err_list
# if just an error message is requested, then build it from the information
msg = 'The following duplicated {} identifiers were found:\n'.format(obj_name)
for obj_id, rel_par in zip(dup, top_par):
obj_msg = obj_id + '\n'
if len(rel_par) != 0:
obj_msg += ' Relevant Top-Level Parents:\n'
for par_o in rel_par:
obj_msg += ' {} "{}"\n'.format(
par_o.__class__.__name__, par_o.full_id)
msg += obj_msg
msg = msg.strip()
if raise_exception:
raise ValueError(msg)
return msg
return [] if detailed else ''
[docs]
def is_equivalent(object_1, object_2):
"""Check if two objects are equal with an initial check for the same instance.
"""
if object_1 is object_2: # first see if they're the same instance
return True
return object_1 == object_2 # two objects that should have == operators