def report(text, showcmd=False, subsrc=None, file=sys.stdout, newline=True): """ Generic report. Text is output to the file descriptor, with one newline appended by default. @param text: text to report @type text: string @param showcmd: whether to show the command name @type showcmd: bool @param subsrc: more detailed source of the text @type subsrc: C{None} or string @param file: send output to this file descriptor @type file: C{file} @param newline: whether to append newline to output @type newline: bool """ if not isinstance(text, ColorString): text = ColorString("%s") % text text = text.resolve(dest=file) cmdname = None if showcmd: cmdname = os.path.basename(sys.argv[0]) lines = text.split("\n") for i in range(len(lines)): if i == 0: if cmdname and subsrc: head = "%s (%s): " % (cmdname, subsrc) elif cmdname: head = "%s: " % cmdname elif subsrc: head = "(%s): " % subsrc else: head = "" lhead = len(head) else: if lhead: head = "... " else: head = "" lines[i] = head + lines[i] if newline: lines.append("") text = "\n".join(lines) encwrite(file, text)
def spell_error(msg, cat, faultyWord, suggestions): """Print formated rule error message on screen @param msg: pology.message.Message object @param cat: pology.catalog.Catalog object @param faultyWord: badly spelled word @param suggestions : list of correct words to suggest""" report("-" * 40) report( ColorString("<bold>%s:%d(%d)</bold>") % (cat.filename, msg.refline, msg.refentry)) if msg.msgctxt: report( _("@info", "<bold>Context:</bold> %(snippet)s", snippet=msg.msgctxt)) #TODO: color in red part of context that make the mistake report( _("@info", "<bold>Faulty word:</bold> <red>%(word)s</red>", word=faultyWord)) if suggestions: report( _("@info", "<bold>Suggestions:</bold> %(wordlist)s", wordlist=format_item_list(suggestions)))
def _highlight_spans(text, spans, color, ftext=None): """ Adds colors around highlighted spans in text. Spans are given as list of index tuples C{[(start1, end1), ...]} where start and end index have standard Python semantics. Span tuples can have more than two elements, with indices followed by additional elements, which are ignored by this function. If start or end index in a span is not an integer, the span is ignored. The C{color} parameter is one of the color tags available in L{ColorString<colors.ColorString>} markup. If C{ftext} is not C{None}, spans are understood as relative to it, and the function will try to adapt them to the main text (see L{pology.diff.adapt_spans}). @param text: text to be highlighted @type text: string @param spans: spans to highlight @type spans: list of tuples @param color: color tag @type color: string @param ftext: text to which spans are actually relative @type ftext: string @returns: highlighted text @rtype: string """ if not spans or color is None: return text # Adapt spans regardless if filtered text has been given or not, # to fix any overlapping and put into expected ordering. if ftext is None: ftext = text spans = adapt_spans(text, ftext, spans, merge=True) if not spans: return text ctext = "" cstart = 0 for span in spans: if not isinstance(span[0], int) or not isinstance(span[1], int): continue ctext += text[cstart:span[0]] ctext += (ColorString("<%s>%%s</%s>" % (color, color)) % text[span[0]:span[1]]) # outside, to have auto-escaping cstart = span[1] ctext += text[span[1]:] return ctext
def to_string(self): """ Translate the text to get ordinary string. @returns: translated text @rtype: L{ColorString<colors.ColorString>} """ if self._msgid_plural is None: trf = _tr.ugettext # camouflaged against xgettext if self._msgctxt is None: msgstr = trf(self._msgid) else: msgstr = trf("%s\x04%s" % (self._msgctxt, self._msgid)) if "\x04" in msgstr: msgstr = self._msgid else: n = self._kwargs.get("num") if n is None or not isinstance(n, int): raise PologyError( _("@info", "No '%(arg)s' keyword argument to " "plural translation request.", arg="num")) trf = _tr.ungettext # camouflaged against xgettext if self._msgctxt is None: msgstr = trf(self._msgid, self._msgid_plural, n) else: msgstr = trf("%s\x04%s" % (self._msgctxt, self._msgid), self._msgid_plural, n) if "\x04" in msgstr: msgstr = self._msgid msgstr = ColorString(msgstr) # before substituting arguments msgstr = msgstr % self._kwargs return msgstr
def _renew_lines_bymod(self, mod, wrapf=wrap_field, force=False, colorize=0): prefix = {} if self.obsolete: prefix["curr"] = "#~ " prefix["prev"] = "#~| " else: prefix["curr"] = "" prefix["prev"] = "#| " if force or mod["manual_comment"] or not self._lines_manual_comment: self._lines_manual_comment = [] for manc in self.manual_comment: ls = wrap_comment_unwrap("", manc) if colorize >= 2: ls = [ColorString("<grey>%s</grey>") % x for x in ls] self._lines_manual_comment.extend(ls) if force or mod["auto_comment"] or not self._lines_auto_comment: self._lines_auto_comment = [] for autoc in self.auto_comment: ls = wrap_comment_unwrap(".", autoc) if colorize >= 2: ls = [ColorString("<blue>%s</blue>") % x for x in ls] self._lines_auto_comment.extend(ls) if force or mod["source"] or not self._lines_source: self._lines_source = [] srcrefs = [] for src in self.source: if src[1] > 0: srcrefs.append(src[0] + ":" + str(src[1])) else: srcrefs.append(src[0]) if srcrefs: ls = wrap_comment(":", cjoin(srcrefs, " ")) if colorize >= 2: ls = [ColorString("<blue>%s</blue>") % x for x in ls] self._lines_source = ls if force or mod["flag"] or not self._lines_flag: self._lines_flag = [] # Rearange so that fuzzy is first, if present. flst = [] for fl in self.flag: if fl == u"fuzzy": if colorize >= 1: fl = ColorString("<underline>%s</underline>") % fl flst.insert(0, fl) else: flst.append(fl) if flst: ls = wrap_comment(",", cjoin(flst, ", ")) if colorize >= 2: ls = [ColorString("<blue>%s</blue>") % x for x in ls] self._lines_flag = ls for att in _Message_single_fields: att_lins = "_lines_" + att if force or mod[att] or not self.__dict__[att_lins]: # modcount of this string > 0 or lines not cached or forced self.__dict__[att_lins] = [] msgsth = getattr(self, att) if msgsth is not None or att in _Message_mandatory_fields: if msgsth is None: msgsth = u"" if att.endswith("_previous"): fname = att[:-len("_previous")] pstat = "prev" else: fname = att pstat = "curr" if colorize >= 1: fname = ColorString("<bold>%s</bold>") % fname self.__dict__[att_lins] = wrapf(fname, _escape(msgsth), prefix[pstat]) # msgstr must be renewed if the plurality of the message changed. new_plurality = (getattr(self, "_lines_msgstr", []) and ((self.msgid_plural is None and "msgstr[" in self._lines_msgstr[0]) or (self.msgid_plural is not None and "msgstr[" not in self._lines_msgstr[0]))) if force or mod["msgstr"] or not self._lines_msgstr or new_plurality: self._lines_msgstr = [] msgstr = self.msgstr or [u""] if self.msgid_plural is None: fname = "msgstr" if colorize >= 1: fname = ColorString("<bold>%s</bold>") % fname self._lines_msgstr.extend( wrapf(fname, _escape(msgstr[0]), prefix["curr"])) else: for i in range(len(msgstr)): fname = "msgstr[%d]" % i if colorize >= 1: fname = ColorString("<bold>%s</bold>") % fname self._lines_msgstr.extend( wrapf(fname, _escape(msgstr[i]), prefix["curr"])) # Marshal the lines into proper order. self._lines_all = [] lins = self._lines_all lins.extend(self._lines_manual_comment) lins.extend(self._lines_auto_comment) if not self.obsolete: # no source for an obsolete message lins.extend(self._lines_source) lins.extend(self._lines_flag) # Actually, it might make sense regardless... ## Old originals makes sense only for a message with a fuzzy flag. #if self.fuzzy: lins.extend(self._lines_msgctxt_previous) lins.extend(self._lines_msgid_previous) lins.extend(self._lines_msgid_plural_previous) lins.extend(self._lines_msgctxt) lins.extend(self._lines_msgid) lins.extend(self._lines_msgid_plural) lins.extend(self._lines_msgstr) if self._lines_all[-1] != "\n": lins.extend(u"\n")
def tabulate(data, coln=None, rown=None, dfmt=None, space=" ", none="", rotated=False, colorize=False, indent="", colnra=False, rownra=False, colw=0): """ Tabulate data in plain text. All data fields can have missing trailing entries. They will be set to C{None} according to table extents. Examples: >>> print T.tabulate(data=((1, 4), (2, ), (3, 6)), ... coln=("c1", "c2", "c3"), rown=("r1", "r2"), ... space=" ", none="-") - c1 c2 c3 r1 1 2 3 r2 4 - 6 @param data: column entries (cells) by column @type data: [[string*]*] @param coln: column names @type coln: [string*] @param rown: row names @type rown: [string*] @param dfmt: format strings per column (e.g. C{"%+.2f"} for floats) @type dfmt: [string*] @param space: fill-in for spacing between cells @type space: string @param none: fill-in for displaying empty cells (i.e. C{None}-valued) @type none: string @param rotated: whether the table should be transposed @type rotated: bool @param colorize: whether the table should have color highlighting @type colorize: bool @param indent: indent string for the whole table @type indent: string @param colnra: right align column names @type colnra: bool @param rownra: right align row names @type rownra: bool @param colw: minimal column width @type colw: integer @returns: plain text representation of the table (no trailing newline) @rtype: string/L{ColorString<colors.ColorString>} """ # Make local copies, to be able to extend to table extents. _data = [] for col in data: _data.append(list(col)) _coln = None if coln: _coln = list(coln) _rown = None if rown: _rown = list(rown) _dfmt = None if dfmt: _dfmt = list(dfmt) # Calculate maximum row and column number. # ...look at data: nrows = 0 ncols = 0 for col in _data: if nrows < len(col): nrows = len(col) ncols += 1 # ...look at column and row names: if _coln is not None: if ncols < len(_coln): ncols = len(_coln) if _rown is not None: if nrows < len(_rown): nrows = len(_rown) # Index offsets due to column/row names. ro = 0 if _coln is not None: ro = 1 co = 0 if _rown is not None: co = 1 # Extend all missing table fields. # ...add columns: for c in range(len(_data), ncols): _data.append([]) # ...add rows: for col in _data: for r in range(len(col), nrows): col.append(None) # ...add column names: if _coln is not None: if _rown is not None: _coln.insert(0, none) # header corner for c in range(len(_coln), ncols + co): _coln.append(None) # ...add row names: if _rown is not None: if _coln is not None: _rown.insert(0, none) # header corner for r in range(len(_rown), nrows + ro): _rown.append(None) # ...add formats: if _dfmt is None: _dfmt = [] if _rown is not None: _dfmt.insert(0, u"%s") # header corner for c in range(len(_dfmt), ncols + co): _dfmt.append(u"%s") # Stringize data. # ...nice fat deep assembly of empty stringized table: sdata = [[u"" for i in range(nrows + ro)] for j in range(ncols + co)] # ...table body: for c in range(ncols): for r in range(nrows): if _data[c][r] is not None: sdata[c + co][r + ro] = _dfmt[c + co] % (_data[c][r], ) else: sdata[c + co][r + ro] = none # ...column names: if _coln is not None: for c in range(ncols + co): if _coln[c] is not None: sdata[c][0] = u"%s" % (_coln[c], ) # ...row names: if _rown is not None: for r in range(nrows + ro): if _rown[r] is not None: sdata[0][r] = u"%s" % (_rown[r], ) # Rotate needed data for output. if rotated: _coln, _rown = _rown, _coln ncols, nrows = nrows, ncols co, ro = ro, co sdata_r = [[u"" for i in range(nrows + ro)] for j in range(ncols + co)] for c in range(ncols + co): for r in range(nrows + ro): sdata_r[c][r] = sdata[r][c] sdata = sdata_r # Calculate maximum lengths per screen column. maxlen = [colw] * (ncols + co) for c in range(ncols + co): for r in range(nrows + ro): l = len(sdata[c][r]) if maxlen[c] < l: maxlen[c] = l # Reformat strings to maximum length per column. for c in range(co, ncols + co): lfmt = u"%" + str(maxlen[c]) + "s" for r in range(ro, nrows + ro): sdata[c][r] = lfmt % (sdata[c][r], ) # ...but column names aligned as requested: if _coln is not None: if colnra: lfmt = u"%" + str(maxlen[c]) + "s" else: lfmt = u"%-" + str(maxlen[c]) + "s" sdata[c][0] = lfmt % (sdata[c][0], ) if colorize: sdata[c][0] = ColorString("<purple>%s</purple>") % sdata[c][0] # ...but row names aligned as requested: if _rown is not None: if rownra: lfmt = u"%" + str(maxlen[0]) + "s" else: lfmt = u"%-" + str(maxlen[0]) + "s" for r in range(nrows + ro): sdata[0][r] = lfmt % (sdata[0][r], ) if colorize: sdata[0][r] = ColorString("<blue>%s</blue>") % sdata[0][r] # Assemble the table. lines = [] for r in range(nrows + ro): cells = [] for c in range(ncols + co): cells.append(sdata[c][r]) lines.append(indent + cjoin(cells, space)) return cjoin(lines, "\n")
def _msg_pos_fmt(path, line, col): return (ColorString("<cyan>%s</cyan>:<purple>%d</purple>" "(<purple>#%d</purple>)") % (path, line, col))
def report_msg_content(msg, cat, wrapf=None, force=False, note=None, delim=None, highlight=None, showmsg=True, fmsg=None, showfmsg=False, subsrc=None, file=sys.stdout): """ Report the content of a PO message. Provides the message reference, consisting of the catalog name and the message position within it, the message contents, and any notes on particular segments. Parts of the message can be highlighted using colors. Parameter C{highlight} provides the highlighting specification, as list of tuples where each tuple consists of: name of the message element to highlight, element index (used when the element is a list of values), list of spans, and optionally the filtered text of the element value. For example, to highlight spans C{(5, 10)} and C{(15, 25)} in the C{msgid}, and C{(30, 40)} in C{msgstr}, the highlighting specification would be:: [("msgid", 0, [(5, 10), (15, 25)]), ("msgstr", 0, [(30, 40)])] Names of the elements that can presently be highlighted are: C{"msgctxt"}, C{"msgid"}, C{"msgid_plural"}, C{"msgstr"}, C{"manual_comment"}, C{"auto_comment"}, C{"source"}, C{"flag"}. For unique fields the element index is not used, but 0 should be given for consistency (may be enforced later). Span tuples can have a third element, following the indices, which is the note about why the particular span is highlighted; there may be more elements after the note, and these are all ignored. If start or end index of a span is not an integer, then the note is taken as relating to the complete field. Sometimes the match to which the spans correspond has been made on a filtered value of the message field (e.g. after accelerator markers or tags have been removed). In that case, the filtered text can be given as the fourth element of the tuple, after the list of spans, and the function will try to fit spans from filtered onto original text. More globally, if the complete highlight is relative to a modified, filtered version of the message, this message can be given as C{fmsg} parameter. The display of content can be controlled by C{showmsg} parameter; if it is C{False}, only the message reference and span notes are shown. Similarly for the C{showfmsg} parameter, which controls the display of the content of filtered message (if given by C{fmsg}). To show the filtered message may be useful for debugging filtering in cases when it is not straightforward, or it is user-defined. @param msg: the message to report the content for @type msg: L{Message_base} @param cat: the catalog where the message lives @type cat: L{Catalog} or C{None} @param wrapf: the function used for wrapping message fields in output. See L{to_lines()<message.Message_base.to_lines>} method of message classes for details. If not given, it will be taken from the catalog (see L{Catalog.wrapf<catalog.Catalog.wrapf>}). @type wrapf: (string)->[string...] @param force: whether to force reformatting of cached message content @type force: bool @param note: note about why the content is being reported @type note: string @param delim: text to print on the line following the message @type delim: C{None} or string @param highlight: highlighting specification of message elements @type highlight: (see description) @param showmsg: show content of the message @type showmsg: bool @param fmsg: filtered message @type fmsg: L{Message_base} @param showfmsg: show content of the filtered message, if any @type showfmsg: bool @param subsrc: more detailed source of the message @type subsrc: C{None} or string @param file: output stream @type file: file """ rsegs = [] wrapf = wrapf or cat.wrapf() notes_data = [] if highlight: msg = Message(msg) # must work on copy, highlight modifies it ffmsg = fmsg or msg # use original message as filtered if not given # Unify spans for same parts, to have single coloring pass per part # (otherwise markup can get corrupted). highlightd = {} for hspec in highlight: name, item, spans = hspec[:3] pkey = (name, item) phspec = highlightd.get(pkey) if phspec is None: # Make needed copies in order not to modify # the original highlight when adding stuff later. highlightd[pkey] = list(hspec) highlightd[pkey][2] = list(spans) else: phspec[2].extend(spans) # Take filtered text if available and not already taken. if len(hspec) > 3 and len(phspec) <= 3: phspec.append(hspec[3]) highlight = highlightd.values() for hspec in highlight: name, item, spans = hspec[:3] def hl(text, ftext): if len(hspec) > 3: # Override filtered text from filtered message # by filtered text from the highlight spec. ftext = hspec[3] aspans = adapt_spans(text, ftext, spans, merge=False) notes_data.append((text, name, item, aspans)) text = _highlight_spans(text, spans, "red", ftext=ftext) return text if name == "msgctxt": if msg.msgctxt or ffmsg.msgctxt: msg.msgctxt = hl(msg.msgctxt or u"", ffmsg.msgctxt or u"") elif name == "msgid": msg.msgid = hl(msg.msgid, ffmsg.msgid) elif name == "msgid_plural": msg.msgid_plural = hl(msg.msgid_plural or u"", ffmsg.msgid_plural or u"") elif name == "msgstr": msg.msgstr[item] = hl(msg.msgstr[item], ffmsg.msgstr[item]) elif name == "manual_comment": msg.manual_comment[item] = hl(msg.manual_comment[item], ffmsg.manual_comment[item]) elif name == "auto_comment": msg.auto_comment[item] = hl(msg.auto_comment[item], ffmsg.auto_comment[item]) elif name == "source": msg.source[item] = Monpair( (hl(msg.source[item][0], ffmsg.source[item][0]), msg.source[item][1])) elif name == "flag": pass # FIXME: How to do this? else: warning( _("@info", "Unknown field '%(field)s' " "in highlighting specification.", field=name)) # Report the message. msegs = [] if cat is not None: msegs += [_msg_pos_fmt(cat.filename, msg.refline, msg.refentry) + "\n"] if showmsg: msgstr = msg.to_string(wrapf=wrapf, force=force, colorize=1) msegs += [msgstr.rstrip() + "\n"] if msegs: rsegs.append(cjoin(msegs).rstrip()) # Report notes. if note is not None: # global notestr = _("@info", "<bold>[note]</bold> %(msg)s", msg=note) rsegs.append(notestr) if notes_data: # span notes note_ord = 1 for text, name, item, spans in notes_data: if msg.msgid_plural is not None and name == "msgstr": name = "%s_%d" % (name, item) for span in spans: if len(span) < 3: continue start, end, snote = span if isinstance(start, int) and isinstance(end, int): seglen = end - start if seglen > 0: segtext = text[start:end] if len(segtext) > 30: segtext = _("@item:intext shortened longer text", "%(snippet)s...", snippet=segtext[:27]) posinfo = "%s:%d:\"%s\"" % (name, start, escape(segtext)) else: posinfo = "%s:%d" % (name, start) else: posinfo = "%s" % name posinfo = ColorString("<green>%s</green>") % posinfo rsegs.append( _("@info", "[%(pos)s]: %(msg)s", pos=posinfo, msg=snote)) note_ord += 1 # Report the filtered message, if given and requested. if fmsg and showfmsg: fmtnote = (ColorString("<green>%s</green>") % _("@info", ">>> Filtered message was:")) rsegs.append(fmtnote) fmsgstr = fmsg.to_string(wrapf=wrapf, force=force, colorize=1) mstr = fmsgstr.rstrip() + "\n" rsegs.append(mstr.rstrip()) if delim: rsegs.append(delim) rtext = cjoin(rsegs, "\n").rstrip() report(rtext, subsrc=subsrc, file=file)
def report_on_msg_hl(highlight, msg, cat, fmsg=None, subsrc=None, file=sys.stdout): """ Report on parts of a PO message. For each of the spans found in the L{highlight<report_msg_content>} specification which have a note attached, outputs the position reference (catalog name, message position, spanned segment) and the span note. The highlight can be relative to a somewhat modified, filtered message instead of the original one. @param highlight: highlight specification @type highlight: L{highlight<report_msg_content>} @param msg: the message for which the text is reported @type msg: L{Message_base} @param cat: the catalog where the message lives @type cat: L{Catalog} @param fmsg: filtered message to which the highlight corresponds @type fmsg: L{Message_base} @param subsrc: more detailed source of the message @type subsrc: C{None} or string @param file: send output to this file descriptor @type file: C{file} """ refpos = _msg_pos_fmt(cat.filename, msg.refline, msg.refentry) if not fmsg: # use original message as filtered if not given fmsg = msg for hspec in highlight: name, item, spans = hspec[:3] if name == "msgctxt": text = msg.msgctxt or u"" ftext = fmsg.msgctxt or u"" elif name == "msgid": text = msg.msgid ftext = fmsg.msgid elif name == "msgid_plural": text = msg.msgid_plural or u"" ftext = fmsg.msgid_plural or u"" elif name == "msgstr": text = msg.msgstr[item] ftext = fmsg.msgstr[item] # TODO: Add more fields. else: warning( _("@info", "Unknown field '%(field)s' " "in highlighting specification.", field=name)) continue if len(hspec) > 3: # Override filtered text from filtered message # by filtered text from the highlight spec. ftext = hspec[3] spans = adapt_spans(text, ftext, spans, merge=False) if msg.msgid_plural is not None and name == "msgstr": name = "%s_%d" % (name, item) for span in spans: if len(span) < 3: continue start, end, snote = span if isinstance(start, int) and isinstance(end, int): seglen = end - start if seglen > 0: segtext = text[start:end] if len(segtext) > 30: segtext = segtext[:27] + "..." posinfo = "%s:%d:\"%s\"" % (name, start, escape(segtext)) else: posinfo = "%s:%d" % (name, start) else: posinfo = "%s" % name posinfo = ColorString("<green>%s</green>") % posinfo rtext = cinterp("%s[%s]: %s", refpos, posinfo, snote) report(rtext, subsrc=subsrc, showcmd=False)
def _bar_stats(self, counts, title, count, summed, dlabel, dcolumn): # Count categories to display and chars/colors associated to them. # Note: Use only characters from Latin1. tspecs = (("trn", u"×", "green"), ("fuz", u"¤", "blue"), ("unt", u"·", "red")) # Find out maximum counts overall. maxcounts = dict(trn=0, fuz=0, unt=0, tot=0) maxcounts_jumbled = maxcounts.copy() for otitle, ocount, osummed in counts: # If absolute bars, compare counts only for non-summed counts. if self.p.absolute and osummed: continue # Count both messages and words, for the number display padding. for tkey in maxcounts_jumbled: for dcol in (0, 1): c = ocount[tkey][dcol] if maxcounts_jumbled[tkey] < c: maxcounts_jumbled[tkey] = c for tkey in maxcounts: c = ocount[tkey][dcolumn] if maxcounts[tkey] < c: maxcounts[tkey] = c # Character widths of maximum count categories. maxcountscw = {} for tkey, tval in maxcounts.iteritems(): maxcountscw[tkey] = len(str(tval)) maxcountscw_jumbled = {} for tkey, tval in maxcounts_jumbled.iteritems(): maxcountscw_jumbled[tkey] = len(str(tval)) # Formatted counts by disjunct categories. fmt_counts = [] for tkey, tchar, tcol in tspecs: cstr = str(count[tkey][dcolumn]) if cstr == "0": cstr = "-" cfmt = ("%%%ds" % maxcountscw_jumbled[tkey]) % cstr if tcol is not None: fmt_counts.append( (ColorString("<%s>%%s</%s>") % (tcol, tcol)) % cfmt) else: fmt_counts.append(cfmt) fmt_counts = cjoin(fmt_counts, "/") # Maximum and nominal bar widths in characters. # TODO: Make parameters. if self.inline: nombarcw = 20 maxbarcw = 50 else: nombarcw = 40 maxbarcw = 80 def roundnear(x): return int(round(x, 0)) def roundup(x): ix = int(x) if x - ix > 1e-16: ix += 1 return ix # Compute number of cells per category. n_cells = {} if self.p.absolute: # Absolute bar. n_per_cell = 0 for npc in (1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000): if npc * maxbarcw > maxcounts["tot"]: n_per_cell = npc break if not n_per_cell: warning( _("@info", "Count too large, cannot display bar graph.")) return for tkey, roundf in (("fuz", roundup), ("unt", roundup), ("tot", roundnear)): c = count[tkey][dcolumn] n_cells[tkey] = roundf(float(c) / n_per_cell) # Correct the situation when there are no cells. if n_cells["tot"] < 1: n_cells["tot"] = 1 # Correct the situation when the sum of cells fuzzy+untranslated # goes over the total; give priority to untranslated when reducing. while n_cells["fuz"] + n_cells["unt"] > n_cells["tot"]: if n_cells["fuz"] >= n_cells["unt"]: n_cells["fuz"] -= 1 else: n_cells["unt"] -= 1 n_cells["trn"] = n_cells["tot"] - n_cells["fuz"] - n_cells["unt"] else: # Relative bar. if count["tot"][dcolumn] > 0: n_per_cell = float(nombarcw) / count["tot"][dcolumn] else: n_per_cell = 0 for tkey in ("fuz", "unt"): c = count[tkey][dcolumn] n_cells[tkey] = roundup(c * n_per_cell) # When there are almost none translated, it may have happened that # the sum of cells fuzzy+untranslated is over nominal; reduce. while n_cells["fuz"] + n_cells["unt"] > nombarcw: if n_cells["fuz"] >= n_cells["unt"]: n_cells["fuz"] -= 1 else: n_cells["unt"] -= 1 n_cells["trn"] = nombarcw - n_cells["fuz"] - n_cells["unt"] # Create the bar. fmt_bar = [] for tkey, tchar, tcol in tspecs: bar = tchar * n_cells[tkey] if tcol is not None: bar = (ColorString("<%s>%%s</%s>") % (tcol, tcol)) % bar fmt_bar.append(bar) fmt_bar = cjoin(fmt_bar) # Assemble final output. if not self.p.absolute or not summed: if count["tot"][dcolumn] == 0: fmt_bar = "" report(cinterp("%s %s |%s|", fmt_counts, dlabel, fmt_bar)) else: report(cinterp("%s %s", fmt_counts, dlabel))