Changed: The `%d/%sd/%bd` expansion patterns are now affected by the global `date_format`.
- Can be disabled using `date_reformat: false`. - Related to #121
This commit is contained in:
parent
631cef7fd9
commit
3eb82bc86d
|
|
@ -63,6 +63,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
a context not related to variants. I.e. when a `compress` target expands
|
||||
`%v`.
|
||||
- Now you get an error when defining two outputs with the same name.
|
||||
- The `%d/%sd/%bd` expansion patterns are now affected by the global `date_format`.
|
||||
Can be disabled using `date_reformat: false`. (#121)
|
||||
|
||||
### Fixed
|
||||
- Position files now defaults to use the auxiliar origin as KiCad.
|
||||
|
|
|
|||
|
|
@ -312,7 +312,10 @@ The behavior of these patterns isn't fully defined in this case and the results
|
|||
|
||||
#### Date format option
|
||||
|
||||
* The **%d**, **%sd** and **%bd** patterns use the date and time from the PCB and schematic. When abscent they use the file timestamp. The `date_time_format` global option controls the format used.
|
||||
* The **%d**, **%sd** and **%bd** patterns use the date and time from the PCB and schematic.
|
||||
When abscent they use the file timestamp, and the `date_time_format` global option controls the format used.
|
||||
When available, and in ISO format, the `date_format` controls the format used.
|
||||
You can disable this reformatting assigning `false` to the `date_reformat` option.
|
||||
* The **%D** format is controlled by the `date_format` global option.
|
||||
* The **%T** format is controlled by the `time_format` global option.
|
||||
|
||||
|
|
|
|||
|
|
@ -254,7 +254,10 @@ The behavior of these patterns isn't fully defined in this case and the results
|
|||
|
||||
#### Date format option
|
||||
|
||||
* The **%d**, **%sd** and **%bd** patterns use the date and time from the PCB and schematic. When abscent they use the file timestamp. The `date_time_format` global option controls the format used.
|
||||
* The **%d**, **%sd** and **%bd** patterns use the date and time from the PCB and schematic.
|
||||
When abscent they use the file timestamp, and the `date_time_format` global option controls the format used.
|
||||
When available, and in ISO format, the `date_format` controls the format used.
|
||||
You can disable this reformatting assigning `false` to the `date_reformat` option.
|
||||
* The **%D** format is controlled by the `date_format` global option.
|
||||
* The **%T** format is controlled by the `time_format` global option.
|
||||
|
||||
|
|
|
|||
|
|
@ -31,9 +31,14 @@ class Globals(FiltersOptions):
|
|||
self.date_time_format = '%Y-%m-%d_%H-%M-%S'
|
||||
""" Format used for the PCB and schematic date when using the file timestamp. Uses the `strftime` format """
|
||||
self.date_format = '%Y-%m-%d'
|
||||
""" Format used for the day we started the script. Uses the `strftime` format """
|
||||
""" Format used for the day we started the script.
|
||||
Is also used for the PCB/SCH date formatting when `time_reformat` is enabled (default behavior).
|
||||
Uses the `strftime` format """
|
||||
self.time_format = '%H-%M-%S'
|
||||
""" Format used for the time we started the script. Uses the `strftime` format """
|
||||
self.time_reformat = True
|
||||
""" Tries to reformat the PCB/SCH date using the `date_format`.
|
||||
This assumes you let KiCad fill this value and hence the time is in ISO format (YY-MM-DD) """
|
||||
self.set_doc('filters', " [list(dict)] KiBot warnings to be ignored ")
|
||||
self._filter_what = 'KiBot warnings'
|
||||
self._unkown_is_error = True
|
||||
|
|
@ -41,7 +46,7 @@ class Globals(FiltersOptions):
|
|||
|
||||
@staticmethod
|
||||
def set_global(current, new_val, opt):
|
||||
if current:
|
||||
if current is not None:
|
||||
logger.info('Using command line value `{}` for global option `{}`'.format(current, opt))
|
||||
return current
|
||||
if new_val:
|
||||
|
|
@ -56,6 +61,7 @@ class Globals(FiltersOptions):
|
|||
GS.global_date_time_format = self.set_global(GS.global_date_time_format, self.date_time_format, 'date_time_format')
|
||||
GS.global_date_format = self.set_global(GS.global_date_format, self.date_format, 'date_format')
|
||||
GS.global_time_format = self.set_global(GS.global_time_format, self.time_format, 'time_format')
|
||||
GS.global_time_reformat = self.set_global(GS.global_time_reformat, self.time_reformat, 'time_reformat')
|
||||
GS.global_kiauto_wait_start = self.set_global(GS.global_kiauto_wait_start, self.kiauto_wait_start, 'kiauto_wait_start')
|
||||
if GS.global_kiauto_wait_start and int(GS.global_kiauto_wait_start) != GS.global_kiauto_wait_start:
|
||||
GS.global_kiauto_wait_start = int(GS.global_kiauto_wait_start)
|
||||
|
|
|
|||
26
kibot/gs.py
26
kibot/gs.py
|
|
@ -4,10 +4,10 @@
|
|||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
import os
|
||||
from datetime import datetime
|
||||
from datetime import datetime, date
|
||||
from sys import exit
|
||||
from .misc import (EXIT_BAD_ARGS)
|
||||
from .log import (get_logger)
|
||||
from .misc import EXIT_BAD_ARGS, W_DATEFORMAT
|
||||
from .log import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
|
@ -87,6 +87,7 @@ class GS(object):
|
|||
global_date_time_format = None
|
||||
global_date_format = None
|
||||
global_time_format = None
|
||||
global_time_reformat = None
|
||||
test_boolean = True
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -121,6 +122,20 @@ class GS(object):
|
|||
GS.sch_com3 = GS.sch.comment3
|
||||
GS.sch_com4 = GS.sch.comment4
|
||||
|
||||
@staticmethod
|
||||
def format_date(d, fname, what):
|
||||
if not d:
|
||||
return datetime.fromtimestamp(os.path.getmtime(fname)).strftime(GS.global_date_time_format)
|
||||
elif GS.global_time_reformat:
|
||||
try:
|
||||
dt = date.fromisoformat(d)
|
||||
except ValueError as e:
|
||||
logger.warning(W_DATEFORMAT+"Trying to reformat {} time, but not in ISO format ({})".format(what, d))
|
||||
logger.warning(W_DATEFORMAT+"Problem: {}".format(e))
|
||||
return d
|
||||
return dt.strftime(GS.global_date_format)
|
||||
return d
|
||||
|
||||
@staticmethod
|
||||
def load_pcb_title_block():
|
||||
if GS.pcb_title is not None:
|
||||
|
|
@ -131,10 +146,7 @@ class GS(object):
|
|||
GS.pcb_comp = ''
|
||||
# This is based on InterativeHtmlBom expansion
|
||||
title_block = GS.board.GetTitleBlock()
|
||||
GS.pcb_date = title_block.GetDate()
|
||||
if not GS.pcb_date:
|
||||
file_mtime = os.path.getmtime(GS.pcb_file)
|
||||
GS.pcb_date = datetime.fromtimestamp(file_mtime).strftime(GS.global_date_time_format)
|
||||
GS.pcb_date = GS.format_date(title_block.GetDate(), GS.pcb_file, 'PCB')
|
||||
GS.pcb_title = title_block.GetTitle()
|
||||
if not GS.pcb_title:
|
||||
GS.pcb_title = GS.pcb_basename
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ Currently oriented to collect the components for the BoM.
|
|||
# Encapsulate file/line
|
||||
import re
|
||||
import os
|
||||
from datetime import datetime
|
||||
from copy import deepcopy
|
||||
from collections import OrderedDict
|
||||
from .config import KiConf, un_quote
|
||||
|
|
@ -961,7 +960,10 @@ class SchematicComponent(object):
|
|||
self.footprint_lib = res[0]
|
||||
self.footprint = res[1]
|
||||
else:
|
||||
raise SchFileError('Footprint with more than one colon', f.value, fr)
|
||||
if fr:
|
||||
raise SchFileError('Footprint with more than one colon', f.value, fr)
|
||||
else:
|
||||
raise SchError('Footprint with more than one colon (`{}`)'.format(f.value))
|
||||
basic += 1
|
||||
elif f.number == 3:
|
||||
self.datasheet = f.value
|
||||
|
|
@ -1498,9 +1500,7 @@ class Schematic(object):
|
|||
# Load the title block
|
||||
self._get_title_block(f)
|
||||
# Fill in some missing info
|
||||
if not self.date:
|
||||
file_mtime = os.path.getmtime(fname)
|
||||
self.date = datetime.fromtimestamp(file_mtime).strftime(GS.global_date_time_format)
|
||||
self.date = GS.format_date(self.date, fname, 'SCH')
|
||||
if not self.title:
|
||||
self.title = os.path.splitext(os.path.basename(fname))[0]
|
||||
logger.debug("SCH title: `{}`".format(self.title))
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ W_NEEDSPCB = '(W071) '
|
|||
W_NOGLOBALS = '(W072) '
|
||||
W_EMPTREP = '(W073) '
|
||||
W_BADCHARS = '(W074) '
|
||||
W_DATEFORMAT = '(W075) '
|
||||
|
||||
|
||||
class Rect(object):
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ def filter(v):
|
|||
return inspect.isclass(v) or not (callable(v) or isinstance(v, (dict, list)))
|
||||
|
||||
|
||||
def _cl(text):
|
||||
""" Eliminates dangerous characters from the text """
|
||||
return re.sub(r'[\\\/\?%\*:|"<>]', '_', text)
|
||||
|
||||
|
||||
class Optionable(object):
|
||||
""" A class to validate and hold configuration outputs/options.
|
||||
Is configured from a dict and the collected values are stored in its attributes. """
|
||||
|
|
@ -219,40 +224,40 @@ class Optionable(object):
|
|||
""" Expansions common to the PCB and Schematic """
|
||||
# PCB expansions, explicit
|
||||
if GS.board and '%b' in name:
|
||||
name = name.replace('%bc', GS.pcb_comp)
|
||||
name = name.replace('%bd', GS.pcb_date)
|
||||
name = name.replace('%bc', _cl(GS.pcb_comp))
|
||||
name = name.replace('%bd', _cl(GS.pcb_date))
|
||||
name = name.replace('%bF', GS.pcb_no_ext)
|
||||
name = name.replace('%bf', GS.pcb_basename)
|
||||
name = name.replace('%bp', GS.pcb_title)
|
||||
name = name.replace('%br', GS.pcb_rev)
|
||||
name = name.replace('%bC1', GS.pcb_com1)
|
||||
name = name.replace('%bC2', GS.pcb_com2)
|
||||
name = name.replace('%bC3', GS.pcb_com3)
|
||||
name = name.replace('%bC4', GS.pcb_com4)
|
||||
name = name.replace('%bp', _cl(GS.pcb_title))
|
||||
name = name.replace('%br', _cl(GS.pcb_rev))
|
||||
name = name.replace('%bC1', _cl(GS.pcb_com1))
|
||||
name = name.replace('%bC2', _cl(GS.pcb_com2))
|
||||
name = name.replace('%bC3', _cl(GS.pcb_com3))
|
||||
name = name.replace('%bC4', _cl(GS.pcb_com4))
|
||||
if GS.solved_global_variant:
|
||||
name = name.replace('%g', GS.solved_global_variant.file_id)
|
||||
name = name.replace('%G', GS.solved_global_variant.name)
|
||||
# Schematic expansions, explicit
|
||||
if GS.sch and '%s' in name:
|
||||
name = name.replace('%sc', GS.sch_comp)
|
||||
name = name.replace('%sd', GS.sch_date)
|
||||
name = name.replace('%sc', _cl(GS.sch_comp))
|
||||
name = name.replace('%sd', _cl(GS.sch_date))
|
||||
name = name.replace('%sF', GS.sch_no_ext)
|
||||
name = name.replace('%sf', GS.sch_basename)
|
||||
name = name.replace('%sp', GS.sch_title)
|
||||
name = name.replace('%sr', GS.sch_rev)
|
||||
name = name.replace('%sC1', GS.sch_com1)
|
||||
name = name.replace('%sC2', GS.sch_com2)
|
||||
name = name.replace('%sC3', GS.sch_com3)
|
||||
name = name.replace('%sC4', GS.sch_com4)
|
||||
name = name.replace('%sp', _cl(GS.sch_title))
|
||||
name = name.replace('%sr', _cl(GS.sch_rev))
|
||||
name = name.replace('%sC1', _cl(GS.sch_com1))
|
||||
name = name.replace('%sC2', _cl(GS.sch_com2))
|
||||
name = name.replace('%sC3', _cl(GS.sch_com3))
|
||||
name = name.replace('%sC4', _cl(GS.sch_com4))
|
||||
name = name.replace('%D', GS.n.strftime(GS.global_date_format))
|
||||
name = name.replace('%T', GS.n.strftime(GS.global_time_format))
|
||||
if self:
|
||||
name = name.replace('%i', self._expand_id)
|
||||
name = name.replace('%v', self._find_variant())
|
||||
name = name.replace('%V', self._find_variant_name())
|
||||
name = name.replace('%v', _cl(self._find_variant()))
|
||||
name = name.replace('%V', _cl(self._find_variant_name()))
|
||||
name = name.replace('%x', self._expand_ext)
|
||||
if parent and hasattr(parent, 'output_id'):
|
||||
name = name.replace('%I', parent.output_id)
|
||||
name = name.replace('%I', _cl(parent.output_id))
|
||||
return name
|
||||
|
||||
def expand_filename_both(self, name, is_sch=True):
|
||||
|
|
@ -281,27 +286,27 @@ class Optionable(object):
|
|||
# This member can be called with a preflight object
|
||||
name = Optionable.expand_filename_common(self, name, parent)
|
||||
if GS.board and do_pcb:
|
||||
name = name.replace('%c', GS.pcb_comp)
|
||||
name = name.replace('%d', GS.pcb_date)
|
||||
name = name.replace('%c', _cl(GS.pcb_comp))
|
||||
name = name.replace('%d', _cl(GS.pcb_date))
|
||||
name = name.replace('%F', GS.pcb_no_ext)
|
||||
name = name.replace('%f', GS.pcb_basename)
|
||||
name = name.replace('%p', GS.pcb_title)
|
||||
name = name.replace('%r', GS.pcb_rev)
|
||||
name = name.replace('%C1', GS.pcb_com1)
|
||||
name = name.replace('%C2', GS.pcb_com2)
|
||||
name = name.replace('%C3', GS.pcb_com3)
|
||||
name = name.replace('%C4', GS.pcb_com4)
|
||||
name = name.replace('%p', _cl(GS.pcb_title))
|
||||
name = name.replace('%r', _cl(GS.pcb_rev))
|
||||
name = name.replace('%C1', _cl(GS.pcb_com1))
|
||||
name = name.replace('%C2', _cl(GS.pcb_com2))
|
||||
name = name.replace('%C3', _cl(GS.pcb_com3))
|
||||
name = name.replace('%C4', _cl(GS.pcb_com4))
|
||||
if GS.sch and do_sch:
|
||||
name = name.replace('%c', GS.sch_comp)
|
||||
name = name.replace('%d', GS.sch_date)
|
||||
name = name.replace('%c', _cl(GS.sch_comp))
|
||||
name = name.replace('%d', _cl(GS.sch_date))
|
||||
name = name.replace('%F', GS.sch_no_ext)
|
||||
name = name.replace('%f', GS.sch_basename)
|
||||
name = name.replace('%p', GS.sch_title)
|
||||
name = name.replace('%r', GS.sch_rev)
|
||||
name = name.replace('%C1', GS.sch_com1)
|
||||
name = name.replace('%C2', GS.sch_com2)
|
||||
name = name.replace('%C3', GS.sch_com3)
|
||||
name = name.replace('%C4', GS.sch_com4)
|
||||
name = name.replace('%p', _cl(GS.sch_title))
|
||||
name = name.replace('%r', _cl(GS.sch_rev))
|
||||
name = name.replace('%C1', _cl(GS.sch_com1))
|
||||
name = name.replace('%C2', _cl(GS.sch_com2))
|
||||
name = name.replace('%C3', _cl(GS.sch_com3))
|
||||
name = name.replace('%C4', _cl(GS.sch_com4))
|
||||
# sanitize the name to avoid characters illegal in file systems
|
||||
name = name.replace('\\', '/')
|
||||
name = re.sub(r'[?%*:|"<>]', '_', name)
|
||||
|
|
|
|||
|
|
@ -875,3 +875,22 @@ def test_compress_sources_1(test_dir):
|
|||
files = ['source/'+prj+'.kicad_pcb', 'source/'+prj+'.sch', 'source/deeper.sch', 'source/sub-sheet.sch']
|
||||
ctx.test_compress(prj + '-result.tar.bz2', files)
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_date_format_1(test_dir):
|
||||
""" Date from SCH reformated """
|
||||
prj = 'test_v5'
|
||||
ctx = context.TestContext(test_dir, 'test_date_format_1', prj, 'date_format_1', '')
|
||||
ctx.run(extra=[])
|
||||
ctx.expect_out_file(POS_DIR+'/test_v5_20200812.csv')
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_date_format_2(test_dir):
|
||||
""" Date from SCH reformated """
|
||||
prj = 'bom'
|
||||
ctx = context.TestContext(test_dir, 'test_date_format_2', prj, 'date_format_1', '')
|
||||
ctx.run(extra=[])
|
||||
ctx.expect_out_file(POS_DIR+'/bom_13_07_2020.csv')
|
||||
assert ctx.search_err('Trying to reformat SCH time, but not in ISO format')
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
kibot:
|
||||
version: 1
|
||||
|
||||
global:
|
||||
output: '%f_%sd.%x'
|
||||
date_format: '%Y%m%d'
|
||||
|
||||
outputs:
|
||||
- name: 'position'
|
||||
type: position
|
||||
dir: positiondir
|
||||
options:
|
||||
format: CSV # CSV or ASCII format
|
||||
units: millimeters # millimeters or inches
|
||||
separate_files_for_front_and_back: false
|
||||
only_smd: true
|
||||
Loading…
Reference in New Issue