Ejemplo n.º 1
0
    def __call__ (self, gloss):

        self._indent = "  "
        self._gloss = gloss

        # Resolve pivotal language and environment.
        self._lang = self._options.lang or gloss.lang
        if self._lang not in gloss.languages:
            error(p_("error message",
                     "language '%(lang)s' not present in the glossary")
                    % dict(lang=self._lang))
        self._env = self._options.env or gloss.env[0]
        if self._env and self._env not in gloss.environments:
            error(p_("error message",
                     "environment '%(env)s' not defined by the glossary")
                  % dict(env=self._env))

        # Determine concepts to present and in which order.
        concepts = self._select_concepts()

        # Prepare text formatters.
        self._tf = TextFormatterPlain(gloss, lang=self._lang, env=self._env,
                                      escape=escape_xml)

        # Create TBX.
        accl = LineAccumulator(self._indent)
        self._fmt_prologue(accl)
        self._fmt_concepts(accl.newind(2), concepts)
        self._fmt_epilogue(accl)

        if self._options.file:
            tbx_fname = self._options.file
        else:
            tbx_fname = gloss.id + ".tbx"
        accl.write(tbx_fname)
Ejemplo n.º 2
0
    def __call__ (self, gloss):

        self._indent = "  "

        # Resolve languages and environment.
        olang = self._options.olang
        if olang not in gloss.languages:
            error(p_("error message",
                     "origin language '%(lang)s' not present in the glossary")
                    % dict(lang=olang))
        tlang = self._options.tlang
        if tlang not in gloss.languages:
            error(p_("error message",
                     "target language '%(lang)s' not present in the glossary")
                    % dict(lang=tlang))
        env = self._options.env or gloss.env[0]
        if env is not None and env not in gloss.environments:
            error(p_("error message",
                     "environment '%(env)s' not defined by the glossary")
                  % dict(env=env))

        # Select all concepts which have a term in both langenvs.
        concepts = {}
        for ckey, concept in gloss.concepts.iteritems():
            if concept.term(olang, env) and concept.term(tlang, env):
                concepts[ckey] = concept
        if not concepts:
            warning(p_("warning message",
                       "no concepts found which have terms in both "
                       "the origin and the target language and environment"))

        # Prepare text formatters.
        refbase = dict([(ckey, "") for ckey in concepts])
        tfn = TextFormatterPlain(gloss, lang=tlang, env=env)
        tf = TextFormatterHtml(gloss, lang=tlang, env=env, refbase=refbase)
        tfp = TextFormatterHtml(gloss, lang=tlang, env=env, refbase=refbase,
                                wtag="p")

        # Dictionary is presented as follows:
        # - all unique terms in the origin language presented
        # - for each unique origin term, all corresponding unique terms
        #   in the target language presented
        # - for each unique (origin, target) term pair, the descriptions of
        #   all concepts named by it are presented in the target language

        # Collect dict(oterm: dict(tterm: set(ckey)))
        # Collect dict(tterm: dict(gr: set(decl)))
        tdecls = {}
        bidict = {}
        for ckey, concept in concepts.iteritems():
            oterms = concept.term(olang, env)
            tterms = concept.term(tlang, env)
            for oterm in oterms:
                otnom = tfn(oterm.nom.text)
                if otnom not in bidict:
                    bidict[otnom] = {}
                for tterm in tterms:
                    # Target terms.
                    ttnom = tfn(tterm.nom.text)
                    if ttnom not in bidict[otnom]:
                        bidict[otnom][ttnom] = set()
                    bidict[otnom][ttnom].add(ckey)

                    # Declensions.
                    if ttnom not in tdecls:
                        tdecls[ttnom] = {}
                    for decl in tterm.decl:
                        gr = gloss.grammar[decl.gr]
                        grnam = tfn(gr.shortname(tlang, env)[0].text)
                        if grnam not in tdecls[ttnom]:
                            tdecls[ttnom][grnam] = set()
                        ttdecl = tfn(decl.text)
                        tdecls[ttnom][grnam].add(ttdecl)

        # Alphabetically sort origin terms.
        oterms_sorted = bidict.keys()
        langsort(oterms_sorted, olang)

        # Compose the dictionary table.
        accl = LineAccumulator(self._indent, 2)

        accl(stag("table", {"class":"bd-table"}))
        accl()

        # Header.
        accl(stag("tr", {"class":"bd-header"}), 1)
        olname = tfn(gloss.languages[olang].name(tlang, env)[0].text)
        accl(wtext(olname, "th", {"class":"bd-header-ol"}), 2)
        tlname = tfn(gloss.languages[tlang].name(tlang, env)[0].text)
        accl(wtext(tlname, "th", {"class":"bd-header-tl"}), 2)
        accl(etag("tr"), 1)

        # Entries by origin term.
        anchored = {}
        n_entry = 0
        n_entry_by_alpha = 0
        curr_alpha = None
        for oterm in oterms_sorted:
            n_entry += 1
            n_entry_by_alpha += 1

            # Add new alphabetical separator if needed.
            prev_alpha = curr_alpha
            curr_alpha = _term_alpha(oterm)
            if prev_alpha != curr_alpha:
                n_entry_by_alpha = 1
                accl(stag("tr", {"class":"bd-alsep"}), 1)
                accl(wtext(curr_alpha, "td", {"class":"bd-alsep-al",
                                              "colspan":"2"}), 2)
                accl(etag("tr"), 1)

            # Collapse all target terms which have same concepts.
            # Sort them alphabetically within the group,
            # then groups alphabetically by first term in the group.
            tterms_by_ckeygr = {}
            for tterm in bidict[oterm]:
                ckeys = list(bidict[oterm][tterm])
                ckeys.sort()
                ckeygr = tuple(ckeys)
                if ckeygr not in tterms_by_ckeygr:
                    tterms_by_ckeygr[ckeygr] = []
                tterms_by_ckeygr[ckeygr].append(tterm)
            tterms_groups = []
            for ckeys, tterms in tterms_by_ckeygr.iteritems():
                langsort(tterms, tlang)
                tterms_groups.append((tterms[0], tterms, ckeys))
            langsort_tuples(tterms_groups, 0, tlang)
            tterms_ckeys = [x[1:] for x in tterms_groups]

            if n_entry_by_alpha % 2 == 1:
                accl(stag("tr", {"class":"bd-entry-odd"}), 1)
            else:
                #accl(stag("tr", {"class":"bd-entry-even"}), 1)
                #... provide as option; randomly increases VCS deltas.
                accl(stag("tr", {"class":"bd-entry-odd"}), 1)

            # Column with origin term and anchors.
            accl(stag("td", {"class":"bd-oterm"}), 2)

            # Dummy anchors, for cross-references in descriptions to work.
            # Add anchors for all concepts covered by this entry,
            # and remember them, to avoid duplicate anchors on synonyms.
            new_ckeys = []
            for tterms, ckeys in tterms_ckeys:
                for ckey in ckeys:
                    if ckey not in anchored:
                        anchored[ckey] = True
                        new_ckeys.append(ckey)
            accl("".join([stag("span", {"id":x}, close=True)
                          for x in new_ckeys]), 3)

            # Origin term.
            accl(wtext(oterm, "p", {"class":"bd-otline"}), 3)
            accl(etag("td"), 2)

            # Column with target terms.
            accl(stag("td", {"class":"bd-tterms"}), 2)

            n_ttgr = 0
            for tterms, ckeys in tterms_ckeys:
                n_ttgr += 1
                accl(stag("div", {"class":"bd-ttgroup"}), 3)

                # Equip each term with extra info.
                tterms_compgr = []
                for tterm in tterms:
                    # Declensions.
                    lsep_dc = p_("list separator: "
                                 "acceptable variants of the same declension",
                                 ", ")
                    fmt_dcgr = p_("declension group: single declension given "
                                  "by its name and acceptable variants",
                                  "<i>%(dname)s</i> %(dvars)s")
                    lsep_gr = p_("list separator: "
                                 "declension groups",
                                 "; ")
                    tdecl = None
                    if tterm in tdecls:
                        lst = []
                        for gr, decls in tdecls[tterm].iteritems():
                            lst2 = list(decls)
                            langsort(lst2, tlang)
                            lst.append((gr, lsep_dc.join(lst2)))
                        langsort_tuples(lst, 0, tlang)
                        tdecl = lsep_gr.join([fmt_dcgr % dict(dname=x[0],
                                                              dvars=x[1])
                                              for x in lst])
                    # Compose.
                    if tdecl:
                        ttcgr = p_("term with declensions",
                                   "%(term)s (%(decls)s)") \
                                % dict(term=tterm, decls=tdecl)
                    else:
                        ttcgr = tterm
                    tterms_compgr.append(ttcgr)

                # Collect details for each term.
                has_details = False
                # - descriptions
                descstrs = []
                for ckey in ckeys:
                    for desc in concepts[ckey].desc(tlang, env):
                        if tfn(desc.text):
                            descstrs.append(tfp(desc.text, pclass="bd-desc"))
                            has_details = True
                if len(descstrs) > 1:
                    for i in range(len(descstrs)):
                        dhead = "%d. " % (i + 1)
                        descstrs[i] = descstrs[i].replace(">", ">" + dhead, 1)

                # Entry display control (if any details present).
                details_id = "opt_%s_%d" % (oterm.replace(" ", "_"), n_ttgr)
                if has_details:
                    accl(stag("div", {"class":"bd-edctl"}), 4)
                    accl(wtext("[+]", "a",
                               {"class":"bd-edctl",
                                "title":p_("tooltip", "Show details"),
                                "href":"#",
                                "onclick":"return show_hide(this, '%s')"
                                          % details_id}), 5)
                    accl(etag("div"), 4)

                # Line with terms.
                lsep_tt = p_("list separator: synonymous terms",
                             ", ")
                ttstr = lsep_tt.join(tterms_compgr)
                if len(tterms_ckeys) > 1:
                    ttstr = p_("enumerated target term in the dictionary, "
                               "one of the meanings of the original term",
                               "%(num)d. %(term)s") \
                            % dict(num=n_ttgr, term=ttstr)
                accl(wtext(ttstr, "p", {"class":"bd-ttline"}), 4)

                # Optional details.
                if has_details:
                    accl(stag("div", {"id":details_id,
                                      "style":"display: none;"}), 4)

                    for descstr in descstrs:
                        accl(descstr, 5)

                    accl(etag("div"), 4)

                accl(etag("div"), 3)

            accl(etag("td"), 2)
            accl(etag("tr"), 1)
            accl()

        accl(etag("table"))
        accl()

        # Prepare style file path.
        stylepath = None
        if self._options.style:
            if self._options.cssfile:
                stylepath = self._options.cssfile
            else:
                stylepath = _replace_ext(os.path.basename(self._options.file),
                                         "css")
            stylepath_nr = os.path.join(os.path.dirname(self._options.file),
                                        stylepath)
            stylesrc = os.path.join(  _src_style_dir, self._options.style
                                    + ".css.in")

        # Prepare JavaScript file path.
        dctlpath = None
        if self._options.jsfile:
            dctlpath = self._options.jsfile
        else:
            dctlpath = _replace_ext(os.path.basename(self._options.file), "js")
        dctlpath_nr = os.path.join(os.path.dirname(self._options.file),
                                   dctlpath)

        # Prepare PHP inclusion file path.
        phpincpath = None
        if self._options.incfile:
            phpincpath = self._options.incfile
        else:
            phpincpath = _replace_ext(os.path.basename(self._options.file),
                                      "inc")
        phpincpath_nr = os.path.join(os.path.dirname(self._options.file),
                                     phpincpath)

        # If style requested, fetch the .in file and resolve placeholders.
        if self._options.style:
            # Parse values given in the command line.
            stodict = dict([x[:2] for x in _styleopt_spec])
            for sopt in self._options.styleopt:
                lst = [x.strip() for x in sopt.split("=", 1)]
                if len(lst) < 2:
                    warning(p_("warning message",
                               "malformed CSS style option '%(opt)s'")
                            % dict(opt=sopt))
                    continue
                name, value = lst
                if name not in stodict:
                    warning(p_("warning message",
                               "unknown CSS style option '%(opt)s'")
                            % dict(opt=sopt))
                    continue
                stodict[name] = value

            # Replace placeholders in the input style sheet.
            raccl = LineAccumulator()
            raccl.read(stylesrc)
            styleaccl = LineAccumulator()
            sto_rx = re.compile("@(\w+)@")
            for line in raccl.lines:
                nline = ""
                lastpos = 0
                for m in sto_rx.finditer(line):
                    nline += line[lastpos:m.span()[0]]
                    lastpos = m.span()[1]
                    soname = m.group(1)
                    sovalue = stodict.get(soname)
                    if soname not in stodict:
                        error(p_("error message",
                                 "unknown CSS style option '%(opt)s' "
                                 "requested by the input style sheet "
                                 "'%(fname)s'")
                              % dict(opt=soname, fname=stylesrc))
                    nline += sovalue
                nline += line[lastpos:]
                styleaccl(nline)

        # Create separate CSS and JS files, or raw inclusion file,
        # or collect everything for direct embedding.
        auxaccl = None
        if not self._options.phpinc and not self._options.allinone:
            shutil.copyfile(_src_dctl_file, dctlpath_nr)
            if self._options.style:
                styleaccl.write(stylepath_nr)
            phpincpath = None # _fmt_header checks this for what to include
        else:
            raccl = LineAccumulator()
            raccl("<script type='text/javascript'>")
            raccl.read(_src_dctl_file)
            raccl("</script>")
            raccl()
            if self._options.style:
                raccl("<style type='text/css'>")
                raccl(styleaccl)
                raccl("</style>")
                raccl()
            if not self._options.allinone:
                raccl.write(phpincpath_nr)
            else:
                auxaccl = raccl

        # Header.
        accl_head = LineAccumulator(self._indent, 0)
        if not self._options.header:
            gname = tfn(gloss.title(tlang, env)[0].text)
            if env:
                ename = tfn(gloss.environments[env].name(tlang, env)[0].text)
                title = p_("top page title",
                           "%(gloss)s (%(env)s)") \
                        % dict(gloss=gname, env=ename)
            else:
                title = gname
            self._fmt_header(accl_head, tlang, title,
                             stylepath, dctlpath, phpincpath)
        else:
            accl_head.read(self._options.header)

        # Footer.
        accl_foot = LineAccumulator(self._indent, 0)
        if not self._options.footer:
            self._fmt_footer(accl_foot)
        else:
            accl_foot.read(self._options.footer)

        # Collect everything and write out the HTML page.
        accl_all = LineAccumulator(self._indent, 0)
        accl_all(accl_head)
        if auxaccl:
            accl_all(auxaccl, 2)
        accl_all(accl)
        accl_all(accl_foot)
        accl_all.write(self._options.file)