def gen_pcb_dxf(projfolder, force=False): """ Generates a DXF file of the PCB provided by the gEDA project. The pcb file is the one listed in the gEDA project file, and the pcbname is the one specified in the :mod:`tendril.gedaif.conffile.ConfigsFile`. This function does not use jinja2 and latex. It relies on :func:`tendril.connectors.geda.pcb.conv_pcb2dxf` instead. :param projfolder: The gEDA project folder. :type projfolder: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output File : ``<projectfolder>/pcb/<pcbfile>.dxf`` * Source Files : The project's `.pcb` file. """ configfile = conffile.ConfigsFile(projfolder) gpf = projfile.GedaProjectFile(configfile.projectfolder) pcb_mtime = fsutils.get_file_mtime( os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb'), ) if pcb_mtime is None: logger.warning("PCB does not seem to exist for : " + projfolder) return docfolder = get_project_doc_folder(projfolder) dxffile = path.normpath(os.path.join(docfolder, os.pardir, configfile.pcbname + '.dxf')) bottom_dxffile = path.normpath(os.path.join(docfolder, os.pardir, configfile.pcbname + 'bottom.dxf')) outf_mtime = fsutils.get_file_mtime(dxffile, fs=refdoc_fs) if not force and outf_mtime is not None and outf_mtime > pcb_mtime: logger.debug('Skipping up-to-date ' + dxffile) return dxffile logger.info('Regenerating ' + dxffile + os.linesep + 'Last modified : ' + str(pcb_mtime) + '; Last Created : ' + str(outf_mtime)) workspace_folder = workspace_fs.getsyspath(path.dirname(dxffile)) workspace_fs.makedir(path.dirname(dxffile), recursive=True, allow_recreate=True) pcb.conv_pcb2dxf( os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb'), workspace_folder, configfile.pcbname ) copyfile(workspace_fs, dxffile, refdoc_fs, dxffile, overwrite=True) copyfile(workspace_fs, bottom_dxffile, refdoc_fs, bottom_dxffile, overwrite=True) return dxffile
def gen_cobom_csv(projfolder, namebase, force=False): """ Generates a CSV file in the :mod:`tendril.boms.outputbase.CompositeOutputBom` format, including the BOMs of the all the defined configurations of the project. This function uses a :mod:`csv.writer` instead of rendering a jinja2 template. It also generates configdocs for all the defined configurations of the project, using :func:`gen_confpdf`. :param projfolder: The gEDA project folder. :type projfolder: str :param namebase: The project name. :type namebase: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output Files : ``<project_doc_folder>/confdocs/conf_boms.csv`` * Also triggers : :func:`gen_confpdf` for all listed configurations. * Source Files : The project's schematic folder. """ gpf = projfile.GedaProjectFile(projfolder) configfile = conffile.ConfigsFile(projfolder) sch_mtime = fsutils.get_folder_mtime(gpf.configsfile.schfolder) docfolder = get_project_doc_folder(projfolder) cobom_csv_path = path.join(docfolder, 'confdocs', 'conf-boms.csv') outf_mtime = fsutils.get_file_mtime(cobom_csv_path, fs=refdoc_fs) if not force and outf_mtime is not None and outf_mtime > sch_mtime: logger.debug('Skipping up-to-date ' + cobom_csv_path) return cobom_csv_path logger.info('Regenerating ' + cobom_csv_path + os.linesep + 'Last modified : ' + str(sch_mtime) + '; Last Created : ' + str(outf_mtime)) bomlist = [] for cfn in configfile.configuration_names: gen_confpdf(projfolder, cfn, namebase, force=force) lbom = boms_electronics.import_pcb(projfolder) lobom = lbom.create_output_bom(cfn) bomlist.append(lobom) cobom = boms_outputbase.CompositeOutputBom(bomlist) with refdoc_fs.open(cobom_csv_path, 'wb') as f: writer = csv.writer(f) writer.writerow(['device'] + [x.configname for x in cobom.descriptors]) for line in cobom.lines: writer.writerow([line.ident] + line.columns)
def generate_pcb_pricing(projfolder, noregen=True, forceregen=False): gpf = projfile.GedaProjectFile(projfolder) try: pcbparams = gpf.configsfile.configdata['pcbdetails']['params'] except KeyError: logger.warning('Geda project does not seem have pcb details. ' 'Not generating PCB pricing information : ' + projfolder) return None try: if gpf.configsfile.configdata['pcbdetails']['params'][ 'panelize'] is True: logger.warning('Not obtaining pricing for panelized pcb : ' + projfolder) return None except KeyError: pass try: searchparams = gpf.configsfile.configdata['pcbdetails'][ 'indicativepricing'] except KeyError: searchparams = { 'qty': 20, 'dterm': 7, } pricingfp = os.path.join(gpf.configsfile.projectfolder, 'pcb', 'sourcing.yaml') if noregen is True: if os.path.exists(pricingfp): return pricingfp if forceregen is False: pcb_mtime = fsutils.get_file_mtime( os.path.join(gpf.configsfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb')) # noqa outf_mtime = fsutils.get_file_mtime(pricingfp) if outf_mtime is not None and outf_mtime > pcb_mtime: logger.info('Skipping up-to-date ' + pricingfp) return pricingfp logger.info('Generating PCB Pricing for ' + pricingfp) pcbparams['qty'] = range(searchparams['qty']) sourcingdata = get_csil_prices(pcbparams) dumpdata = {'params': pcbparams, 'pricing': sourcingdata} with open(pricingfp, 'w') as pricingf: pricingf.write(yaml.dump(dumpdata)) return pricingfp
def get_img_list(projfolder, cardname=None): """ Returns a list of :class:`docstore.ExposedDocument` instances, pointing to the generated renders for the gEDA project or card specified by the parameters. Currently, the ``cardname`` parameter is ignored, since no configuration specific images are generated. :param projfolder: The gEDA project folder. :param cardname: The cardname. :return: list of :class:`ExposedDocument` """ configfile = conffile.ConfigsFile(projfolder) gpf = projfile.GedaProjectFile(configfile.projectfolder) namebase = configfile.pcbname project_doc_folder = get_project_doc_folder(projfolder) if not project_doc_folder: return [] project_img_folder = os.path.join(project_doc_folder, os.pardir, 'img') rval = [ ExposedDocument( namebase + ' PCB Top View', path.join(project_img_folder, gpf.pcbfile + '.top.png'), refdoc_fs ), ExposedDocument( namebase + ' PCB Bottom View', path.join(project_img_folder, gpf.pcbfile + '.bottom.png'), refdoc_fs ), ExposedDocument( namebase + ' PCB Layers', path.join(project_img_folder, gpf.pcbfile + '.devel.png'), refdoc_fs ) ] for img in rval: if not img.exists: rval.remove(img) return rval
def _load_prices(self): gpf = projfile.GedaProjectFile(self._projectfolder) pricingfp = os.path.join(gpf.configsfile.projectfolder, 'pcb', 'sourcing.yaml') if not os.path.exists(pricingfp): logger.debug( "PCB does not have sourcing file. Not loading prices : " + self._pcbname) return None with open(pricingfp, 'r') as f: data = yaml.load(f) for qty, prices in viewitems(data['pricing']): if 10 not in prices.keys(): logger.warning( "Default Delivery Time not in prices. Quantity pricing not imported : " + # noqa str([qty, self._pcbname])) else: price = VendorPrice(qty, prices[10], self._vendor.currency) self._prices.append(price)
def _load_descriptors(self): gpf = projfile.GedaProjectFile(self._projectfolder) pricingfp = os.path.join(gpf.configsfile.projectfolder, 'pcb', 'sourcing.yaml') if not os.path.exists(pricingfp): logger.debug( "PCB does not have sourcing file. Not loading prices : " + self._pcbname) return None with open(pricingfp, 'r') as f: data = yaml.load(f) self._descriptors.append( str(data['params']['dX']) + 'mm x ' + str(data['params']['dY']) + 'mm') # noqa if data["params"]["layers"] == 2: self._descriptors.append("Double Layer") elif data["params"]["layers"] == 4: self._descriptors.append("ML4") # HAL, Sn, Au, PBFREE, H, NP, I, OC if data["params"]["finish"] == 'Au': self._descriptors.append("Immersion Gold/ENIG finish") elif data["params"]["finish"] == 'Sn': self._descriptors.append("Immersion Tin finish") elif data["params"]["finish"] == 'PBFREE': self._descriptors.append("Any Lead Free finish") elif data["params"]["finish"] == 'H': self._descriptors.append("Lead F ree HAL finish") elif data["params"]["finish"] == 'NP': self._descriptors.append("No Copper finish") elif data["params"]["finish"] == 'I': self._descriptors.append("OSP finish") elif data["params"]["finish"] == 'OC': self._descriptors.append("Only Copper finish") else: self._descriptors.append("UNKNOWN FINISH: " + data["params"]["finish"]) # noqa self._descriptors.append("10 Working Days")
def get_docs_list(projfolder, cardname=None): """ Returns a list of :class:`docstore.ExposedDocument` instances, pointing to the documentation linked to the gEDA project or card specified by the parameters. If the ``cardname`` is not specified, the documents linked to the base PCB only are returned. If the ``cardname`` is specified, the documents defining the specific configuration only are returned. :param projfolder: The gEDA project folder. :param cardname: The cardname. :return: list of :class:`ExposedDocument` """ configfile = conffile.ConfigsFile(projfolder) namebase = configfile.pcbname is_cable = False if namebase is None: try: namebase = configfile.rawconfig['cblname'] is_cable = True except KeyError: logger.error("Project does not have a known identifier. " "Skipping : " + projfolder) return project_doc_folder = get_project_doc_folder(projfolder) if not project_doc_folder: return [] if not cardname: # Get all docs linked to the project rval = [ ExposedDocument( 'Project Master Doc', path.join(project_doc_folder, namebase + '-masterdoc.pdf'), refdoc_fs), ExposedDocument( namebase + ' Schematic (Full)', path.join(project_doc_folder, namebase + '-schematic.pdf'), refdoc_fs), ExposedDocument( 'Composite Bom (All Configs)', path.join(project_doc_folder, 'confdocs', 'conf-boms.csv'), refdoc_fs), ] if is_cable: return rval gpf = projfile.GedaProjectFile(configfile.projectfolder) rval.extend([ ExposedDocument( namebase + ' PCB Layers', path.join(project_doc_folder, namebase + '-pcb.pdf'), refdoc_fs), ExposedDocument( namebase + ' PCB Pricing', path.join(project_doc_folder, namebase + '-pricing.pdf'), refdoc_fs), ExposedDocument( namebase + ' PCB DXF', path.join(project_doc_folder, os.pardir, configfile.pcbname + '.dxf'), refdoc_fs), ExposedDocument( namebase + ' PCB Gerber', path.join(project_doc_folder, os.pardir, gpf.pcbfile + '-gerber.zip'), refdoc_fs), ]) return rval else: cardname = cardname.strip() rval = [ ExposedDocument( cardname + ' Doc', path.join(project_doc_folder, 'confdocs', cardname + '.pdf'), refdoc_fs), ExposedDocument( cardname + ' Reference BOM', path.join(project_doc_folder, 'confdocs', cardname + '-bom.pdf'), refdoc_fs), ExposedDocument( cardname + ' Schematic (Configured)', path.join(project_doc_folder, 'confdocs', namebase + '-conf-schematic.pdf'), refdoc_fs), ExposedDocument( cardname + ' Schematic (Full)', path.join(project_doc_folder, namebase + '-schematic.pdf'), refdoc_fs), ExposedDocument( 'Composite Bom (All Configs)', path.join(project_doc_folder, 'confdocs', 'conf-boms.csv'), refdoc_fs), ExposedDocument( 'Project Master Doc', path.join(project_doc_folder, namebase + '-masterdoc.pdf'), refdoc_fs), ] return rval
def gen_pcbpricing(projfolder, namebase, force=False): """ Generates a PDF file with the pricing of the (bare) PCB provided by the gEDA project. The pcb file is the one listed in the gEDA project file, and the pcbname is the one specified in the :mod:`tendril.gedaif.conffile.ConfigsFile`. The pricing information is read out from the PCB's ``sourcing.yaml`` file, which in turn is intended to be created by sourcing modules. .. todo:: This function presently uses :func:`tendril.dox.render.render_lineplot`, which is marked for deprecation. It should be rewritten to use the :func:`tendril.dox.render.make_graph` route instead. :param projfolder: The gEDA project folder. :type projfolder: str :param namebase: The project name. :type namebase: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output File : ``<project_doc_folder>/<namebase>-pricing.pdf`` * Source Files : ``<projectfolder>/pcb/sourcing.yaml`` """ gpf = projfile.GedaProjectFile(projfolder) pcbpricingfp = os.path.join(gpf.configsfile.projectfolder, 'pcb', 'sourcing.yaml') pcbpricing_mtime = fsutils.get_file_mtime(pcbpricingfp) if not os.path.exists(pcbpricingfp): return None docfolder = get_project_doc_folder(projfolder) plotfile = path.join(docfolder, namebase + '-pricing.pdf') outf_mtime = fsutils.get_file_mtime(plotfile, fs=refdoc_fs) if not force and outf_mtime is not None and outf_mtime > pcbpricing_mtime: logger.debug('Skipping up-to-date ' + pcbpricingfp) return pcbpricingfp logger.info('Regnerating ' + plotfile + os.linesep + 'Last modified : ' + str(pcbpricing_mtime) + '; Last Created : ' + str(outf_mtime)) with open(pcbpricingfp, 'r') as f: data = yaml.load(f) workspace_outpath = workspace_fs.getsyspath(plotfile) workspace_folder = workspace_fs.getsyspath(path.dirname(plotfile)) workspace_fs.makedir(path.dirname(plotfile), recursive=True, allow_recreate=True) plot1file = os.path.join(workspace_folder, namebase + '-1pricing.pdf') plot2file = os.path.join(workspace_folder, namebase + '-2pricing.pdf') pltnote = "This pricing refers to the bare PCB only. " \ "See the corresponding Config Docs for Card Pricing" plt1data = { key: data['pricing'][key] for key in data['pricing'].keys() if key <= 10 } plt1title = gpf.configsfile.configdata['pcbname'] plt1title += " PCB Unit Price vs Order Quantity (Low Quantity)" plot1file = render.render_lineplot(plot1file, plt1data, plt1title, pltnote) if max(data['pricing'].keys()) > 10: plt2data = { key: data['pricing'][key] for key in data['pricing'].keys() if key > 10 } plt2title = gpf.configsfile.configdata['pcbname'] plt2title += " PCB Unit Price vs Order Quantity (Production Quantity)" plot2file = render.render_lineplot(plot2file, plt2data, plt2title, pltnote) pdf.merge_pdf([plot1file, plot2file], workspace_outpath) os.remove(plot2file) else: shutil.copyfile(plot1file, workspace_outpath) os.remove(plot1file) copyfile(workspace_fs, plotfile, refdoc_fs, plotfile, overwrite=True) return plotfile
def gen_pcb_gbr(projfolder, force=False): """ Generates gerber files for the PCB provided by the gEDA project, and also creates a ``zip`` file of the generated gerbers. The pcbfile is the one listed in the gEDA project file, and the pcbname is the one specified in the :mod:`tendril.gedaif.conffile.ConfigsFile`. This function does not use jinja2 and latex. It relies on :func:`tendril.gedaif.pcb.conv_pcb2gbr` instead. :param projfolder: The gEDA project folder. :type projfolder: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output Files : ``<project_doc_folder>/../gerber/*`` * Output Zip File : ``<project_doc_folder>/../<pcbfile>-gerber.zip`` * Source Files : The project's `.pcb` file. """ configfile = conffile.ConfigsFile(projfolder) gpf = projfile.GedaProjectFile(configfile.projectfolder) pcb_mtime = fsutils.get_file_mtime( os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb')) if pcb_mtime is None: logger.warning("PCB does not seem to exist for : " + projfolder) return docfolder = get_project_doc_folder(projfolder) imgfolder = os.path.join(docfolder, os.pardir, 'img') gbrfolder = os.path.join(docfolder, os.pardir, 'gerber') outf_mtime = None if not refdoc_fs.exists(gbrfolder): refdoc_fs.makedir(gbrfolder) else: outf_mtime = fsutils.get_folder_mtime(gbrfolder, fs=refdoc_fs) if not refdoc_fs.exists(imgfolder): refdoc_fs.makedir(imgfolder) if not force and outf_mtime is not None and outf_mtime > pcb_mtime: logger.debug('Skipping up-to-date ' + gbrfolder) return gbrfolder logger.info('Regenerating ' + gbrfolder + os.linesep + 'Last modified : ' + str(pcb_mtime) + '; Last Created : ' + str(outf_mtime)) rf = refdoc_fs.listdir(gbrfolder, files_only=True, full=True) for f in rf: refdoc_fs.remove(f) workspace_folder = workspace_fs.getsyspath(gbrfolder) workspace_fs.makedir(gbrfolder, recursive=True, allow_recreate=True) gbrfolder = pcb.conv_pcb2gbr( os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb'), workspace_folder) workspace_fs.makedir(imgfolder, recursive=True, allow_recreate=True) img_workspace_folder = workspace_fs.getsyspath(imgfolder) gen_pcb_img(gbrfolder, outfolder=img_workspace_folder, outfname=gpf.pcbfile, force=False) for f in os.listdir(img_workspace_folder): fpath = os.path.relpath(os.path.join(img_workspace_folder, f), workspace_fs.getsyspath('/')) copyfile(workspace_fs, fpath, refdoc_fs, fpath, overwrite=True) zfile = os.path.join(workspace_folder, os.pardir, gpf.pcbfile + '-gerber.zip') fsutils.zipdir(gbrfolder, zfile) for f in os.listdir(workspace_folder): fpath = os.path.relpath(os.path.join(workspace_folder, f), workspace_fs.getsyspath('/')) copyfile(workspace_fs, fpath, refdoc_fs, fpath, overwrite=True) zfpath = os.path.relpath(os.path.join(workspace_folder, zfile), workspace_fs.getsyspath('/')) copyfile(workspace_fs, zfpath, refdoc_fs, zfpath, overwrite=True) return gbrfolder
def gen_confpdf(projfolder, configname, namebase, force=False): """ Generates a PDF file of the documentation for a specific configuration of a project. It uses other document generator functions to make the various parts of the master document and then merges them. :param projfolder: The gEDA project folder. :type projfolder: str :param configname: The name of the configuration. :type configname: str :param namebase: The project name. :type namebase: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output File : ``<project_doc_folder>/confdocs/<configname>.pdf`` * Source Files : The project's schematic folder. .. rubric:: Included Documents * Configuration BOM, generated by :func:`gen_confbom` * (Full) Schematic PDF, generated by :func:`gen_schpdf` .. todo:: It may be useful to rebuild the schematics after removing all the unpopulated components. This is a fairly involved process, and is deferred until later. """ gpf = projfile.GedaProjectFile(projfolder) sch_mtime = fsutils.get_folder_mtime(gpf.schfolder) docfolder = get_project_doc_folder(projfolder) confdocfile = path.join(docfolder, 'confdocs', configname + '.pdf') outf_mtime = fsutils.get_file_mtime(confdocfile, fs=refdoc_fs) if not force and outf_mtime is not None and outf_mtime > sch_mtime: logger.debug('Skipping up-to-date ' + confdocfile) return confdocfile logger.info('Regenerating ' + confdocfile + os.linesep + 'Last modified : ' + str(sch_mtime) + '; Last Created : ' + str(outf_mtime)) pdffiles = [ gen_confbom(projfolder, configname, force), gen_confdoc(projfolder, configname, force), gen_schpdf(projfolder, namebase, configname=configname, force=force) ] for p in pdffiles: if p and not workspace_fs.exists(p): workspace_fs.makedir(path.dirname(p), recursive=True, allow_recreate=True) copyfile(refdoc_fs, p, workspace_fs, p) workspace_pdffiles = [ workspace_fs.getsyspath(x) for x in pdffiles if x is not None ] workspace_outpath = workspace_fs.getsyspath(confdocfile) workspace_fs.makedir(path.dirname(confdocfile), recursive=True, allow_recreate=True) pdf.merge_pdf(workspace_pdffiles, workspace_outpath) copyfile(workspace_fs, confdocfile, refdoc_fs, confdocfile, overwrite=True) return confdocfile
def gen_masterdoc(projfolder, namebase, force=False): """ Generates a PDF file of the project's Master documentation. It uses other document generator functions to make the various parts of the master document and then merges them. .. note:: Due to the way groups and motifs are handled, an unconfigured BOM is somewhat meaningless. Therefore, no BOM is included in the masterdoc. :param projfolder: The gEDA project folder. :type projfolder: str :param namebase: The project name. :type namebase: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output File : ``<project_doc_folder>/<namebase>-masterdoc.pdf`` * Source Files : The project's schematic folder. .. rubric:: Included Documents * Config Documentation, generated by :func:`gen_configsdoc` * Schematic PDF, generated by :func:`gen_schpdf` """ gpf = projfile.GedaProjectFile(projfolder) sch_mtime = fsutils.get_folder_mtime(gpf.schfolder) docfolder = get_project_doc_folder(projfolder) masterdocfile = path.join(docfolder, namebase + '-masterdoc.pdf') outf_mtime = fsutils.get_file_mtime(masterdocfile, fs=refdoc_fs) if not force and outf_mtime is not None and outf_mtime > sch_mtime: logger.debug('Skipping up-to-date ' + masterdocfile) return masterdocfile logger.info('Regnerating ' + masterdocfile + os.linesep + 'Last modified : ' + str(sch_mtime) + '; Last Created : ' + str(outf_mtime)) pdffiles = [ gen_configsdoc(projfolder, namebase, force=False), gen_schpdf(projfolder, namebase, force=False) ] for p in pdffiles: if p and not workspace_fs.exists(p): workspace_fs.makedir(path.dirname(p), recursive=True, allow_recreate=True) copyfile(refdoc_fs, p, workspace_fs, p) workspace_pdffiles = [ workspace_fs.getsyspath(x) for x in pdffiles if x is not None ] workspace_outpath = workspace_fs.getsyspath(masterdocfile) workspace_fs.makedir(path.dirname(masterdocfile), recursive=True, allow_recreate=True) pdf.merge_pdf(workspace_pdffiles, workspace_outpath) copyfile(workspace_fs, masterdocfile, refdoc_fs, masterdocfile, overwrite=True) return masterdocfile
def gen_schpdf(projfolder, namebase, configname=None, force=False): """ Generates a PDF file of all the project schematics listed in the gEDA project file. This function does not use jinja2 and latex. It relies on :func:`tendril.gedaif.gschem.conv_gsch2pdf` instead. :param projfolder: The gEDA project folder. :type projfolder: str :param namebase: The project name. :type namebase: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output File : ``<project_doc_folder>/<namebase>-schematic.pdf`` * Source Files : The project's schematic folder. """ gpf = projfile.GedaProjectFile(projfolder) sch_mtime = fsutils.get_folder_mtime(gpf.schfolder) configfile = conffile.ConfigsFile(projfolder) docfolder = get_project_doc_folder(projfolder) # TODO Consider converting all configurations in one go instead? if configname is None: schpdfpath = path.join(docfolder, namebase + '-schematic.pdf') else: docfolder = path.join(docfolder, 'confdocs') pdfname = configname + '-conf-schematic.pdf' schpdfpath = path.join(docfolder, pdfname) outf_mtime = fsutils.get_file_mtime(schpdfpath, fs=refdoc_fs) if not force and outf_mtime is not None and outf_mtime > sch_mtime: logger.debug('Skipping up-to-date ' + schpdfpath) return schpdfpath logger.info('Regenerating ' + schpdfpath + os.linesep + 'Last modified : ' + str(sch_mtime) + '; Last Created : ' + str(outf_mtime)) if configfile.rawconfig is not None: workspace_outpath = workspace_fs.getsyspath(schpdfpath) workspace_folder = workspace_fs.getsyspath(docfolder) workspace_fs.makedir(docfolder, recursive=True, allow_recreate=True) pdffiles = [] obom = None if configname is not None: tfolder = path.join(docfolder, configname) workspace_tfolder = workspace_fs.getsyspath(tfolder) workspace_fs.makedir(tfolder, recursive=False, allow_recreate=True) bom = boms_electronics.import_pcb(projfolder) bom.configure_motifs(configname) obom = bom.create_output_bom(configname) for schematic in gpf.schfiles: schfile = os.path.normpath(projfolder + '/schematic/' + schematic) if configname is not None: tschfile = path.join(workspace_tfolder, schematic) gschem.rewrite_schematic(schfile, obom, gpf, tschfile) pdffile = gschem.conv_gsch2pdf(tschfile, workspace_tfolder) os.remove(tschfile) else: pdffile = gschem.conv_gsch2pdf(schfile, workspace_folder) pdffiles.append(pdffile) pdf.merge_pdf(pdffiles, workspace_outpath) for pdffile in pdffiles: os.remove(pdffile) copyfile(workspace_fs, schpdfpath, refdoc_fs, schpdfpath, overwrite=True) return schpdfpath
def gen_confdoc(projfolder, configname, force=False): """ Generate a PDF documenting a single configuration of the project. The document should include a reasonably thorough representation of the contents of the configuration related sections of the `tendril.gedaif.conffile.ConfigsFile``. :param projfolder: The gEDA project folder :type projfolder: str :param configname: The configuration name for which the BOM should be generated. :type configname: str :param force: Regenerate even if up-to-date. :type force: bool :return: The output file path. .. rubric:: Paths * Output File : ``<project_doc_folder>/confdocs/<configname>-doc.pdf`` * Source Files : The project's schematic folder. .. rubric:: Template Used ``tendril/dox/templates/projects/geda-conf-doc.tex`` (:download:`Included version <../../tendril/dox/templates/projects/geda-conf-doc.tex>`) .. rubric:: Stage Keys Provided .. list-table:: * - ``configname`` - The name of the configuration (a card or cable name). * - ``desc`` - The description of the configuration. * - ``pcbname`` - The name of the base PCB. * - ``obom`` - An :mod:`tendril.boms.outputbase.OutputBom` instance """ gpf = projfile.GedaProjectFile(projfolder) sch_mtime = fsutils.get_folder_mtime(gpf.schfolder) docfolder = get_project_doc_folder(projfolder) outpath = path.join(docfolder, 'confdocs', configname + '-doc.pdf') outf_mtime = fsutils.get_file_mtime(outpath, fs=refdoc_fs) if not force and outf_mtime is not None and outf_mtime > sch_mtime: logger.debug('Skipping up-to-date ' + outpath) return outpath logger.info('Regenerating ' + outpath + os.linesep + 'Last modified : ' + str(sch_mtime) + '; Last Created : ' + str(outf_mtime)) bom = boms_electronics.import_pcb(projfolder) obom = bom.create_output_bom(configname) group_oboms = bom.get_group_boms(configname) stage = { 'configname': obom.descriptor.configname, 'pcbname': obom.descriptor.pcbname, 'bom': bom, 'obom': obom, 'group_oboms': group_oboms } config = obom.descriptor.configurations.configuration(configname) stage['desc'] = config['desc'] template = 'projects/geda-conf-doc.tex' workspace_outpath = workspace_fs.getsyspath(outpath) workspace_fs.makedir(path.dirname(outpath), recursive=True, allow_recreate=True) render.render_pdf(stage, template, workspace_outpath) copyfile(workspace_fs, outpath, refdoc_fs, outpath, overwrite=True) return outpath
def flush_pcb_pricing(projfolder): gpf = projfile.GedaProjectFile(projfolder) pricingfp = os.path.join(gpf.configsfile.projectfolder, 'pcb', 'sourcing.yaml') if os.path.exists(pricingfp): os.remove(pricingfp)
row[headers.index('Used Break Qty')], row[headers.index('Effective Unit Price')]) except IndexError: pricing[row[0]] = None summaryf = open(os.path.join(orderfolder, 'costing-summary.csv'), 'w') summaryw = csv.writer(summaryf) summaryw.writerow([ "Card", "Total BOM Lines", "Uncosted BOM Lines", "Quantity", "Indicative Unit Cost", "Indicative Line Cost" ]) all_uncosted_idents = [] for k, v in sorted(data['cards'].iteritems()): logger.info('Creating Indicative Pricing for Card : ' + k) bom = tendril.boms.electronics.import_pcb(projects.cards[k]) obom = bom.create_output_bom(k) gpf = projfile.GedaProjectFile(obom.descriptor.cardfolder) ipfolder = gpf.configsfile.indicative_pricing_folder if not os.path.exists(ipfolder): os.makedirs(ipfolder) for configuration in gpf.configsfile.configdata['configurations']: if configuration['configname'] == k: desc = configuration['desc'] if desc is None: desc = '' ipfile = os.path.join( ipfolder, obom.descriptor.configname + '~' + indication_context + '.csv') totalcost = 0 uncosted_idents = [] total_lines = 0 with open(ipfile, 'w') as f: