New filter `field_modify`
- Also added `pre_transform` shortcut.
This commit is contained in:
parent
82b299a159
commit
925d3596dd
|
|
@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Command line option to specify warnings to be excluded. Useful for
|
||||
warnings issued before applying the global options (i.e during
|
||||
import). (#296)
|
||||
- `pre_transform` filter to outputs supporting variants.
|
||||
- New outputs:
|
||||
- PCB_Variant: saves a PCB with filters and variants applied.
|
||||
- Copy_Files: used to copy files to the output directory. (#279)
|
||||
|
|
@ -45,7 +46,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Internal BoM: Now you can aggregate components using CSV files. (See #248)
|
||||
- Now you can check PCB and schematic parity using the `update_xml` preflight
|
||||
(See #297)
|
||||
- New filter `urlify` to convert URLs in fields to HTML links (#311)
|
||||
- New filters:
|
||||
- `urlify` to convert URLs in fields to HTML links (#311)
|
||||
- `field_modify` a more generic field transformer
|
||||
|
||||
### Fixed
|
||||
- Problems to compress netlists. (#287)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2022 Salvador E. Tropea
|
||||
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
# Description: Applies changes to fields
|
||||
import re
|
||||
from .optionable import Optionable
|
||||
from .error import KiPlotConfigurationError
|
||||
from .fil_base import BaseFilter
|
||||
from .macros import macros, document, filter_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
|
||||
logger = log.get_logger()
|
||||
|
||||
|
||||
@filter_class
|
||||
class Field_Modify(BaseFilter): # noqa: F821
|
||||
""" Field_Modify
|
||||
Changes the content of one or more fields """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._is_transform = True
|
||||
with document:
|
||||
self.fields = Optionable
|
||||
""" [string|list(string)='Datasheet'] Fields to convert """
|
||||
self.regex = r'(https?://\S+)'
|
||||
""" Regular expression to match the field content.
|
||||
Only fields that matches will be modified.
|
||||
An empty regex will match anything.
|
||||
The example matches an HTTP URL """
|
||||
self.replace = r'<a href="\1">\1</a>'
|
||||
""" Text to replace, can contain references to sub-expressions.
|
||||
The example converts an HTTP URL into an HTML link, like the URLify filter """
|
||||
self.include = Optionable
|
||||
""" [string|list(string)=''] Name of the filter to select which components will be affected.
|
||||
Applied to all if nothing specified here """
|
||||
self._fields_example = 'Datasheet'
|
||||
self._include_solved = False
|
||||
|
||||
def config(self, parent):
|
||||
super().config(parent)
|
||||
if isinstance(self.fields, type):
|
||||
self.fields = ['Datasheet']
|
||||
self.fields = Optionable.force_list(self.fields)
|
||||
try:
|
||||
self._regex = re.compile(self.regex)
|
||||
except Exception as e:
|
||||
raise KiPlotConfigurationError('Invalid regular expression '+str(e))
|
||||
|
||||
def filter(self, comp):
|
||||
""" Apply the regex substitution """
|
||||
if not self._include_solved:
|
||||
self.include = BaseFilter.solve_filter(self.include, 'include')
|
||||
self._include_solved = True
|
||||
if self.include and not self.include.filter(comp):
|
||||
return
|
||||
for fld in self.fields:
|
||||
value = comp.get_field_value(fld)
|
||||
if not value:
|
||||
continue
|
||||
new_value = self._regex.sub(self.replace, value)
|
||||
if new_value != value:
|
||||
logger.debugl(2, '{}: {} -> {}'.format(fld, value, new_value))
|
||||
comp.set_field(fld, new_value)
|
||||
|
|
@ -22,7 +22,7 @@ else:
|
|||
from pcbnew import EDGE_MODULE, wxPoint, LSET
|
||||
from .registrable import RegOutput
|
||||
from .optionable import Optionable, BaseOptions
|
||||
from .fil_base import BaseFilter, apply_fitted_filter, reset_filters
|
||||
from .fil_base import BaseFilter, apply_fitted_filter, reset_filters, apply_pre_transform
|
||||
from .kicad.config import KiConf
|
||||
from .macros import macros, document # noqa: F401
|
||||
from .error import KiPlotConfigurationError
|
||||
|
|
@ -196,6 +196,9 @@ class VariantOptions(BaseOptions):
|
|||
self.dnf_filter = Optionable
|
||||
""" [string|list(string)='_none'] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill """
|
||||
self.pre_transform = Optionable
|
||||
""" [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
|
||||
A short-cut to use for simple cases where a variant is an overkill """
|
||||
super().__init__()
|
||||
self._comps = None
|
||||
self.undo_3d_models = {}
|
||||
|
|
@ -205,6 +208,7 @@ class VariantOptions(BaseOptions):
|
|||
super().config(parent)
|
||||
self.variant = RegOutput.check_variant(self.variant)
|
||||
self.dnf_filter = BaseFilter.solve_filter(self.dnf_filter, 'dnf_filter')
|
||||
self.pre_transform = BaseFilter.solve_filter(self.pre_transform, 'pre_transform', is_transform=True)
|
||||
|
||||
def get_refs_hash(self):
|
||||
if not self._comps:
|
||||
|
|
@ -715,7 +719,7 @@ class VariantOptions(BaseOptions):
|
|||
|
||||
def run(self, output_dir):
|
||||
""" Makes the list of components available """
|
||||
if not self.dnf_filter and not self.variant:
|
||||
if not self.dnf_filter and not self.variant and not self.pre_transform:
|
||||
return
|
||||
load_sch()
|
||||
# Get the components list from the schematic
|
||||
|
|
@ -723,6 +727,7 @@ class VariantOptions(BaseOptions):
|
|||
get_board_comps_data(comps)
|
||||
# Apply the filter
|
||||
reset_filters(comps)
|
||||
comps = apply_pre_transform(comps, self.pre_transform)
|
||||
apply_fitted_filter(comps, self.dnf_filter)
|
||||
# Apply the variant
|
||||
if self.variant:
|
||||
|
|
|
|||
|
|
@ -428,6 +428,9 @@ class BoMOptions(BaseOptions):
|
|||
self.csv = BoMCSV
|
||||
""" *[dict] Options for the CSV, TXT and TSV formats """
|
||||
# * Filters
|
||||
self.pre_transform = Optionable
|
||||
""" [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
|
||||
This option is for simple cases, consider using a full variant for complex cases """
|
||||
self.exclude_filter = Optionable
|
||||
""" [string|list(string)='_mechanical'] Name of the filter to exclude components from BoM processing.
|
||||
The default filter excludes test points, fiducial marks, mounting holes, etc.
|
||||
|
|
@ -551,8 +554,8 @@ class BoMOptions(BaseOptions):
|
|||
self.variant.variant = []
|
||||
self.variant.name = 'default'
|
||||
# Delegate any filter to the variant
|
||||
self.variant.set_def_filters(self.exclude_filter, self.dnf_filter, self.dnc_filter)
|
||||
self.exclude_filter = self.dnf_filter = self.dnc_filter = None
|
||||
self.variant.set_def_filters(self.exclude_filter, self.dnf_filter, self.dnc_filter, self.pre_transform)
|
||||
self.exclude_filter = self.dnf_filter = self.dnc_filter = self.pre_transform = None
|
||||
self.variant.config(self) # Fill or adjust any detail
|
||||
|
||||
def process_columns_config(self, cols, valid_columns, extra_columns, add_all=True):
|
||||
|
|
@ -662,6 +665,7 @@ class BoMOptions(BaseOptions):
|
|||
if isinstance(self.component_aliases, type):
|
||||
self.component_aliases = DEFAULT_ALIASES
|
||||
# Filters
|
||||
self.pre_transform = BaseFilter.solve_filter(self.pre_transform, 'pre_transform')
|
||||
self.exclude_filter = BaseFilter.solve_filter(self.exclude_filter, 'exclude_filter')
|
||||
self.dnf_filter = BaseFilter.solve_filter(self.dnf_filter, 'dnf_filter')
|
||||
self.dnc_filter = BaseFilter.solve_filter(self.dnc_filter, 'dnc_filter')
|
||||
|
|
@ -840,6 +844,7 @@ class BoMOptions(BaseOptions):
|
|||
self.aggregate_comps(comps)
|
||||
# Apply all the filters
|
||||
reset_filters(comps)
|
||||
comps = apply_pre_transform(comps, self.pre_transform)
|
||||
apply_exclude_filter(comps, self.exclude_filter)
|
||||
apply_fitted_filter(comps, self.dnf_filter)
|
||||
apply_fixed_filter(comps, self.dnc_filter)
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ class IBoMOptions(VariantOptions):
|
|||
self.blacklist += to_remove
|
||||
# Convert attributes into options
|
||||
for k, v in self.get_attrs_gen():
|
||||
if not v or k in ['output', 'variant', 'dnf_filter']:
|
||||
if not v or k in ['output', 'variant', 'dnf_filter', 'pre_transform']:
|
||||
continue
|
||||
cmd.append(BaseOutput.attr2longopt(k)) # noqa: F821
|
||||
if not isinstance(v, bool): # must be str/(int, float)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020 Salvador E. Tropea
|
||||
# Copyright (c) 2020 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2020-2022 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
# Description: Implements the KiBoM variants mechanism.
|
||||
|
|
@ -22,6 +22,7 @@ class KiBoM(BaseVariant): # noqa: F821
|
|||
+VARIANT includes the component only if we are using this variant """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._def_pre_transform = None
|
||||
self._def_exclude_filter = None
|
||||
self._def_dnf_filter = None
|
||||
self._def_dnc_filter = None
|
||||
|
|
@ -43,22 +44,25 @@ class KiBoM(BaseVariant): # noqa: F821
|
|||
""" Returns the name of the field used to determine if the component belongs to the variant """
|
||||
return self.config_field
|
||||
|
||||
def set_def_filters(self, exclude_filter, dnf_filter, dnc_filter):
|
||||
def set_def_filters(self, exclude_filter, dnf_filter, dnc_filter, pre_transform):
|
||||
""" Filters delegated to the variant """
|
||||
self._def_exclude_filter = exclude_filter
|
||||
self._def_dnf_filter = dnf_filter
|
||||
self._def_dnc_filter = dnc_filter
|
||||
self._def_pre_transform = pre_transform
|
||||
|
||||
def config(self, parent):
|
||||
# Now we can let the parent initialize the filters
|
||||
super().config(parent)
|
||||
# Variants, ensure a lowercase list
|
||||
self.variant = [v.lower() for v in self.force_list(self.variant)]
|
||||
self.pre_transform = BaseFilter.solve_filter(self.pre_transform, 'pre_transform', is_transform=True)
|
||||
# Filters priority:
|
||||
# 1) Defined here
|
||||
# 2) Delegated from the output format
|
||||
# 3) KiBoM default behavior
|
||||
# pre_transform
|
||||
self.pre_transform = BaseFilter.solve_filter(self.pre_transform, 'pre_transform', self._def_pre_transform,
|
||||
is_transform=True)
|
||||
# exclude_filter
|
||||
if not self._def_exclude_filter:
|
||||
self._def_exclude_filter = IFILT_MECHANICAL
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# Example KiBot config file
|
||||
kibot:
|
||||
version: 1
|
||||
|
||||
filters:
|
||||
- name: 'not_u2'
|
||||
type: generic
|
||||
exclude_refs: ['U2']
|
||||
- name: ds_link
|
||||
type: field_modify
|
||||
#include: 'not_u2'
|
||||
|
||||
outputs:
|
||||
- name: 'bom_internal'
|
||||
comment: "iBoM with datasheet"
|
||||
type: ibom
|
||||
dir: BoM
|
||||
options:
|
||||
extra_fields: Datasheet
|
||||
# pre_transform: _datasheet_link
|
||||
pre_transform: ds_link
|
||||
Loading…
Reference in New Issue