Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def get_part_groups(in_file, ignore_fields, variant):
'''Get groups of identical parts from an generic CSV file and return them as a dictionary.
@param in_file `str()` with the file name.
@param ignore_fields `list()` fields do be ignored on the read action.
@param variant `str()` in regular expression to match with the design version of the BOM.
For now, `variant`is not used on CSV read, just kept to compatibility with the other EDA submodules.
@return `dict()` of the parts designed. The keys are the components references.
'''
ign_fields = [str(f.lower()) for f in ignore_fields]
logger.log(DEBUG_OVERVIEW, '# Getting from CSV \'{}\' BoM...'.format(
os.path.basename(in_file)) )
try:
file_h = open(in_file, 'r')
content = file_h.read()
except UnicodeDecodeError: # It happens with some Windows CSV files on Python 3.
file_h.close()
file_h = open(in_file, 'r', encoding='ISO-8859-1')
content = file_h.read()
file_h.close()
# Collapse multiple, consecutive tabs.
content = re.sub('\t+', '\t', content)
# Determine the column delimiter used in the CSV file.
try:
dialect = csv.Sniffer().sniff(content, [',',';','\t'])
fields[i][field_name_translations.get(hdr.lower(),hdr.lower())] = v.strip()
return refs, fields
# Read-in the schematic XML file to get a tree and get its root.
logger.log(DEBUG_OVERVIEW, '# Getting from XML \'{}\' Altium BoM...'.format(
os.path.basename(in_file)) )
file_h = open(in_file)
root = BeautifulSoup(file_h, 'lxml')
file_h.close()
# Get the header of the XML file of Altium, so KiCost is able to to
# to get all the informations in the file.
logger.log(DEBUG_OVERVIEW, 'Getting the XML table header...')
header = [ extract_field(entry, 'name') for entry in root.find('columns').find_all('column') ]
logger.log(DEBUG_OVERVIEW, 'Getting components...')
accepted_components = {}
for row in root.find('rows').find_all('row'):
# Get the values for the fields in each library part (if any).
refs, fields = extract_fields_row(row, variant, header)
for i in range(len(refs)):
ref = refs[i]
ref = re.sub('\+$', 'p', ref) # Finishing "+".
ref = re.sub(PART_REF_REGEX_NOT_ALLOWED, '', ref) # Generic special characters not allowed. To work around #ISSUE #89.
ref = re.sub('\-+', '-', ref) # Double "-".
ref = re.sub('^\-', '', ref) # Starting "-".
ref = re.sub('\-$', 'n', ref) # Finishing "-".
if not re.search('\d$', ref):
ref += '0'
accepted_components[ re.sub(PART_REF_REGEX_NOT_ALLOWED, '', ref) ] = fields[i]
if name not in ('manf#', 'manf') and name[:-1] not in distributor_dict:
if SEPRTR not in name: # This field has no distributor.
name = 'local:' + name # Assign it to a local distributor.
for i in range(qty):
if len(value)==qty:
v = value[i]
else:
v = value[0] # Footprint is just one for group.
# Do not create empty fields. This is useful
# when used more than one `manf#` alias in one designator.
if v and v!=ALTIUM_NONE:
fields[i][field_name_translations.get(hdr.lower(),hdr.lower())] = v.strip()
return refs, fields
# Read-in the schematic XML file to get a tree and get its root.
logger.log(DEBUG_OVERVIEW, '# Getting from XML \'{}\' Altium BoM...'.format(
os.path.basename(in_file)) )
file_h = open(in_file)
root = BeautifulSoup(file_h, 'lxml')
file_h.close()
# Get the header of the XML file of Altium, so KiCost is able to to
# to get all the informations in the file.
logger.log(DEBUG_OVERVIEW, 'Getting the XML table header...')
header = [ extract_field(entry, 'name') for entry in root.find('columns').find_all('column') ]
logger.log(DEBUG_OVERVIEW, 'Getting components...')
accepted_components = {}
for row in root.find('rows').find_all('row'):
# Get the values for the fields in each library part (if any).
refs, fields = extract_fields_row(row, variant, header)
if name not in ('manf#', 'manf') and name[:-1] not in distributor_dict:
if SEPRTR not in name: # This field has no distributor.
name = 'local:' + name # Assign it to a local distributor.
for i in range(qty):
if len(value)==qty:
v = value[i]
else:
v = value[0] # Footprint is just one for group.
# Do not create empty fields. This is useful
# when used more than one `manf#` alias in one designator.
if v and v!=ALTIUM_NONE:
fields[i][field_name_translations.get(hdr.lower(),hdr.lower())] = v.strip()
return refs, fields
# Read-in the schematic XML file to get a tree and get its root.
logger.log(DEBUG_OVERVIEW, '# Getting from XML \'{}\' Altium BoM...'.format(
os.path.basename(in_file)) )
file_h = open(in_file)
root = BeautifulSoup(file_h, 'lxml')
file_h.close()
# Get the header of the XML file of Altium, so KiCost is able to to
# to get all the informations in the file.
logger.log(DEBUG_OVERVIEW, 'Getting the XML table header...')
header = [ extract_field(entry, 'name') for entry in root.find('columns').find_all('column') ]
logger.log(DEBUG_OVERVIEW, 'Getting components...')
accepted_components = {}
for row in root.find('rows').find_all('row'):
# Get the values for the fields in each library part (if any).
refs, fields = extract_fields_row(row, variant, header)
# Find the components used in the schematic and elaborate
# them with global values from the libraries and local values
# from the schematic.
logger.log(DEBUG_OVERVIEW, 'Getting components...')
components = {}
for c in root.find('components').find_all('comp'):
# Find the library used for this component.
libsource = c.find('libsource')
if libsource:
# Create the key to look up the part in the libparts dict.
#libpart = str(libsource['lib'] + SEPRTR + libsource['part'])
libpart = str(libsource['lib']) + SEPRTR + str(libsource['part'])
else:
libpart = '???'
logger.log(DEBUG_OVERVIEW, 'Fottprint library not assigned to {}'.format(''))#TODO
# Initialize the fields from the global values in the libparts dict entry.
# (These will get overwritten by any local values down below.)
# (Use an empty dict if no part exists in the library.)
fields = libparts.get(libpart, dict()).copy() # Make a copy! Don't use reference!
try:
del fields['refs'] # Delete this entry that was creating problem
# to group parts of differents sheets ISSUE #97.
except KeyError:
pass
# Store the part key and its value.
fields['libpart'] = libpart
# Get the footprint for the part (if any) from the schematic.
try:
fields[i][field_name_translations.get(hdr.lower(),hdr.lower())] = v.strip()
return refs, fields
# Read-in the schematic XML file to get a tree and get its root.
logger.log(DEBUG_OVERVIEW, '# Getting from XML \'{}\' Altium BoM...'.format(
os.path.basename(in_file)) )
file_h = open(in_file)
root = BeautifulSoup(file_h, 'lxml')
file_h.close()
# Get the header of the XML file of Altium, so KiCost is able to to
# to get all the informations in the file.
logger.log(DEBUG_OVERVIEW, 'Getting the XML table header...')
header = [ extract_field(entry, 'name') for entry in root.find('columns').find_all('column') ]
logger.log(DEBUG_OVERVIEW, 'Getting components...')
accepted_components = {}
for row in root.find('rows').find_all('row'):
# Get the values for the fields in each library part (if any).
refs, fields = extract_fields_row(row, variant, header)
for i in range(len(refs)):
ref = refs[i]
ref = re.sub('\+$', 'p', ref) # Finishing "+".
ref = re.sub(PART_REF_REGEX_NOT_ALLOWED, '', ref) # Generic special characters not allowed. To work around #ISSUE #89.
ref = re.sub('\-+', '-', ref) # Double "-".
ref = re.sub('^\-', '', ref) # Starting "-".
ref = re.sub('\-$', 'n', ref) # Finishing "-".
if not re.search('\d$', ref):
ref += '0'
accepted_components[ re.sub(PART_REF_REGEX_NOT_ALLOWED, '', ref) ] = fields[i]
if len(ocurrences)>1:
if f=='desc' and len(ocurrences)==2 and '' in ocurrences.keys():
value = ''.join(list(ocurrences.keys()))
else:
value = SGROUP_SEPRTR.join( [','.join( order_refs(r) ) + SEPRTR + ' ' + t for t,r in ocurrences.items()] )
for r in grp.refs:
components[r][f] = value
#print('\n\n\n3++++++++++++++',len(new_component_groups))
#for grp in new_component_groups:
# print(grp.refs)
# for r in grp.refs:
# print(r, components[r])
# Now get the values of all fields within the members of a group.
# These will become the field values for ALL members of that group.
logger.log(DEBUG_OVERVIEW, 'Propagating field values to identical components...')
for grp in new_component_groups:
grp_fields = {}
qty = []
for ref in grp.refs:
for key, val in list(components[ref].items()):
if key == 'manf#_qty':
try:
for i in range(len(val)):
grp_fields['manf#_qty'][i] += '+' + val[i] # DUMMY way and need improvement to really do arithmetic and not string cat. #TODO
val[i] = grp_fields['manf#_qty'][i] # Make the first values take also equal.
except:
grp_fields['manf#_qty'] = val
continue
if val is None: # Field with no value...
continue # so ignore it.
if grp_fields.get(key): # This field has been seen before.
# number or a distributors catalog number, then add
# it to 'local' if it doesn't start with a distributor
# name and colon.
if name not in ('manf#', 'manf') and name[:-1] not in distributor_dict:
if SEPRTR not in name: # This field has no distributor.
name = 'local:' + name # Assign it to a local distributor.
value = str(f.string)
if value:
fields[name] = value
except AttributeError:
pass # No fields found for this part.
return fields
# Read-in the schematic XML file to get a tree and get its root.
logger.log(DEBUG_OVERVIEW, '# Getting from XML \'{}\' KiCad BoM...'.format(
os.path.basename(in_file)) )
file_h = open(in_file)
root = BeautifulSoup(file_h, 'lxml')
file_h.close()
# Get the general information of the project BoM XML file.
logger.log(DEBUG_OVERVIEW, 'Getting authorship data...')
title = root.find('title_block')
def title_find_all(data, field):
'''Helper function for finding title info, especially if it is absent.'''
try:
return data.find_all(field)[0].string
except (AttributeError, IndexError):
return None
prj_info = dict()
prj_info['title'] = title_find_all(title, 'title') or os.path.basename( in_file )
# Store the field dict under the key made from the
# concatenation of the library and part names.
libparts[str(p['lib']) + SEPRTR + str(p['part'])] = fields
# Also have to store the fields under any part aliases.
try:
for alias in p.find('aliases').find_all('alias'):
libparts[str(p['lib']) + SEPRTR + str(alias.string)] = fields
except AttributeError:
pass # No aliases for this part.
# Find the components used in the schematic and elaborate
# them with global values from the libraries and local values
# from the schematic.
logger.log(DEBUG_OVERVIEW, 'Getting components...')
components = {}
for c in root.find('components').find_all('comp'):
# Find the library used for this component.
libsource = c.find('libsource')
if libsource:
# Create the key to look up the part in the libparts dict.
#libpart = str(libsource['lib'] + SEPRTR + libsource['part'])
libpart = str(libsource['lib']) + SEPRTR + str(libsource['part'])
else:
libpart = '???'
logger.log(DEBUG_OVERVIEW, 'Footprint library not assigned to {}'.format(''))#TODO
# Initialize the fields from the global values in the libparts dict entry.
# (These will get overwritten by any local values down below.)
# (Use an empty dict if no part exists in the library.)
# with duplicated information that could be found by
# translating header titles that are the same for KiCost.
# Set some key with default values, needed for KiCost.
# Have to be created after the loop above because of the
# warning in the case of trying to re-write a key.
if 'libpart' not in fields: fields['libpart'] = 'Lib:???'
if 'footprint' not in fields: fields['footprint'] = 'Foot:???'
if 'value' not in fields: fields['value'] = '???'
return refs, fields
extract_fields.gen_cntr = 0
# Make a dictionary from the fields in the parts library so these field
# values can be instantiated into the individual components in the schematic.
logger.log(DEBUG_OVERVIEW, 'Getting parts...')
# Read the each line content.
accepted_components = {}
for row in content:
# Get the values for the fields in each library part (if any).
try:
refs, fields = extract_fields(row)
except:
# If error in one line, try get the part proprieties in last one.
continue
for ref in refs:
accepted_components[ref] = fields
# Not founded project information at the file content.
prj_info = {'title': os.path.basename( in_file ),
'company': None,