Exemplo n.º 1
0
def gen_submitdocs(invoice, target_folder, serialno):
    """
    Generates collated PDF files containing the submittable
    documents. Two PDF files are generated - one for printing on
    the company letterhead and one on regular paper.

    :param invoice: The invoice object with customs information
    :type invoice: :class:`tendril.sourcing.customs.CustomsInvoice`
    :param target_folder: The folder in which the generated files
                          should be written to
    :param serialno: The serial number of the Customs documentation set
    :type serialno: str
    :return: List of output file tuples (path, type)


    .. rubric:: Included Documents - For Letterhead Printing

    * CHA Authorization, generated by :func:`gen_authorization`
    * RSP Declaration, generated by :func:`gen_rsp_declaration`
    * Note on Valuation, generated by :func:`gen_valuation`
    * Technical Writeup, generated by :func:`gen_tech_writeup`

    .. rubric:: Included Documents - For Plain Paper Printing

    * Declaration (ORIGINAL), generated by :func:`gen_declaration`
    * Declaration (DUPLICATE), generated by :func:`gen_declaration`
    * Two copies of the Gatt Declaration, from :mod:`tendril.dox.wallet`,
      key ``CUSTOMS-DECL``.
    * Scanned copy of the IEC, from :mod:`tendril.dox.wallet`,
      key ``IEC``.

    """
    lh_files = [gen_authorization(invoice, target_folder, serialno),
                gen_rsp_declaration(invoice, target_folder, serialno),
                gen_valuation(invoice, target_folder, serialno),
                gen_tech_writeup(invoice, target_folder, serialno)
                ]
    lh_fpath = os.path.join(
        target_folder, "customs-printable-lh-" + str(invoice.inv_no) + ".pdf"
    )
    lh_fpath = pdf.merge_pdf(lh_files, lh_fpath, remove_sources=True)

    pp_files = [gen_declaration(invoice, target_folder,
                                'ORIGINAL', serialno),
                gen_declaration(invoice, target_folder,
                                'DUPLICATE', serialno),
                wallet.get_document_path('CUSTOMS-DECL'),
                wallet.get_document_path('CUSTOMS-DECL'),
                wallet.get_document_path('IEC'),
                ]
    pp_fpath = os.path.join(
        target_folder, "customs-printable-pp-" + str(invoice.inv_no) + ".pdf"
    )
    pp_fpath = pdf.merge_pdf(pp_files, pp_fpath, remove_sources=True)

    files = [(lh_fpath, 'CUST-PRINTABLE-LH'), (pp_fpath, 'CUST-PRINTABLE-PP')]
    return files
Exemplo n.º 2
0
def gen_submitdocs(invoice, target_folder, serialno):
    """
    Generates collated PDF files containing the submittable
    documents. Two PDF files are generated - one for printing on
    the company letterhead and one on regular paper.

    :param invoice: The invoice object with customs information
    :type invoice: :class:`tendril.sourcing.customs.CustomsInvoice`
    :param target_folder: The folder in which the generated files
                          should be written to
    :param serialno: The serial number of the Customs documentation set
    :type serialno: str
    :return: List of output file tuples (path, type)


    .. rubric:: Included Documents - For Letterhead Printing

    * CHA Authorization, generated by :func:`gen_authorization`
    * RSP Declaration, generated by :func:`gen_rsp_declaration`
    * Note on Valuation, generated by :func:`gen_valuation`
    * Technical Writeup, generated by :func:`gen_tech_writeup`

    .. rubric:: Included Documents - For Plain Paper Printing

    * Declaration (ORIGINAL), generated by :func:`gen_declaration`
    * Declaration (DUPLICATE), generated by :func:`gen_declaration`
    * Two copies of the Gatt Declaration, from :mod:`tendril.dox.wallet`,
      key ``CUSTOMS-DECL``.
    * Scanned copy of the IEC, from :mod:`tendril.dox.wallet`,
      key ``IEC``.

    """
    lh_files = [gen_authorization(invoice, target_folder, serialno),
                gen_rsp_declaration(invoice, target_folder, serialno),
                gen_valuation(invoice, target_folder, serialno),
                gen_tech_writeup(invoice, target_folder, serialno)
                ]
    lh_fpath = os.path.join(
        target_folder, "customs-printable-lh-" + str(invoice.inv_no) + ".pdf"
    )
    lh_fpath = pdf.merge_pdf(lh_files, lh_fpath, remove_sources=True)

    pp_files = [gen_declaration(invoice, target_folder,
                                'ORIGINAL', serialno),
                gen_declaration(invoice, target_folder,
                                'DUPLICATE', serialno),
                wallet.get_document_path('CUSTOMS-DECL'),
                wallet.get_document_path('CUSTOMS-DECL'),
                wallet.get_document_path('IEC'),
                ]
    pp_fpath = os.path.join(
        target_folder, "customs-printable-pp-" + str(invoice.inv_no) + ".pdf"
    )
    pp_fpath = pdf.merge_pdf(pp_files, pp_fpath, remove_sources=True)

    files = [(lh_fpath, 'CUST-PRINTABLE-LH'), (pp_fpath, 'CUST-PRINTABLE-PP')]
    return files
Exemplo n.º 3
0
def gen_schpdf(projfolder, namebase, force=False):
    """
    Generates a PDF file of all the project schematics listed in the
    gEDA project file. This function does not ise 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)

    schpdfpath = path.join(docfolder, namebase + '-schematic.pdf')
    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(path.dirname(schpdfpath))
        workspace_fs.makedir(path.dirname(schpdfpath),
                             recursive=True, allow_recreate=True)
        pdffiles = []
        for schematic in gpf.schfiles:
            schfile = os.path.normpath(projfolder + '/schematic/' + schematic)
            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
Exemplo n.º 4
0
def get_production_order_manifest_set(serialno):
    workspace = temp_fs.makeopendir(get_tempname())
    children = serialnos.get_child_serialnos(sno=serialno)
    manifests = []
    for child in children:
        files = []

        am = docstore.get_docs_list_for_sno_doctype(serialno=child,
                                                    doctype='ASSEMBLY MANIFEST')
        if len(am) == 1:
            uam = am[0]
            copyfile(uam.fs, uam.path, workspace, uam.filename, overwrite=True)
            files = [workspace.getsyspath(uam.filename)]
        elif len(am) > 1:
            raise ValueError(
                    "Found {0} manifests for {2}".format(len(am), child)
            )

        dms = docstore.get_docs_list_for_sno_doctype(
                serialno=child, doctype='DELTA ASSEMBLY MANIFEST'
        )
        if len(dms):
            for dm in dms:
                copyfile(dm.fs, dm.path, workspace, dm.filename,
                         overwrite=True)
                files.append(workspace.getsyspath(dm.filename))

        if len(files) > 1:
            wdmfile = merge_pdf(
                files,
                os.path.join(workspace.getsyspath('/'),
                             os.path.splitext(am[0].filename)[0] + '-wdm.pdf'),
                remove_sources=True
            )
            manifests.append(wdmfile)
        elif len(files) == 1:
            manifests.append(files[0])

    if len(manifests):
        output = merge_pdf(
            manifests,
            os.path.join(workspace.getsyspath('/'), serialno + '.pdf'),
            remove_sources=True
        )
        return output
    return None
Exemplo n.º 5
0
def get_production_order_manifest_set(serialno):
    workspace = temp_fs.makeopendir(get_tempname())
    children = serialnos.get_child_serialnos(sno=serialno)
    manifests = []
    for child in children:
        files = []

        am = docstore.get_docs_list_for_sno_doctype(
            serialno=child, doctype='ASSEMBLY MANIFEST')
        if len(am) == 1:
            uam = am[0]
            copyfile(uam.fs, uam.path, workspace, uam.filename, overwrite=True)
            files = [workspace.getsyspath(uam.filename)]
        elif len(am) > 1:
            raise ValueError("Found {0} manifests for {2}".format(
                len(am), child))

        dms = docstore.get_docs_list_for_sno_doctype(
            serialno=child, doctype='DELTA ASSEMBLY MANIFEST')
        if len(dms):
            for dm in dms:
                copyfile(dm.fs,
                         dm.path,
                         workspace,
                         dm.filename,
                         overwrite=True)
                files.append(workspace.getsyspath(dm.filename))

        if len(files) > 1:
            wdmfile = merge_pdf(
                files,
                os.path.join(workspace.getsyspath('/'),
                             os.path.splitext(am[0].filename)[0] + '-wdm.pdf'),
                remove_sources=True)
            manifests.append(wdmfile)
        elif len(files) == 1:
            manifests.append(files[0])

    if len(manifests):
        output = merge_pdf(manifests,
                           os.path.join(workspace.getsyspath('/'),
                                        serialno + '.pdf'),
                           remove_sources=True)
        return output
    return None
Exemplo n.º 6
0
 def generate_pdf(self, targetfolder, force=False):
     files = self.generate_pdfs(targetfolder, force=force)
     if len(files) == 0:
         return None
     elif len(files) == 1:
         return files[0]
     else:
         return merge_pdf(files,
                          os.path.join(targetfolder, 'labels.pdf'),
                          remove_sources=True)
Exemplo n.º 7
0
 def generate_pdf(self, targetfolder, force=False):
     files = self.generate_pdfs(targetfolder, force=force)
     if len(files) == 0:
         return None
     elif len(files) == 1:
         return files[0]
     else:
         return merge_pdf(files,
                          os.path.join(targetfolder, 'labels.pdf'),
                          remove_sources=True)
Exemplo n.º 8
0
def gen_pcb_am(configname,
               outfolder,
               sno=None,
               productionorderno=None,
               indentsno=None,
               scaffold=False,
               verbose=True,
               session=None):
    """
    Generates a PCB Assembly Manifest for a 'card', a card being defined as a
    gEDA project, with a specified ``configname``.

    In the present implementation, the project could provide either a PCB or a
    Cable.

        - In case the project provides the card, the schematic for the cable
          is included along with the assembly manifest.
        - In case the project provides a PCB, the schematic is not included
          with the assembly manifest.

    This behavior is not really intuitive nor universally desirable. This
    rationale should be changed to something that makes more sense.

    .. note::
        This function does not register the document in the
        :mod:`tendril.dox.docstore`. You should use the output file path
        (returned by this function) to register the document when desired.

    .. seealso::
        - :mod:`tendril.gedaif.conffile`, for information about confignames.
        - :mod:`tendril.entityhub.projects`, for information about 'cards'

    .. todo:: Update this function to also handle registering once the main
              scripts are better integrated into the core.

    :param configname: The name of the project configuration to use.
    :type configname: str
    :param outfolder: The folder within which the output file should be
                      created.
    :type outfolder: str
    :param sno: The serial number of the card for which you want the Assembly
                Manifest.
    :type sno: str
    :param productionorderno: The serial number of the Production Order for
                              the card.
    :type productionorderno: str
    :param indentsno: The serial number of the Stock Indent which accounts for
                      the components used in this card.
    :type indentsno: str
    :return: The path of the generated file.

    .. rubric:: Template Used

    ``tendril/dox/templates/production/pcb-assem-manifest.tex``
    (:download:`Included version
    <../../tendril/dox/templates/production/pcb-assem-manifest.tex>`)

    .. rubric:: Stage Keys Provided
    .. list-table::

        * - ``sno``
          - The serial number of the card.
        * - ``configname``
          - The configuration name of the card.
        * - ``pcbname``
          - The name of the PCB provided by the gEDA project.
        * - ``title``
          - Whether the device is a PCB or a Cable.
        * - ``desc``
          - The description of the card.
        * - ``lines``
          - List of :class:`tendril.boms.outputbase.OutputBomLine` instances.
        * - ``stockindent``
          - The serial number of the Stock Indent which accounts for
            the components used in this card.
        * - ``productionorderno``
          - The serial number of the Production Order for the card.
        * - ``repopath``
          - The root of the VCS repository which contains the gEDA project.
        * - ``evenpages``
          - Whether to render PDF with even number of pages by adding an extra
            page if needed (useful for bulk printing).

    """
    if sno is None:
        # TODO Generate real S.No. here
        sno = 1

    outpath = os.path.join(outfolder,
                           'am-' + configname + '-' + str(sno) + '.pdf')

    instance = get_module_instance(sno,
                                   configname,
                                   scaffold=scaffold,
                                   session=session)
    obom = instance.obom

    if projects.check_module_is_card(configname):
        entityname = instance.pcbname
        title = 'PCB '
        evenpages = True
        add_schematic = False
    elif projects.check_module_is_cable(configname):
        entityname = instance.cblname
        title = 'Cable '
        evenpages = False
        add_schematic = True
    else:
        raise ValueError

    stage = {
        'configname': obom.descriptor.configname,
        'pcbname': entityname,
        'title': title,
        'sno': sno,
        'lines': obom.lines,
        'evenpages': evenpages,
        'stockindent': indentsno,
        'repopath': projects.get_project_repo_repr(configname),
        'productionorderno': productionorderno
    }

    for config in obom.descriptor.configurations.configurations:
        if config['configname'] == configname:
            stage['desc'] = config['desc']

    template = 'production/pcb-assem-manifest.tex'

    render.render_pdf(stage, template, outpath, verbose=verbose)

    if add_schematic is True:
        merge_pdf([
            outpath,
            os.path.join(instance.projfolder, 'doc',
                         entityname + '-schematic.pdf')
        ], outpath)
    return outpath
Exemplo n.º 9
0
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
Exemplo n.º 10
0
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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
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
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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, 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
Exemplo n.º 15
0
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
Exemplo n.º 16
0
def gen_pcb_am(configname, outfolder, sno=None, productionorderno=None,
               indentsno=None, scaffold=False, verbose=True, session=None):
    """
    Generates a PCB Assembly Manifest for a 'card', a card being defined as a
    gEDA project, with a specified ``configname``.

    In the present implementation, the project could provide either a PCB or a
    Cable.

        - In case the project provides the card, the schematic for the cable
          is included along with the assembly manifest.
        - In case the project provides a PCB, the schematic is not included
          with the assembly manifest.

    This behavior is not really intuitive nor universally desirable. This
    rationale should be changed to something that makes more sense.

    .. note::
        This function does not register the document in the
        :mod:`tendril.dox.docstore`. You should use the output file path
        (returned by this function) to register the document when desired.

    .. seealso::
        - :mod:`tendril.gedaif.conffile`, for information about confignames.
        - :mod:`tendril.entityhub.projects`, for information about 'cards'

    .. todo:: Update this function to also handle registering once the main
              scripts are better integrated into the core.

    :param configname: The name of the project configuration to use.
    :type configname: str
    :param outfolder: The folder within which the output file should be
                      created.
    :type outfolder: str
    :param sno: The serial number of the card for which you want the Assembly
                Manifest.
    :type sno: str
    :param productionorderno: The serial number of the Production Order for
                              the card.
    :type productionorderno: str
    :param indentsno: The serial number of the Stock Indent which accounts for
                      the components used in this card.
    :type indentsno: str
    :return: The path of the generated file.

    .. rubric:: Template Used

    ``tendril/dox/templates/production/pcb-assem-manifest.tex``
    (:download:`Included version
    <../../tendril/dox/templates/production/pcb-assem-manifest.tex>`)

    .. rubric:: Stage Keys Provided
    .. list-table::

        * - ``sno``
          - The serial number of the card.
        * - ``configname``
          - The configuration name of the card.
        * - ``pcbname``
          - The name of the PCB provided by the gEDA project.
        * - ``title``
          - Whether the device is a PCB or a Cable.
        * - ``desc``
          - The description of the card.
        * - ``lines``
          - List of :class:`tendril.boms.outputbase.OutputBomLine` instances.
        * - ``stockindent``
          - The serial number of the Stock Indent which accounts for
            the components used in this card.
        * - ``productionorderno``
          - The serial number of the Production Order for the card.
        * - ``repopath``
          - The root of the VCS repository which contains the gEDA project.
        * - ``evenpages``
          - Whether to render PDF with even number of pages by adding an extra
            page if needed (useful for bulk printing).

    """
    if sno is None:
        # TODO Generate real S.No. here
        sno = 1

    outpath = os.path.join(outfolder,
                           'am-' + configname + '-' + str(sno) + '.pdf')

    instance = get_module_instance(sno, configname,
                                   scaffold=scaffold, session=session)
    obom = instance.obom

    if projects.check_module_is_card(configname):
        entityname = instance.pcbname
        title = 'PCB '
        evenpages = True
        add_schematic = False
    elif projects.check_module_is_cable(configname):
        entityname = instance.cblname
        title = 'Cable '
        evenpages = False
        add_schematic = True
    else:
        raise ValueError

    stage = {'configname': obom.descriptor.configname,
             'pcbname': entityname,
             'title': title,
             'sno': sno,
             'lines': obom.lines,
             'evenpages': evenpages,
             'stockindent': indentsno,
             'repopath': projects.get_project_repo_repr(configname),
             'productionorderno': productionorderno}

    for config in obom.descriptor.configurations.configurations:
        if config['configname'] == configname:
            stage['desc'] = config['desc']

    template = 'production/pcb-assem-manifest.tex'

    render.render_pdf(stage, template, outpath, verbose=verbose)

    if add_schematic is True:
        merge_pdf([outpath,
                   os.path.join(instance.projfolder, 'doc',
                                entityname + '-schematic.pdf')],
                  outpath)
    return outpath