diff --git a/CHANGELOG.md b/CHANGELOG.md index bffe9813..3af1d509 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `gerber.gerber_job_file` option to control the gerber job file name. - `output` option to control the file name to all plot output formats. - `drill`, `drill.map` and `position` file names can be configured. +- Output file names supports expansion of various interesting values (base + name, sheet title, revision, etc.) ### Changed - Default file names for: @@ -23,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - dril: uses drill instead of drl, used in gbr and drl. - position: no -pos in CSVs - step: adds -3D + - pdf_sch_print: adds -schematic ## [0.5.0] - 2020-07-11 ### Changed diff --git a/kiplot/gs.py b/kiplot/gs.py index 6ab4b5e8..299605f4 100644 --- a/kiplot/gs.py +++ b/kiplot/gs.py @@ -1,3 +1,6 @@ +import os +import re +from datetime import datetime from sys import exit from .misc import (EXIT_BAD_ARGS) from .log import (get_logger) @@ -11,11 +14,91 @@ class GS(object): Is a static class, just a placeholder for some global variables. """ pcb_file = None + sch_basename = None sch_file = None + sch_basename = None out_dir = None filter_file = None board = None debug_enabled = False + n = datetime.now() + today = n.strftime('%Y-%m-%d') + time = n.strftime('%H-%M-%S') + # Data from the SCH because it doesn't have a Python API + sch_title = None + sch_date = None + sch_rev = None + sch_comp = None + # Data from the board title block + pcb_title = None + pcb_date = None + pcb_rev = None + pcb_comp = None + + @staticmethod + def load_sch_title_block(): + if GS.sch_title is not None: + return + GS.sch_title = '' + GS.sch_date = '' + GS.sch_rev = '' + GS.sch_comp = '' + if not GS.sch_file: + return + re_val = re.compile(r"(\w+)\s+\"([^\"]+)\"") + with open(GS.sch_file) as f: + for line in f: + m = re_val.match(line) + if not m: + continue + name, val = m.groups() + if name == "Title": + GS.sch_title = val + elif name == "Date": + GS.sch_date = val + elif name == "Rev": + GS.sch_rev = val + elif name == "Comp": + GS.sch_comp = val + elif name == "$EndDescr": + break + if not GS.sch_date: + file_mtime = os.path.getmtime(GS.sch_file) + GS.sch_file = datetime.fromtimestamp(file_mtime).strftime('%Y-%m-%d_%H-%M-%S') + GS.sch_basename = os.path.splitext(os.path.basename(GS.sch_file))[0] + if not GS.sch_title: + GS.sch_title = GS.sch_basename + logger.debug("SCH title: `{}`".format(GS.sch_title)) + logger.debug("SCH date: `{}`".format(GS.sch_date)) + logger.debug("SCH revision: `{}`".format(GS.sch_rev)) + logger.debug("SCH company: `{}`".format(GS.sch_comp)) + + @staticmethod + def load_pcb_title_block(): + if GS.pcb_title is not None: + return + GS.pcb_title = '' + GS.pcb_date = '' + GS.pcb_rev = '' + GS.pcb_comp = '' + if not GS.board: + return + # 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('%Y-%m-%d_%H-%M-%S') + GS.pcb_basename = os.path.splitext(os.path.basename(GS.pcb_file))[0] + GS.pcb_title = title_block.GetTitle() + if not GS.pcb_title: + GS.pcb_title = GS.pcb_basename + GS.pcb_rev = title_block.GetRevision() + GS.pcb_comp = title_block.GetCompany() + logger.debug("PCB title: `{}`".format(GS.pcb_title)) + logger.debug("PCB date: `{}`".format(GS.pcb_date)) + logger.debug("PCB revision: `{}`".format(GS.pcb_rev)) + logger.debug("PCB company: `{}`".format(GS.pcb_comp)) @staticmethod def check_pcb(): diff --git a/kiplot/optionable.py b/kiplot/optionable.py index fa0667ac..2ff2598f 100644 --- a/kiplot/optionable.py +++ b/kiplot/optionable.py @@ -150,31 +150,38 @@ class Optionable(object): return ((k, v) for k, v in attrs.items() if k[0] != '_') def expand_filename(self, out_dir, name, id='', ext=''): - """ Expands %x values in filenames """ + """ Expands %* values in filenames. + Uses data from the PCB. """ if GS.board: - # This is based on InterativeHtmlBom expansion - title_block = GS.board.GetTitleBlock() - file_date = title_block.GetDate() - if not file_date: - file_mtime = os.path.getmtime(GS.pcb_file) - file_date = datetime.fromtimestamp(file_mtime).strftime('%Y-%m-%d_%H-%M-%S') - pcb_file_name = os.path.splitext(os.path.basename(GS.pcb_file))[0] - title = title_block.GetTitle() - if not title: - title = pcb_file_name - revision = title_block.GetRevision() - company = title_block.GetCompany() - n = datetime.now() - today = n.strftime('%Y-%m-%d') - time = n.strftime('%H-%M-%S') + GS.load_pcb_title_block() # Do the replacements - name = name.replace('%f', pcb_file_name) - name = name.replace('%p', title) - name = name.replace('%c', company) - name = name.replace('%r', revision) - name = name.replace('%d', file_date) - name = name.replace('%D', today) - name = name.replace('%T', time) + name = name.replace('%f', GS.pcb_basename) + name = name.replace('%p', GS.pcb_title) + name = name.replace('%c', GS.pcb_comp) + name = name.replace('%r', GS.pcb_rev) + name = name.replace('%d', GS.pcb_date) + name = name.replace('%D', GS.today) + name = name.replace('%T', GS.time) + name = name.replace('%i', id) + name = name.replace('%x', ext) + # sanitize the name to avoid characters illegal in file systems + name = name.replace('\\', '/') + name = re.sub(r'[?%*:|"<>]', '_', name) + return os.path.abspath(os.path.join(out_dir, name)) + + def expand_filename_sch(self, out_dir, name, id='', ext=''): + """ Expands %* values in filenames. + Uses data from the SCH. """ + if GS.sch_file: + GS.load_sch_title_block() + # Do the replacements + name = name.replace('%f', GS.sch_basename) + name = name.replace('%p', GS.sch_title) + name = name.replace('%c', GS.sch_comp) + name = name.replace('%r', GS.sch_rev) + name = name.replace('%d', GS.sch_date) + name = name.replace('%D', GS.today) + name = name.replace('%T', GS.time) name = name.replace('%i', id) name = name.replace('%x', ext) # sanitize the name to avoid characters illegal in file systems diff --git a/kiplot/out_pdf_sch_print.py b/kiplot/out_pdf_sch_print.py index b7d611f8..4a7bc555 100644 --- a/kiplot/out_pdf_sch_print.py +++ b/kiplot/out_pdf_sch_print.py @@ -14,8 +14,8 @@ class PDF_Sch_PrintOptions(BaseOptions): def __init__(self): super().__init__() with document: - self.output = '' - """ filename for the output PDF (the name of the schematic if empty) """ # pragma: no cover + self.output = '%f-%i.%x' + """ filename for the output PDF (%i=schematic %x=pdf) """ # pragma: no cover def run(self, output_dir, board): check_eeschema_do() @@ -29,8 +29,10 @@ class PDF_Sch_PrintOptions(BaseOptions): logger.error(CMD_EESCHEMA_DO+' returned %d', ret) exit(PDF_SCH_PRINT) if self.output: - cur = os.path.abspath(os.path.join(output_dir, os.path.splitext(os.path.basename(GS.pcb_file))[0]) + '.pdf') - new = os.path.abspath(os.path.join(output_dir, self.output)) + id = 'schematic' + ext = 'pdf' + cur = self.expand_filename_sch(output_dir, '%f.%x', id, ext) + new = self.expand_filename_sch(output_dir, self.output, id, ext) logger.debug('Moving '+cur+' -> '+new) os.rename(cur, new) diff --git a/tests/board_samples/bom.sch b/tests/board_samples/bom.sch index 0dff0ddc..ee82632b 100644 --- a/tests/board_samples/bom.sch +++ b/tests/board_samples/bom.sch @@ -4,10 +4,10 @@ EELAYER END $Descr A4 11693 8268 encoding utf-8 Sheet 1 1 -Title "" -Date "" -Rev "" -Comp "" +Title "BoM Test" +Date "13/07/2020" +Rev "r1" +Comp "INTI-CMNB" Comment1 "" Comment2 "" Comment3 "" diff --git a/tests/test_plot/test_print_sch.py b/tests/test_plot/test_print_sch.py index a13952ba..57d7b965 100644 --- a/tests/test_plot/test_print_sch.py +++ b/tests/test_plot/test_print_sch.py @@ -23,7 +23,7 @@ PDF_DIR = '' PDF_FILE = 'Schematic.pdf' -def test_print_sch(): +def test_print_sch_ok(): prj = 'bom' ctx = context.TestContext('PrSCH', prj, 'print_sch', PDF_DIR) ctx.run()