import re import urlparse from cherrypy.lib.tidy import html_break from web.lib.links import viewcvs_link, \ bugzilla_bug_link, \ ciavc_link # We use short variable names! # pylint: disable-msg=C0103 re_author = re.compile(r' 20[01]\d; ([^<]+)? *<([^@> ]+)@[^>]') re_author2 = re.compile(r' ') re_email2 = re.compile(r'([^@ ]+)@gentoo.org') re_file = re.compile(r'([\+-]?)(\S+)([:,]|[:,]$)') re_bugid = re.compile(r'([Bb][uU][gG]\s+?#?)(\d+)') re_url_base = '(https?://[^\s/)>]+(?:/[\S]+)?)' re_url = re.compile("([\s<(]*)"+re_url_base+"([\s>)\"']+?.?|$)?") re_url_notend = re.compile(r'[\s.)>\'"]+$') def _pretty_changelog_pass1(cat, pn, changelog): """Changelog prettification, pass1: replace text with markers""" changelog = changelog.strip() changelog_lines = changelog.splitlines() i = 0 while changelog_lines is not None and \ len(changelog_lines) > i and \ changelog_lines[i] is not None and \ changelog_lines[i].startswith('*'): i += 1 seen_files = False seen_author = False authorname = None authorid = None while True and len(changelog_lines) > i: oldline = changelog_lines[i] line = oldline.split() newline = [] if not seen_author or not seen_files: for w in line: if not seen_author and '@gentoo.org' in w: (authorname, authorid) = \ extract_changelog_entry_author(oldline) if authorid == '': print 'Bad changelog entry for %s/%s = "%r"' \ % (cat, pn, changelog) w = re_email1.sub('__CIA_VC__\\1__/CIA_VC__', w) w = re_email2.sub(' __CIA_VC__\\1__/CIA_VC__', w) seen_author = True elif not seen_files and seen_author: w = re_file.sub('\\1__FILE__\\2__/FILE__\\3', w) newline.append(w) changelog_lines[i] = ' '.join(newline) else: # re.IGNORECASE does not work on Unicode strings in 2.4! newline = oldline def bug_markup(m): return '%s__BUG__%s__/BUG__' % (m.group(1), m.group(2)) newline = _single_pass_re_loop(re_bugid, bug_markup, newline) def url_markup(m): prefix = m.group(1) url = m.group(2) suffix = m.group(3) if prefix is None: prefix = '' if suffix is None: suffix = '' extra_suffix = re_url_notend.search(url) if extra_suffix: extra_suffix = extra_suffix.group(0) suffix = extra_suffix + suffix url = url[0:-len(extra_suffix)] return '%s__URL__%s__/URL__%s' % (prefix, url, suffix) newline = _single_pass_re_loop(re_url, url_markup, newline) changelog_lines[i] = newline if oldline.endswith(':'): seen_files = True i += 1 changelog = "\n".join(changelog_lines) changelog.strip() if len(changelog) == 0: changelog = "No changelog entry available" return (changelog, authorname, authorid) def _pretty_changelog_pass2(changelog): """Now convert remaining stuff to be HTML. This catches all lurking entities as well as \\n""" changelog = html_break(changelog) return changelog re_m_ciavc = re.compile(r'__CIA_VC__(\S+)__/CIA_VC__ ?') def _pretty_changelog_pass3(changelog): """Convert author markup to CIA.vc links""" def markup(m): user = m.group(1) url = ciavc_link(user) return '(%s) ' % (url, user) changelog = _single_pass_re_loop(re_m_ciavc, markup, changelog) return changelog re_m_file = re.compile(r'__FILE__(\S+)__/FILE__') def _pretty_changelog_pass4(cat, pn, changelog): """Convert any file markup entries to Gentoo ViewCVS links""" def markup(m): filename = m.group(1) relpath = '%s/%s/%s' % (cat, pn, filename) url = viewcvs_link(relpath) return '%s' % (url, filename) changelog = _single_pass_re_loop(re_m_file, markup, changelog) return changelog re_m_bug = re.compile(r'__BUG__(\d+)__/BUG__') def _pretty_changelog_pass5(changelog): """Convert any bug id markup to bugzilla links""" def markup(m): bugid = m.group(1) url = bugzilla_bug_link(int(bugid)) return '%s' % (url, bugid) changelog = _single_pass_re_loop(re_m_bug, markup, changelog) return changelog re_m_url = re.compile(r'__URL__(\S+)__/URL__') def _pretty_changelog_pass6(changelog): """Convert any URL markup to real links""" def markup(m): group = m.group(1) url = urlparse.urlparse(group) if not url.scheme: url = 'http://' + m.group(1) else: url = group return '%s' % (url, url) changelog = _single_pass_re_loop(re_m_url, markup, changelog) return changelog def pretty_changelog(cat, pn, changelog): """Given a changelog snippet, make it look nice with HTML: - Make the body HTML-safe via entities. - Replace the author email with a CIA.vc link - Link the changed files to ViewCVS - Link any bug# entries to Bugzilla""" # text -> markup (changelog, authorname, authorid) = \ _pretty_changelog_pass1(cat, pn, changelog) # entities -> HTML changelog = _pretty_changelog_pass2(changelog) # user markup -> CIA.vc changelog = _pretty_changelog_pass3(changelog) # file markup -> sources.g.o link changelog = _pretty_changelog_pass4(cat, pn, changelog) # bug markup -> bugzilla link changelog = _pretty_changelog_pass5(changelog) # url markup -> real link changelog = _pretty_changelog_pass6(changelog) # Done! return (changelog, authorname, authorid) def optimal_collapse(atom, pnlength, pvlength, ellipsis = '^'): """Shrink the PN-PV string using well-placed ellipsis characters so that the maximum length of the string does not exceed the sum of the max specified PN and PV lengths. Retain the maximum amount of information.""" # TP[PN] = target length maxlength = pnlength + pvlength + 1 npn = pn = atom.package npv = pv = atom.fullver sep = '-' if atom.fullver is None: npv = pv = '' sep = '' tlpn = len(pn) tlpv = len(pv) pnpv = "%s%s%s" % (npn, sep, npv) i = 0 while len(pnpv) > maxlength and i < 25: if tlpv > pvlength: tlpv -= 1 npv = pv[0:tlpv-1] + '@' elif tlpn > pnlength: tlpn -= 1 npn = pn[0:tlpn-1] + '@' pnpv = "%s%s%s" % (npn, sep, npv) i += 1 return pnpv.replace('@', ellipsis) # vim:ts=4 et ft=python: