Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
# These include all the manufacture company and codes, distributors codes
# recognized by the insalled modules and, quantity and sub quantity of the part.
FIELDS_MANF = (['manf#', 'manf#_qty', 'manf'] + [d + '#' for d in distributor_dict] + [d + '#_qty' for d in distributor_dict])
# Check if was asked to merge some not allowed fiels (as `manf`, `manf# ...
# other ones as `desc` and even `value` and `footprint`may be merged due
# the different typed (1uF and 1u) or footprint library names to the same one.
fields_merge = list( [field_name_translations.get(f.lower(),f.lower()) for f in fields_merge] )
for c in FIELDS_MANF:
if c in fields_merge:
exit('Manufactutor/distributor codes and manufacture company "{}" can\'t be ignored to create the components groups.'.format(c))
# Now partition the parts into groups of like components.
# First, get groups of identical components but ignore any manufacturer's
# part numbers that may be assigned. Just collect those in a list for each group.
logger.log(DEBUG_OVERVIEW, 'Getting groups of identical components...')
component_groups = {}
for ref, fields in list(components.items()): # part references and field values.
# Take the field keys and values of each part and create a hash.
# Use the hash as the key to a dictionary that stores lists of
# part references that have identical field values. The important fields
# are the reference prefix ('R', 'C', etc.), value, and footprint.
# Don't use the manufacturer's part number when calculating the hash!
# Also, don't use any fields with SEPRTR in the label because that indicates
# a field used by a specific tool (including kicost).
hash_fields = {k: fields[k] for k in fields if k not in FIELDS_MANF+fields_merge and SEPRTR not in k}
h = hash(tuple(sorted(hash_fields.items())))
# Now add the hashed component to the group with the matching hash
# or create a new group if the hash hasn't been seen before.
try:
def subpartqty_split(components):
'''@brief Split the components with subparts in different components.
Take each part and the all manufacture/distributors combination
possibility to split in subpart the components part that have
more than one manufacture/distributors code.
For each designator...
For designator with a "single subpart" check with the quantity
is more than one.
@param components Part components in a `list()` of `dict()`, format given by the EDA modules.
@return Same as the input.
'''
logger.log(DEBUG_OVERVIEW, 'Spliting subparts in the manufacture / distributors codes...')
FIELDS_MANF = [d+'#' for d in distributor_dict]
FIELDS_MANF.append('manf#')
splitted_components = {}
for part_ref, part in components.items():
try:
# Divide the subparts in diferent parts keeping the other fields
# (reference, description, ...).
# First search for the used filed to manufacture/distributor numbers
# and how many subparts are in them. Use the loop also to extract the
# manufacture/distributor codes in list.
founded_fields = []
subparts_qty = 0
subparts_manf_code = dict()
for field_code in FIELDS_MANF:
# For Python 2, create unicode versions of strings.
fields['libpart'] = vals.get('libpart', 'Lib:???').decode('utf-8')
fields['footprint'] = vals.get('footprint', 'Foot:???').decode('utf-8')
fields['value'] = vals.get('value', '???').decode('utf-8')
for h in header:
if not h in (ign_fields + ['refs', 'qty']):
value = vals.get(h, '').decode('utf-8')
if value:
fields[h] = 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,
values_field = [v.get(f) or '' for k,v in components_grp.items()]
ocurrences = Counter(values_field)
ocurrences = {v_g:[ r for r in grp.refs if components[r].get(f) == v_g] for v_g in Counter(values_field)}
if len(ocurrences)>1:
value = SGROUP_SEPRTR.join( [collapse_refs(r) + SEPRTR + ' ' + t for t,r in ocurrences.items()] )
for r in grp.refs:
components[r][f] = value
#print('++++++++++++++',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 realy do arithmetic and not string cat. #TODO
val[i] = grp_fields['manf#_qty'][i] # Make the firt 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.
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...')
file_h = open(in_file)
root = BeautifulSoup(file_h, 'lxml')
file_h.close()
# 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 library...')
libparts = {}
component_groups = {}
# Get the header of the XML file of Altium, so KiCost is able to to
# to get all the informations in the file.
header = [ extract_field(entry, 'name') for entry in root.find('columns').find_all('column') ]
accepted_components = {}
for row in root.find('rows').find_all('row'):
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...')
file_h = open(in_file)
root = BeautifulSoup(file_h, 'lxml')
file_h.close()
# 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 library...')
libparts = {}
component_groups = {}
# Get the header of the XML file of Altium, so KiCost is able to to
# to get all the informations in the file.
header = [ extract_field(entry, 'name') for entry in root.find('columns').find_all('column') ]
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)
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.
except KeyError: # When use local distributor with personalized name.
dist_module = dist_modules[distributor_dict[dist_name]['module']]
try:
if distributor_dict[dist_name]['scrape']=='web':
# Not make sense to configurate a local distributor (yet).
locale_currency = re.findall('\w{2,}', locale_currency)
locale = None
currency = None
for alpha in locale_currency:
if len(alpha)==2:
locale = alpha
elif len(alpha)==3:
currency = alpha
dist_module.define_locale_currency(locale_iso=locale, currency_iso=currency)
except AttributeError:
logger.warning('No currency/country configuration for {}.'.format(distributor_dict[dist_name]['label']))
pass
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 componentes references.
'''
ign_fields = [str(f.lower()) for f in ignore_fields]
logger.log(DEBUG_OVERVIEW, 'Getting from CSV BoM...')
file_h = open(in_file)
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'])
except csv.Error:
# If the CSV file only has a single column of data, there may be no
# delimiter so just set the delimiter to a comma.
dialect = csv.Sniffer().sniff(',,,', [','])
# The first line in the file must be the column header.