Exemple #1
0
    def renderHeaderItems(target):
        has_draft_items = review_utils.renderDraftItems(db, user, review, target)

        target = target.div("buttons")

        if not has_draft_items:
            if review.state == "open":
                if review.accepted(db):
                    target.button(id="closeReview", onclick="closeReview();").text("Close Review")
                else:
                    if user in review.owners or user.getPreference(db, "review.pingAnyReview"):
                        target.button(id="pingReview", onclick="pingReview();").text("Ping Review")
                    if user in review.owners or user.getPreference(db, "review.dropAnyReview"):
                        target.button(id="dropReview", onclick="dropReview();").text("Drop Review")

                if user in review.owners and not review.description:
                    target.button(id="writeDescription", onclick="editDescription();").text("Write Description")
            else:
                target.button(id="reopenReview", onclick="reopenReview();").text("Reopen Review")

        target.span("buttonscope buttonscope-global")
Exemple #2
0
    def renderHeaderItems(target):
        has_draft_items = review_utils.renderDraftItems(db, user, review, target)

        target = target.div("buttons")

        if not has_draft_items:
            if review.state == "open":
                if review.accepted(db):
                    target.button(id="closeReview", onclick="closeReview();").text("Close Review")
                else:
                    if user in review.owners or user.getPreference(db, "review.pingAnyReview"):
                        target.button(id="pingReview", onclick="pingReview();").text("Ping Review")
                    if user in review.owners or user.getPreference(db, "review.dropAnyReview"):
                        target.button(id="dropReview", onclick="dropReview();").text("Drop Review")

                if user in review.owners and not review.description:
                    target.button(id="writeDescription", onclick="editDescription();").text("Write Description")
            else:
                target.button(id="reopenReview", onclick="reopenReview();").text("Reopen Review")

        target.span("buttonscope buttonscope-global")
Exemple #3
0
 def generateButtons(target):
     review_utils.renderDraftItems(db, user, review, target)
     buttons = target.div("buttons")
     if user.getPreference(db, "debug.extensions.customProcessCommits"):
         buttons.button(onclick='customProcessCommits();').text("Process Commits")
     buttons.span("buttonscope buttonscope-global")
Exemple #4
0
def renderShowReviewLog(req, db, user):
    review_id = page.utils.getParameter(req, "review", filter=int)
    granularity = page.utils.getParameter(req, "granularity")
    unassigned = page.utils.getParameter(req, "unassigned", "no") == "yes"

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    document = htmlutils.Document(req)
    html = document.html()
    head = html.head()
    body = html.body()

    head.title("Review Log: %s" % review.branch.name)

    page.utils.generateHeader(
        body,
        db,
        user,
        lambda target: review_utils.renderDraftItems(db, user, review, target),
        extra_links=[("r/%d" % review.id, "Back to Review", True)])

    document.addExternalStylesheet("resource/showreviewlog.css")
    document.addExternalStylesheet("resource/review.css")
    document.addExternalScript("resource/review.js")
    document.addInternalScript(review.getJS())

    target = body.div("main")

    reviewed_reviewers = review_utils.getReviewedReviewers(db, review)

    def formatFiles(files):
        paths = sorted(
            [dbutils.describe_file(db, file_id) for file_id in files])
        if granularity == "file":
            return diff.File.eliminateCommonPrefixes(paths)
        else:
            modules = set()
            files = []
            for path in paths:
                match = re_module.match(path)
                if match: modules.add(match.group(1))
                else: files.append(path)
            return sorted(modules) + diff.File.eliminateCommonPrefixes(files)

    if reviewed_reviewers and not unassigned:
        reviewed = target.table('changes', align='center')
        reviewed.col(width="30%")
        reviewed.col(width="10%")
        reviewed.col(width="60%")
        reviewed.tr().td('h1', colspan=3).h1().text("Reviewed Changes")

        teams = review_utils.collectReviewTeams(reviewed_reviewers)

        for team in teams:
            row = reviewed.tr("reviewers")

            cell = row.td("reviewers")
            users = sorted([
                dbutils.User.fromId(db, user_id).fullname for user_id in team
            ])
            for user in users:
                cell.text(user).br()
            row.td("willreview").innerHTML("reviewed")

            cell = row.td("files")
            for file in formatFiles(teams[team]):
                cell.span("file").innerHTML(file).br()

    pending_reviewers = review_utils.getPendingReviewers(db, review)

    if pending_reviewers:
        pending = target.table('changes', align='center')
        pending.col(width="30%")
        pending.col(width="10%")
        pending.col(width="60%")
        pending.tr().td('h1', colspan=3).h1().text("Pending Changes")

        teams = review_utils.collectReviewTeams(pending_reviewers)

        if not unassigned:
            for team in teams:
                if team is not None:
                    row = pending.tr("reviewers")

                    cell = row.td("reviewers")
                    users = sorted([
                        dbutils.User.fromId(db, user_id).fullname
                        for user_id in team
                    ])
                    for user in users:
                        cell.text(user).br()
                    row.td("willreview").innerHTML("should review")

                    cell = row.td("files")
                    for file in formatFiles(teams[team]):
                        cell.span("file").innerHTML(file).br()

        if None in teams:
            row = pending.tr("reviewers")
            row.td("no-one",
                   colspan=2).text("No reviewers found for changes in")

            cell = row.td("files")
            for file in formatFiles(teams[None]):
                cell.span("file").innerHTML(file).br()

    return document
Exemple #5
0
 def generateButtons(target):
     review_utils.renderDraftItems(db, user, review, target)
     buttons = target.div("buttons")
     if user.getPreference(db, "debug.extensions.customProcessCommits"):
         buttons.button(onclick='customProcessCommits();').text("Process Commits")
     buttons.span("buttonscope buttonscope-global")
Exemple #6
0
def renderShowFile(req, db, user):
    cursor = db.cursor()

    sha1 = req.getParameter("sha1")
    path = req.getParameter("path")
    line = req.getParameter("line", None)
    review_id = req.getParameter("review", None, filter=int)

    default_tabify = "yes" if user.getPreference(db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    if line is None:
        first, last = None, None
    else:
        if "-" in line:
            first, last = map(int, line.split("-"))
        else:
            first = last = int(line)

        context = req.getParameter("context", user.getPreference(db, "commit.diff.contextLines"), int)

        first_with_context = max(1, first - context)
        last_with_context = last + context

    if user.getPreference(db, "commit.diff.compactMode"): default_compact = "yes"
    else: default_compact = "no"

    compact = req.getParameter("compact", default_compact) == "yes"

    if path[0] == '/':
        full_path = path
        if path != "/": path = path[1:]
    else:
        full_path = "/" + path
        if not path: path = "/"

    if review_id is None:
        review = None
        review_arg = ""
        repository_arg = req.getParameter("repository", "")
        if repository_arg:
            repository = gitutils.Repository.fromParameter(db, repository_arg)
        else:
            repository = gitutils.Repository.fromSHA1(db, sha1)
        repository_arg = "&repository=%d" % repository.id
    else:
        review = dbutils.Review.fromId(db, review_id)
        review_arg = "&review=%d" % review_id
        repository_arg = ""
        repository = review.repository

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    if review:
        page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review", True)])
    else:
        page.utils.generateHeader(body, db, user)

    document.addExternalStylesheet("resource/showfile.css")
    document.addInternalStylesheet(htmlutils.stripStylesheet(user.getResource(db, "syntax.css")[1], compact))

    commit = gitutils.Commit.fromSHA1(db, repository, sha1)
    file_sha1 = commit.getFileSHA1(full_path)
    file_id = dbutils.find_file(db, path=path)

    file = diff.File(file_id, path, None, file_sha1, repository)

    # A new file ID might have been added to the database, so need to commit.
    db.commit()

    if file.canHighlight():
        requestHighlights(repository, { file.new_sha1: (file.path, file.getLanguage()) })

    file.loadNewLines(True, request_highlight=True)

    if review:
        document.addInternalScript(user.getJS())
        document.addInternalScript(review.getJS())
        document.addInternalScript("var changeset = { parent: { id: %(id)d, sha1: %(sha1)r }, child: { id: %(id)d, sha1: %(sha1)r } };" % { 'id': commit.getId(db), 'sha1': commit.sha1 })
        document.addInternalScript("var files = { %(id)d: { new_sha1: %(sha1)r }, %(sha1)r: { id: %(id)d, side: 'n' } };" % { 'id': file_id, 'sha1': file_sha1 })
        document.addExternalStylesheet("resource/review.css")
        document.addExternalScript("resource/review.js")

        cursor.execute("""SELECT DISTINCT id
                          FROM commentchains
                            JOIN commentchainlines ON (id=chain)
                          WHERE review=%s
                            AND file=%s
                            AND sha1=%s
                            AND ((commentchains.state!='draft' OR commentchains.uid=%s)
                                 AND commentchains.state!='empty')
                          GROUP BY id""",
                       [review.id, file_id, file_sha1, user.id])

        chains = []
        comment_chain_script = ""

        for (chain_id,) in cursor.fetchall():
            chain = review_comment.CommentChain.fromId(db, chain_id, user, review=review)
            chain.loadComments(db, user)

            comment_chain_script += "commentChains.push(%s);\n" % chain.getJSConstructor(file_sha1)

        if comment_chain_script:
            document.addInternalScript(comment_chain_script)

    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/comment.js")
    document.addExternalScript("resource/showfile.js")

    if tabify:
        document.addExternalStylesheet("resource/tabify.css")
        document.addExternalScript("resource/tabify.js")
        tabwidth = file.getTabWidth()
        indenttabsmode = file.getIndentTabsMode()

    if user.getPreference(db, "commit.diff.highlightIllegalWhitespace"):
        document.addInternalStylesheet(user.getResource(db, "whitespace.css")[1], compact)

    if first is not None:
        document.addInternalScript("var firstSelectedLine = %d, lastSelectedLine = %d;" % (first, last))

    target = body.div("main")
    target.script(type="text/javascript").text("calculateTabWidth();")

    table = target.table('file show expanded paleyellow', align='center', cellspacing=0)

    columns = table.colgroup()
    columns.col('edge').empty()
    columns.col('linenr').empty()
    columns.col('line').empty()
    columns.col('middle').empty()
    columns.col('middle').empty()
    columns.col('line').empty()
    columns.col('linenr').empty()
    columns.col('edge').empty()

    thead = table.thead()
    cell = thead.tr().td('h1', colspan=8)
    h1 = cell.h1()

    h1.a("root", href="showtree?sha1=%s&path=/%s%s" % (sha1, review_arg, repository_arg)).text("root")
    h1.span().text('/')

    components = path.split("/")
    for index, component in enumerate(components[:-1]):
        h1.a(href="showtree?sha1=%s&path=%s%s%s" % (sha1, "/".join(components[:index + 1]), review_arg, repository_arg)).text(component)
        h1.span().text('/')

    if first is not None:
        h1.a(href="showfile?sha1=%s&path=%s%s%s" % (sha1, "/".join(components), review_arg, repository_arg)).text(components[-1])
    else:
        h1.text(components[-1])

    h1.span("right").a(href="/download/%s?repository=%s&sha1=%s" % (path, repository.name, file_sha1)).text("[download]")

    table.tbody('spacer top').tr('spacer top').td(colspan=8).text()

    tbody = table.tbody("lines")

    yield document.render(stop=tbody, pretty=not compact)

    for linenr, line in enumerate(file.newLines(True)):
        linenr = linenr + 1
        highlight_class = ""

        if first is not None:
            if not (first_with_context <= linenr <= last_with_context): continue
            if linenr == first:
                highlight_class += " first-selected"
            if linenr == last:
                highlight_class += " last-selected"

        if tabify:
            line = htmlutils.tabify(line, tabwidth, indenttabsmode)

        line = line.replace("\r", "<i class='cr'></i>")

        row = tbody.tr("line context single", id="f%do%dn%d" % (file.id, linenr, linenr))
        row.td("edge").text()
        row.td("linenr old").text(linenr)
        row.td("line single whole%s" % highlight_class, id="f%dn%d" % (file.id, linenr), colspan=4).innerHTML(line)
        row.td("linenr new").text(linenr)
        row.td("edge").text()

        if linenr % 500:
            yield document.render(stop=tbody, pretty=not compact)

    table.tbody('spacer bottom').tr('spacer bottom').td(colspan=8).text()

    yield document.render(pretty=not compact)
Exemple #7
0
def renderShowComments(req, db, user):
    context_lines = req.getParameter("context",
                                     user.getPreference(
                                         db, "comment.diff.contextLines"),
                                     filter=int)

    default_compact = "yes" if user.getPreference(
        db, "commit.diff.compactMode") else "no"
    compact = req.getParameter("compact", default_compact) == "yes"

    default_tabify = "yes" if user.getPreference(
        db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    original = req.getParameter("original", "no") == "yes"

    review_id = req.getParameter("review", filter=int)
    batch_id = req.getParameter("batch", None, filter=int)
    filter = req.getParameter("filter", "all")
    blame = req.getParameter("blame", None)

    profiler = profiling.Profiler()

    review = dbutils.Review.fromId(db, review_id)
    review.repository.enableBlobCache()

    cursor = db.cursor()

    profiler.check("create review")

    if blame is not None:
        blame_user = dbutils.User.fromName(db, blame)

        cursor.execute(
            """SELECT commentchains.id
                            FROM commentchains
                            JOIN commentchainlines ON (commentchainlines.chain=commentchains.id)
                            JOIN fileversions ON (fileversions.new_sha1=commentchainlines.sha1)
                            JOIN changesets ON (changesets.id=fileversions.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                            JOIN gitusers ON (gitusers.id=commits.author_gituser)
                            JOIN usergitemails USING (email)
                            JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id AND reviewchangesets.review=commentchains.review)
                           WHERE commentchains.review=%s
                             AND usergitemails.uid=%s
                             AND commentchains.state!='empty'
                             AND (commentchains.state!='draft' OR commentchains.uid=%s)
                        ORDER BY commentchains.file, commentchainlines.commit, commentchainlines.first_line""",
            (review.id, blame_user.id, user.id))

        include_chain_ids = set([chain_id for (chain_id, ) in cursor])

        profiler.check("initial blame filtering")
    else:
        include_chain_ids = None

    if filter == "toread":
        query = "SELECT commentchains.id FROM commentchains JOIN comments ON (comments.chain=commentchains.id) JOIN commentstoread ON (commentstoread.comment=comments.id) LEFT OUTER JOIN commentchainlines ON (commentchainlines.chain=commentchains.id) WHERE review=%s AND commentstoread.uid=%s ORDER BY file, commit, first_line"

        cursor.execute(query, (review.id, user.id))
    else:
        query = "SELECT id FROM commentchains LEFT OUTER JOIN commentchainlines ON (chain=id) WHERE review=%s AND commentchains.state!='empty'"
        arguments = [review.id]

        if filter == "issues":
            query += " AND type='issue' AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)
        elif filter == "draft-issues":
            query += " AND type='issue' AND commentchains.state='draft' AND commentchains.uid=%s"
            arguments.append(user.id)
        elif filter == "open-issues":
            query += " AND type='issue' AND commentchains.state='open'"
        elif filter == "addressed-issues":
            query += " AND type='issue' AND commentchains.state='addressed'"
        elif filter == "closed-issues":
            query += " AND type='issue' AND commentchains.state='closed'"
        elif filter == "notes":
            query += " AND type='note' AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)
        elif filter == "draft-notes":
            query += " AND type='note' AND commentchains.state='draft' AND commentchains.uid=%s"
            arguments.append(user.id)
        elif filter == "open-notes":
            query += " AND type='note' AND commentchains.state='open'"
        else:
            query += " AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)

        if batch_id is not None:
            query += " AND batch=%s"
            arguments.append(batch_id)

        # This ordering is inaccurate if comments apply to the same file but
        # different commits, but then, in that case there isn't really a
        # well-defined natural order either.  Two comments that apply to the
        # same file and commit will at least be order by line number, and that's
        # better than nothing.
        query += " ORDER BY file, commit, first_line"

        cursor.execute(query, arguments)

    profiler.check("main query")

    if include_chain_ids is None:
        chain_ids = [chain_id for (chain_id, ) in cursor]
    else:
        chain_ids = [
            chain_id for (chain_id, ) in cursor
            if chain_id in include_chain_ids
        ]

    profiler.check("query result")

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    document.addInternalScript(user.getJS(db))
    document.addInternalScript(review.getJS())

    page.utils.generateHeader(
        body,
        db,
        user,
        lambda target: review_utils.renderDraftItems(db, user, review, target),
        extra_links=[("r/%d" % review.id, "Back to Review", True)])

    profiler.check("page header")

    target = body.div("main")

    if chain_ids and not user.isAnonymous() and user.name == req.user:
        document.addInternalScript(
            "$(function () { markChainsAsRead([%s]); });" %
            ", ".join(map(str, chain_ids)))

    #yield document.render(stop=target, pretty=not compact)

    if chain_ids:
        processed = set()

        chains = []
        file_ids = set()
        changesets_files = {}
        changesets = {}

        if blame is not None:
            annotators = {}
            review.branch.loadCommits(db)
            commits = log.commitset.CommitSet(review.branch.commits)

        for chain_id in chain_ids:
            if chain_id in processed:
                continue
            else:
                processed.add(chain_id)

                chain = review_comment.CommentChain.fromId(db,
                                                           chain_id,
                                                           user,
                                                           review=review)
                chains.append(chain)

                if chain.file_id is not None:
                    file_ids.add(chain.file_id)
                    changesets_files.setdefault(
                        (chain.first_commit, chain.last_commit),
                        set()).add(chain.file_id)

        profiler.check("load chains")

        changeset_cache = {}

        for (from_commit,
             to_commit), filtered_file_ids in changesets_files.items():
            changesets[(from_commit,
                        to_commit)] = changeset_utils.createChangeset(
                            db,
                            user,
                            review.repository,
                            from_commit=from_commit,
                            to_commit=to_commit,
                            filtered_file_ids=filtered_file_ids)[0]
            profiler.check("create changesets")

            if blame is not None:
                annotators[(from_commit,
                            to_commit)] = operation.blame.LineAnnotator(
                                db,
                                from_commit,
                                to_commit,
                                file_ids=file_ids,
                                commits=commits,
                                changeset_cache=changeset_cache)
                profiler.check("create annotators")

        for chain in chains:
            if blame is not None and chain.file_id is not None:
                changeset = changesets[(chain.first_commit, chain.last_commit)]
                annotator = annotators[(chain.first_commit, chain.last_commit)]

                offset, count = chain.lines_by_sha1[changeset.getFile(
                    chain.file_id).new_sha1]

                if not annotator.annotate(chain.file_id,
                                          offset,
                                          offset + count - 1,
                                          check_user=blame_user):
                    continue

            profiler.check("detailed blame filtering")

            if chain.file_id is not None:
                from_commit, to_commit = review_html.getCodeCommentChainChangeset(
                    db, chain, original)
                changeset = changesets.get((from_commit, to_commit))
            else:
                changeset = None

            review_html.renderCommentChain(db,
                                           target,
                                           user,
                                           review,
                                           chain,
                                           context_lines=context_lines,
                                           compact=compact,
                                           tabify=tabify,
                                           original=original,
                                           changeset=changeset,
                                           linkify=linkify.Context(
                                               db=db,
                                               request=req,
                                               review=review))

            profiler.check("rendering")

            yield document.render(
                stop=target, pretty=not compact
            ) + "<script>console.log((new Date).toString());</script>"

            profiler.check("transfer")

        page.utils.renderShortcuts(target, "showcomments")
    else:
        target.h1(align="center").text("No comments.")

    profiler.output(db, user, document)

    yield document.render(pretty=not compact)
Exemple #8
0
def renderShowComments(req, db, user):
    context_lines = req.getParameter("context", user.getPreference(db, "comment.diff.contextLines"), filter=int)

    default_compact = "yes" if user.getPreference(db, "commit.diff.compactMode") else "no"
    compact = req.getParameter("compact", default_compact) == "yes"

    default_tabify = "yes" if user.getPreference(db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    original = req.getParameter("original", "no") == "yes"

    review_id = req.getParameter("review", filter=int)
    batch_id = req.getParameter("batch", None, filter=int)
    filter = req.getParameter("filter", "all")
    blame = req.getParameter("blame", None)

    profiler = profiling.Profiler()

    review = dbutils.Review.fromId(db, review_id)
    review.repository.enableBlobCache()

    cursor = db.cursor()

    profiler.check("create review")

    if blame is not None:
        blame_user = dbutils.User.fromName(db, blame)

        cursor.execute("""SELECT commentchains.id
                            FROM commentchains
                            JOIN commentchainlines ON (commentchainlines.chain=commentchains.id)
                            JOIN fileversions ON (fileversions.new_sha1=commentchainlines.sha1)
                            JOIN changesets ON (changesets.id=fileversions.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                            JOIN gitusers ON (gitusers.id=commits.author_gituser)
                            JOIN usergitemails USING (email)
                            JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id AND reviewchangesets.review=commentchains.review)
                           WHERE commentchains.review=%s
                             AND usergitemails.uid=%s
                             AND commentchains.state!='empty'
                             AND (commentchains.state!='draft' OR commentchains.uid=%s)
                        ORDER BY commentchains.file, commentchainlines.commit, commentchainlines.first_line""",
                       (review.id, blame_user.id, user.id))

        include_chain_ids = set([chain_id for (chain_id,) in cursor])

        profiler.check("initial blame filtering")
    else:
        include_chain_ids = None

    if filter == "toread":
        query = "SELECT commentchains.id FROM commentchains JOIN comments ON (comments.chain=commentchains.id) JOIN commentstoread ON (commentstoread.comment=comments.id) LEFT OUTER JOIN commentchainlines ON (commentchainlines.chain=commentchains.id) WHERE review=%s AND commentstoread.uid=%s ORDER BY file, commit, first_line"

        cursor.execute(query, (review.id, user.id))
    else:
        query = "SELECT id FROM commentchains LEFT OUTER JOIN commentchainlines ON (chain=id) WHERE review=%s AND commentchains.state!='empty'"
        arguments = [review.id]

        if filter == "issues":
            query += " AND type='issue' AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)
        elif filter == "draft-issues":
            query += " AND type='issue' AND commentchains.state='draft' AND commentchains.uid=%s"
            arguments.append(user.id)
        elif filter == "open-issues":
            query += " AND type='issue' AND commentchains.state='open'"
        elif filter == "addressed-issues":
            query += " AND type='issue' AND commentchains.state='addressed'"
        elif filter == "closed-issues":
            query += " AND type='issue' AND commentchains.state='closed'"
        elif filter == "notes":
            query += " AND type='note' AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)
        elif filter == "draft-notes":
            query += " AND type='note' AND commentchains.state='draft' AND commentchains.uid=%s"
            arguments.append(user.id)
        elif filter == "open-notes":
            query += " AND type='note' AND commentchains.state='open'"
        else:
            query += " AND (commentchains.state!='draft' OR commentchains.uid=%s)"
            arguments.append(user.id)

        if batch_id is not None:
            query += " AND batch=%s"
            arguments.append(batch_id)

        # This ordering is inaccurate if comments apply to the same file but
        # different commits, but then, in that case there isn't really a
        # well-defined natural order either.  Two comments that apply to the
        # same file and commit will at least be order by line number, and that's
        # better than nothing.
        query += " ORDER BY file, commit, first_line"

        cursor.execute(query, arguments)

    profiler.check("main query")

    if include_chain_ids is None:
        chain_ids = [chain_id for (chain_id,) in cursor]
    else:
        chain_ids = [chain_id for (chain_id,) in cursor if chain_id in include_chain_ids]

    profiler.check("query result")

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    document.addInternalScript(user.getJS(db))
    document.addInternalScript(review.getJS())

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review", True)])

    profiler.check("page header")

    target = body.div("main")

    if chain_ids and not user.isAnonymous() and user.name == req.user:
        document.addInternalScript("$(function () { markChainsAsRead([%s]); });" % ", ".join(map(str, chain_ids)))

    #yield document.render(stop=target, pretty=not compact)

    if chain_ids:
        processed = set()

        chains = []
        file_ids = set()
        changesets_files = {}
        changesets = {}

        if blame is not None:
            annotators = {}
            review.branch.loadCommits(db)
            commits = log.commitset.CommitSet(review.branch.commits)

        for chain_id in chain_ids:
            if chain_id in processed:
                continue
            else:
                processed.add(chain_id)

                chain = review_comment.CommentChain.fromId(db, chain_id, user, review=review)
                chains.append(chain)

                if chain.file_id is not None:
                    file_ids.add(chain.file_id)
                    changesets_files.setdefault((chain.first_commit, chain.last_commit), set()).add(chain.file_id)

        profiler.check("load chains")

        changeset_cache = {}

        for (from_commit, to_commit), filtered_file_ids in changesets_files.items():
            changesets[(from_commit, to_commit)] = changeset_utils.createChangeset(db, user, review.repository, from_commit=from_commit, to_commit=to_commit, filtered_file_ids=filtered_file_ids)[0]
            profiler.check("create changesets")

            if blame is not None:
                annotators[(from_commit, to_commit)] = operation.blame.LineAnnotator(db, from_commit, to_commit, file_ids=file_ids, commits=commits, changeset_cache=changeset_cache)
                profiler.check("create annotators")

        for chain in chains:
            if blame is not None and chain.file_id is not None:
                changeset = changesets[(chain.first_commit, chain.last_commit)]
                annotator = annotators[(chain.first_commit, chain.last_commit)]

                offset, count = chain.lines_by_sha1[changeset.getFile(chain.file_id).new_sha1]

                if not annotator.annotate(chain.file_id, offset, offset + count - 1, check_user=blame_user):
                    continue

            profiler.check("detailed blame filtering")

            if chain.file_id is not None:
                from_commit, to_commit = review_html.getCodeCommentChainChangeset(db, chain, original)
                changeset = changesets.get((from_commit, to_commit))
            else:
                changeset = None

            review_html.renderCommentChain(db, target, user, review, chain,
                                           context_lines=context_lines,
                                           compact=compact,
                                           tabify=tabify,
                                           original=original,
                                           changeset=changeset,
                                           linkify=linkify.Context(db=db, request=req, review=review))

            profiler.check("rendering")

            yield document.render(stop=target, pretty=not compact) + "<script>console.log((new Date).toString());</script>"

            profiler.check("transfer")

        page.utils.renderShortcuts(target, "showcomments")
    else:
        target.h1(align="center").text("No comments.")

    profiler.output(db, user, document)

    yield document.render(pretty=not compact)
Exemple #9
0
def renderShowReviewLog(req, db, user):
    review_id = page.utils.getParameter(req, "review", filter=int)
    granularity = page.utils.getParameter(req, "granularity")
    unassigned = page.utils.getParameter(req, "unassigned", "no") == "yes"

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    document = htmlutils.Document(req)
    html = document.html()
    head = html.head()
    body = html.body()

    head.title("Review Log: %s" % review.branch.name)

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review", True)])

    document.addExternalStylesheet("resource/showreviewlog.css")
    document.addExternalStylesheet("resource/review.css")
    document.addExternalScript("resource/review.js")
    document.addInternalScript(review.getJS())

    target = body.div("main")

    reviewed_reviewers = review_utils.getReviewedReviewers(db, review)

    def formatFiles(files):
        paths = sorted([dbutils.describe_file(db, file_id) for file_id in files])
        if granularity == "file":
            return diff.File.eliminateCommonPrefixes(paths)
        else:
            modules = set()
            files = []
            for path in paths:
                match = re_module.match(path)
                if match: modules.add(match.group(1))
                else: files.append(path)
            return sorted(modules) + diff.File.eliminateCommonPrefixes(files)

    if reviewed_reviewers and not unassigned:
        reviewed = target.table('changes', align='center')
        reviewed.col(width="30%")
        reviewed.col(width="10%")
        reviewed.col(width="60%")
        reviewed.tr().td('h1', colspan=3).h1().text("Reviewed Changes")

        teams = review_utils.collectReviewTeams(reviewed_reviewers)

        for team in teams:
            row = reviewed.tr("reviewers")

            cell = row.td("reviewers")
            users = sorted([dbutils.User.fromId(db, user_id).fullname for user_id in team])
            for user in users: cell.text(user).br()
            row.td("willreview").innerHTML("reviewed")

            cell = row.td("files")
            for file in formatFiles(teams[team]):
                cell.span("file").innerHTML(file).br()

    pending_reviewers = review_utils.getPendingReviewers(db, review)

    if pending_reviewers:
        pending = target.table('changes', align='center')
        pending.col(width="30%")
        pending.col(width="10%")
        pending.col(width="60%")
        pending.tr().td('h1', colspan=3).h1().text("Pending Changes")

        teams = review_utils.collectReviewTeams(pending_reviewers)

        if not unassigned:
            for team in teams:
                if team is not None:
                    row = pending.tr("reviewers")

                    cell = row.td("reviewers")
                    users = sorted([dbutils.User.fromId(db, user_id).fullname for user_id in team])
                    for user in users: cell.text(user).br()
                    row.td("willreview").innerHTML("should&nbsp;review")

                    cell = row.td("files")
                    for file in formatFiles(teams[team]):
                        cell.span("file").innerHTML(file).br()

        if None in teams:
            row = pending.tr("reviewers")
            row.td("no-one", colspan=2).text("No reviewers found for changes in")

            cell = row.td("files")
            for file in formatFiles(teams[None]):
                cell.span("file").innerHTML(file).br()

    return document
Exemple #10
0
def renderManageReviewers(req, db, user):
    review_id = req.getParameter("review", filter=int)

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    root_directories = {}
    root_files = {}

    def processFile(file_id):
        components = dbutils.describe_file(db, file_id).split("/")
        directories, files = root_directories, root_files
        for directory_name in components[:-1]:
            directories, files = directories.setdefault(directory_name, ({}, {}))
        files[components[-1]] = file_id

    cursor.execute("SELECT file FROM reviewfiles WHERE review=%s", (review.id,))

    for (file_id,) in cursor:
        processFile(file_id)

    cursor.execute("SELECT name FROM users WHERE name IS NOT NULL")
    users = [user_name for (user_name,) in cursor if user_name]

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review", True)])

    document.addExternalStylesheet("resource/managereviewers.css")
    document.addExternalScript("resource/managereviewers.js")
    document.addInternalScript(user.getJS());
    document.addInternalScript(review.getJS());
    document.addInternalScript("var users = [ %s ];" % ", ".join([htmlutils.jsify(user_name) for user_name in sorted(users)]))

    target = body.div("main")

    basic = target.table('manage paleyellow', align='center')
    basic.col(width='10%')
    basic.col(width='60%')
    basic.col(width='30%')
    basic.tr().td('h1', colspan=3).h1().text("Manage Reviewers")

    row = basic.tr("current")
    row.td("select").text("Current:")
    cell = row.td("value")
    for index, reviewer in enumerate(review.reviewers):
        if index != 0: cell.text(", ")
        cell.span("reviewer", critic_username=reviewer.name).innerHTML(htmlutils.htmlify(reviewer.fullname).replace(" ", "&nbsp;"))
    row.td("right").text()

    row = basic.tr("reviewer")
    row.td("select").text("Reviewer:")
    row.td("value").input("reviewer").span("message")
    row.td("right").button("save").text("Save")

    row = basic.tr("help")
    row.td("help", colspan=3).text("Enter the name of a current reviewer to edit assignments (or unassign.)  Enter the name of another user to add a new reviewer.")

    row = basic.tr("headings")
    row.td("select").text("Assigned")
    row.td("path").text("Path")
    row.td().text()

    def outputDirectory(base, name, directories, files):
        if name:
            level = base.count("/")
            row = basic.tr("directory", critic_level=level)
            row.td("select").input(type="checkbox")
            if level > 1:
                row.td("path").preformatted().innerHTML((" " * (len(base) - 2)) + "&#8230;/" + name + "/")
            else:
                row.td("path").preformatted().innerHTML(base + name + "/")
            row.td().text()
        else:
            level = 0

        for directory_name in sorted(directories.keys()):
            outputDirectory(base + name + "/" if name else "", directory_name, directories[directory_name][0], directories[directory_name][1])

        for file_name in sorted(files.keys()):
            row = basic.tr("file", critic_file_id=files[file_name], critic_level=level + 1)
            row.td("select").input(type="checkbox")
            row.td("path").preformatted().innerHTML((" " * (len(base + name) - 1)) + "&#8230;/" + htmlutils.htmlify(file_name))
            row.td().text()

    outputDirectory("", "", root_directories, root_files)

    return document
Exemple #11
0
def renderManageReviewers(req, db, user):
    review_id = req.getParameter("review", filter=int)

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    root_directories = {}
    root_files = {}

    def processFile(file_id):
        components = dbutils.describe_file(db, file_id).split("/")
        directories, files = root_directories, root_files
        for directory_name in components[:-1]:
            directories, files = directories.setdefault(
                directory_name, ({}, {}))
        files[components[-1]] = file_id

    cursor.execute("SELECT file FROM reviewfiles WHERE review=%s",
                   (review.id, ))

    for (file_id, ) in cursor:
        processFile(file_id)

    cursor.execute("SELECT name FROM users WHERE name IS NOT NULL")
    users = [user_name for (user_name, ) in cursor if user_name]

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(
        body,
        db,
        user,
        lambda target: review_utils.renderDraftItems(db, user, review, target),
        extra_links=[("r/%d" % review.id, "Back to Review", True)])

    document.addExternalStylesheet("resource/managereviewers.css")
    document.addExternalScript("resource/managereviewers.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(review.getJS())
    document.addInternalScript(
        "var users = [ %s ];" %
        ", ".join([htmlutils.jsify(user_name) for user_name in sorted(users)]))

    target = body.div("main")

    basic = target.table('manage paleyellow', align='center')
    basic.col(width='10%')
    basic.col(width='60%')
    basic.col(width='30%')
    basic.tr().td('h1', colspan=3).h1().text("Manage Reviewers")

    row = basic.tr("current")
    row.td("select").text("Current:")
    cell = row.td("value")
    for index, reviewer in enumerate(review.reviewers):
        if index != 0: cell.text(", ")
        cell.span("reviewer", critic_username=reviewer.name).innerHTML(
            htmlutils.htmlify(reviewer.fullname).replace(" ", "&nbsp;"))
    row.td("right").text()

    row = basic.tr("reviewer")
    row.td("select").text("Reviewer:")
    row.td("value").input("reviewer").span("message")
    row.td("right").button("save").text("Save")

    row = basic.tr("help")
    row.td("help", colspan=3).text(
        "Enter the name of a current reviewer to edit assignments (or unassign.)  Enter the name of another user to add a new reviewer."
    )

    row = basic.tr("headings")
    row.td("select").text("Assigned")
    row.td("path").text("Path")
    row.td().text()

    def outputDirectory(base, name, directories, files):
        if name:
            level = base.count("/")
            row = basic.tr("directory", critic_level=level)
            row.td("select").input(type="checkbox")
            if level > 1:
                row.td("path").preformatted().innerHTML((" " *
                                                         (len(base) - 2)) +
                                                        "&#8230;/" + name +
                                                        "/")
            else:
                row.td("path").preformatted().innerHTML(base + name + "/")
            row.td().text()
        else:
            level = 0

        for directory_name in sorted(directories.keys()):
            outputDirectory(base + name + "/" if name else "", directory_name,
                            directories[directory_name][0],
                            directories[directory_name][1])

        for file_name in sorted(files.keys()):
            row = basic.tr("file",
                           critic_file_id=files[file_name],
                           critic_level=level + 1)
            row.td("select").input(type="checkbox")
            row.td("path").preformatted().innerHTML(
                (" " * (len(base + name) - 1)) + "&#8230;/" +
                htmlutils.htmlify(file_name))
            row.td().text()

    outputDirectory("", "", root_directories, root_files)

    return document
Exemple #12
0
 def generateRight(target):
     review_utils.renderDraftItems(db, user, review, target)
Exemple #13
0
def renderFilterChanges(req, db, user):
    review_id = page.utils.getParameter(req, "review", filter=int)
    first_sha1 = page.utils.getParameter(req, "first", None)
    last_sha1 = page.utils.getParameter(req, "last", None)

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    root_directories = {}
    root_files = {}

    def processFile(file_id):
        components = dbutils.describe_file(db, file_id).split("/")
        directories, files = root_directories, root_files
        for directory_name in components[:-1]:
            directories, files = directories.setdefault(directory_name, ({}, {}))
        files[components[-1]] = file_id

    if first_sha1 and last_sha1:
        sha1 = last_sha1
        changesets = []

        cursor.execute("""SELECT commits.sha1
                            FROM commits
                            JOIN changesets ON (changesets.child=commits.id)
                            JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id)
                           WHERE reviewchangesets.review=%s""",
                       (review.id,))

        first_commit = gitutils.Commit.fromSHA1(db, review.repository, first_sha1)
        last_commit = gitutils.Commit.fromSHA1(db, review.repository, last_sha1)

        if len(first_commit.parents) != 1:
            raise page.utils.DisplayMessage("Filtering failed!", "First selected commit is a merge commit.  Please go back and select a different range of commits.", review=review)

        from_commit = gitutils.Commit.fromSHA1(db, review.repository, first_commit.parents[0])
        to_commit = last_commit

        commits = log.commitset.CommitSet.fromRange(db, from_commit, to_commit)

        if not commits:
            raise page.utils.DisplayMessage("Filtering failed!", "The range of commits selected includes merges with ancestors not included in the range.  Please go back and select a different range of commits.", review=review)

        cursor.execute("""SELECT DISTINCT reviewfiles.file
                            FROM reviewfiles
                            JOIN changesets ON (changesets.id=reviewfiles.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                           WHERE reviewfiles.review=%s
                             AND commits.sha1=ANY (%s)""",
                       (review.id, [commit.sha1 for commit in commits]))
    else:
        cursor.execute("SELECT DISTINCT file FROM reviewfiles WHERE review=%s", (review.id,))

    for (file_id,) in cursor:
        processFile(file_id)

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(body, db, user, lambda target: review_utils.renderDraftItems(db, user, review, target), extra_links=[("r/%d" % review.id, "Back to Review", True)])

    document.addExternalStylesheet("resource/filterchanges.css")
    document.addExternalScript("resource/filterchanges.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(review.getJS())

    if first_sha1 and last_sha1:
        document.addInternalScript("var commitRange = { first: %s, last: %s };" % (htmlutils.jsify(first_sha1), htmlutils.jsify(last_sha1)))
    else:
        document.addInternalScript("var commitRange = null;")

    target = body.div("main")

    basic = target.table('filter paleyellow', align='center', cellspacing=0)
    basic.col(width='10%')
    basic.col(width='60%')
    basic.col(width='30%')
    row = basic.tr("header")
    row.td('h1', colspan=2).h1().text("Filter Changes")
    row.td('h1 button').button("display").text("Display Diff")

    row = basic.tr("headings")
    row.td("select").text("Include")
    row.td("path").text("Path")
    row.td().text()

    def outputDirectory(base, name, directories, files):
        if name:
            level = base.count("/")
            row = basic.tr("directory", critic_level=level)
            row.td("select").input(type="checkbox")
            if level > 1:
                row.td("path").preformatted().innerHTML((" " * (len(base) - 2)) + "&#8230;/" + name + "/")
            else:
                row.td("path").preformatted().innerHTML(base + name + "/")
            row.td().text()
        else:
            level = 0

        for directory_name in sorted(directories.keys()):
            outputDirectory(base + name + "/" if name else "", directory_name, directories[directory_name][0], directories[directory_name][1])

        for file_name in sorted(files.keys()):
            row = basic.tr("file", critic_file_id=files[file_name], critic_level=level + 1)
            row.td("select").input(type="checkbox")
            row.td("path").preformatted().innerHTML((" " * (len(base + name) - 1)) + "&#8230;/" + htmlutils.htmlify(file_name))
            row.td().text()

    outputDirectory("", "", root_directories, root_files)

    row = basic.tr("footer")
    row.td('spacer', colspan=3)

    row = basic.tr("footer")
    row.td('button', colspan=3).button("display").text("Display Diff")

    if user.getPreference(db, "ui.keyboardShortcuts"):
        page.utils.renderShortcuts(body, "filterchanges")

    return document
Exemple #14
0
def renderShowBatch(req, db, user):
    batch_id = page.utils.getParameter(req, "batch", None, filter=int)
    review_id = page.utils.getParameter(req, "review", None, filter=int)

    cursor = db.cursor()

    user = dbutils.User.fromName(
        db, page.utils.getParameter(req, "user", req.user))

    if batch_id is None and review_id is None:
        return page.utils.displayMessage(db, req, user,
                                         "Missing argument: 'batch'")

    if batch_id:
        cursor.execute(
            "SELECT review, uid, comment, time FROM batches WHERE id=%s",
            (batch_id, ))
        review_id, author_id, chain_id, time = cursor.fetchone()
        author = dbutils.User.fromId(db, author_id)
    else:
        chain_id = None
        time = None
        author = user

    review = dbutils.Review.fromId(db, review_id)

    if chain_id:
        batch_chain = review_comment.CommentChain.fromId(db,
                                                         chain_id,
                                                         user,
                                                         review=review)
        batch_chain.loadComments(db, user)
    else:
        batch_chain = None

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(
        body,
        db,
        user,
        lambda target: review_utils.renderDraftItems(db, user, review, target),
        extra_links=[("r/%d" % review.id, "Back to Review", True)])

    document.addExternalStylesheet("resource/showbatch.css")
    document.addExternalStylesheet("resource/showreview.css")
    document.addExternalStylesheet("resource/review.css")
    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/review.js")
    document.addExternalScript("resource/comment.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(review.getJS())

    if batch_chain:
        document.addInternalScript(
            "commentChainById[%d] = %s;" %
            (batch_chain.id, batch_chain.getJSConstructor()))

    target = body.div("main")

    basic = target.table('paleyellow basic', align='center')
    basic.col(width='10%')
    basic.col(width='80%')
    basic.col(width='10%')
    basic.tr().td('h1', colspan=3).h1().text("Review by %s" %
                                             htmlify(author.fullname))

    if batch_chain:
        batch_chain.loadComments(db, user)

        row = basic.tr("line")
        row.td("heading").text("Comment:")
        row.td("value").preformatted().div("text").text(
            htmlify(batch_chain.comments[0].comment))
        row.td("status").text()

    def renderFiles(title):
        files = []

        for file_id, delete_count, insert_count in cursor.fetchall():
            files.append(
                (dbutils.describe_file(db,
                                       file_id), delete_count, insert_count))

        paths = []
        deleted = []
        inserted = []

        for path, delete_count, insert_count in sorted(files):
            paths.append(path)
            deleted.append(delete_count)
            inserted.append(insert_count)

        if paths:
            diff.File.eliminateCommonPrefixes(paths)

            row = basic.tr("line")
            row.td("heading").text(title)

            table = row.td("files").table("files")
            headers = table.thead().tr()
            headers.th("path").text("Changed Files")
            headers.th(colspan=2).text("Lines")

            files = table.tbody()
            for path, delete_count, insert_count in zip(
                    paths, deleted, inserted):
                file = files.tr()
                file.td("path").preformatted().innerHTML(path)
                file.td().preformatted().text(
                    delete_count and "-%d" % delete_count or "")
                file.td().preformatted().text(
                    delete_count and "+%d" % insert_count or "")

            row.td("status").text()

    def condition(table_name):
        if batch_id:
            return "%s.batch=%d" % (table_name, batch_id)
        else:
            return "review=%d AND %s.batch IS NULL AND %s.uid=%d" % (
                review.id, table_name, table_name, author.id)

    cursor.execute("""SELECT reviewfiles.file, SUM(deleted), SUM(inserted)
                        FROM reviewfiles
                        JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id)
                       WHERE %s
                         AND reviewfilechanges.to='reviewed'
                    GROUP BY reviewfiles.file""" %
                   condition("reviewfilechanges"))
    renderFiles("Reviewed:")

    cursor.execute("""SELECT reviewfiles.file, SUM(deleted), SUM(inserted)
                        FROM reviewfiles
                        JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id)
                       WHERE %s
                         AND reviewfilechanges.to='pending'
                    GROUP BY reviewfiles.file""" %
                   condition("reviewfilechanges"))
    renderFiles("Unreviewed:")

    def renderChains(title, replies):
        all_chains = [
            review_comment.CommentChain.fromId(db, id, user, review=review)
            for (id, ) in rows
        ]

        for chain in all_chains:
            chain.loadComments(db, user)

        issue_chains = filter(lambda chain: chain.type == "issue", all_chains)
        draft_issues = filter(lambda chain: chain.state == "draft",
                              issue_chains)
        open_issues = filter(lambda chain: chain.state == "open", issue_chains)
        addressed_issues = filter(lambda chain: chain.state == "addressed",
                                  issue_chains)
        closed_issues = filter(lambda chain: chain.state == "closed",
                               issue_chains)
        note_chains = filter(lambda chain: chain.type == "note", all_chains)
        draft_notes = filter(
            lambda chain: chain.state == "draft" and chain != batch_chain,
            note_chains)
        open_notes = filter(
            lambda chain: chain.state == "open" and chain != batch_chain,
            note_chains)

        def renderChains(target, chains):
            for chain in chains:
                row = target.tr("comment")
                row.td("author").text(chain.user.fullname)
                row.td("title").a(href="showcomment?chain=%d" %
                                  chain.id).innerHTML(chain.leader())
                row.td("when").text(chain.when())

        if draft_issues or open_issues or addressed_issues or closed_issues:
            chains = target.table("paleyellow comments",
                                  align="center",
                                  cellspacing=0)
            chains.tr().td("h1", colspan=3).h1().text(title)

            if draft_issues:
                chains.tr(id="draft-issues").td(
                    "h2", colspan=3).h2().text("Draft Issues").a(
                        href="showcomments?review=%d&filter=draft-issues" %
                        review.id).text("[display all]")
                renderChains(chains, draft_issues)

            if batch_id is not None or replies:
                if open_issues:
                    h2 = chains.tr(id="open-issues").td(
                        "h2", colspan=3).h2().text("Still Open Issues")
                    if batch_id:
                        h2.a(
                            href=
                            "showcomments?review=%d&filter=open-issues&batch=%d"
                            % (review.id, batch_id)).text("[display all]")
                    renderChains(chains, open_issues)

                if addressed_issues:
                    h2 = chains.tr(id="addressed-issues").td(
                        "h2", colspan=3).h2().text("Now Addressed Issues")
                    if batch_id:
                        h2.a(
                            href=
                            "showcomments?review=%d&filter=addressed-issues&batch=%d"
                            % (review.id, batch_id)).text("[display all]")
                    renderChains(chains, addressed_issues)

                if closed_issues:
                    h2 = chains.tr(id="closed-issues").td(
                        "h2", colspan=3).h2().text("Now Closed Issues")
                    if batch_id:
                        h2.a(
                            href=
                            "showcomments?review=%d&filter=closed-issues&batch=%d"
                            % (review.id, batch_id)).text("[display all]")
                    renderChains(chains, closed_issues)

        if draft_notes or open_notes:
            chains = target.table("paleyellow comments",
                                  align="center",
                                  cellspacing=0)
            chains.tr().td("h1", colspan=3).h1().text(title)

            if draft_notes:
                chains.tr(id="draft-notes").td(
                    "h2", colspan=3).h2().text("Draft Notes").a(
                        href="showcomments?review=%d&filter=draft-notes" %
                        review.id).text("[display all]")
                renderChains(chains, draft_notes)

            if open_notes:
                h2 = chains.tr(id="notes").td("h2",
                                              colspan=3).h2().text("Notes")
                if batch_id:
                    h2.a(
                        href="showcomments?review=%d&filter=open-notes&batch=%d"
                        % (review.id, batch_id)).text("[display all]")
                renderChains(chains, open_notes)

    cursor.execute("SELECT id FROM commentchains WHERE %s AND type='issue'" %
                   condition("commentchains"))
    rows = cursor.fetchall()

    if rows: renderChains("Raised Issues", False)

    cursor.execute("SELECT id FROM commentchains WHERE %s AND type='note'" %
                   condition("commentchains"))
    rows = cursor.fetchall()

    if rows: renderChains("Written Notes", False)

    cursor.execute("""SELECT commentchains.id
                        FROM commentchains
                        JOIN comments ON (comments.chain=commentchains.id)
                       WHERE %s
                         AND comments.id!=commentchains.first_comment""" %
                   condition("comments"))
    rows = cursor.fetchall()

    if rows: renderChains("Replied To", True)

    return document
Exemple #15
0
 def renderHeaderItems(target):
     review_utils.renderDraftItems(db, user, review, target)
     target.div("buttons").span("buttonscope buttonscope-global")
Exemple #16
0
 def generateRight(target):
     review_utils.renderDraftItems(db, user, review, target)
Exemple #17
0
 def renderHeaderItems(target):
     review_utils.renderDraftItems(db, user, review, target)
     target.div("buttons").span("buttonscope buttonscope-global")
Exemple #18
0
def renderShowBatch(req, db, user):
    batch_id = page.utils.getParameter(req, "batch", None, filter=int)
    review_id = page.utils.getParameter(req, "review", None, filter=int)

    cursor = db.cursor()

    user = dbutils.User.fromName(db, page.utils.getParameter(req, "user", req.user))

    if batch_id is None and review_id is None:
        page.utils.displayMessage(db, req, user, "Missing argument: 'batch'")
        return

    if batch_id:
        cursor.execute("SELECT review, uid, comment, time FROM batches WHERE id=%s", (batch_id,))
        review_id, author_id, chain_id, time = cursor.fetchone()
        author = dbutils.User.fromId(db, author_id)
    else:
        chain_id = None
        time = None
        author = user

    review = dbutils.Review.fromId(db, review_id)

    if chain_id:
        batch_chain = review_comment.CommentChain.fromId(db, chain_id, user, review=review)
        batch_chain.loadComments(db, user)
    else:
        batch_chain = None

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(
        body,
        db,
        user,
        lambda target: review_utils.renderDraftItems(db, user, review, target),
        extra_links=[("r/%d" % review.id, "Back to Review", True)],
    )

    document.addExternalStylesheet("resource/showbatch.css")
    document.addExternalStylesheet("resource/showreview.css")
    document.addExternalStylesheet("resource/review.css")
    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/review.js")
    document.addExternalScript("resource/comment.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(review.getJS())

    if batch_chain:
        document.addInternalScript("commentChainById[%d] = %s;" % (batch_chain.id, batch_chain.getJSConstructor()))

    target = body.div("main")

    basic = target.table("paleyellow basic", align="center")
    basic.col(width="10%")
    basic.col(width="80%")
    basic.col(width="10%")
    basic.tr().td("h1", colspan=3).h1().text("Review by %s" % htmlify(author.fullname))

    if batch_chain:
        batch_chain.loadComments(db, user)

        row = basic.tr("line")
        row.td("heading").text("Comment:")
        row.td("value").preformatted().div("text").text(htmlify(batch_chain.comments[0].comment))
        row.td("status").text()

    def renderFiles(title):
        files = []

        for file_id, delete_count, insert_count in cursor.fetchall():
            files.append((dbutils.describe_file(db, file_id), delete_count, insert_count))

        paths = []
        deleted = []
        inserted = []

        for path, delete_count, insert_count in sorted(files):
            paths.append(path)
            deleted.append(delete_count)
            inserted.append(insert_count)

        if paths:
            diff.File.eliminateCommonPrefixes(paths)

            row = basic.tr("line")
            row.td("heading").text(title)

            table = row.td("files").table("files")
            headers = table.thead().tr()
            headers.th("path").text("Changed Files")
            headers.th(colspan=2).text("Lines")

            files = table.tbody()
            for path, delete_count, insert_count in zip(paths, deleted, inserted):
                file = files.tr()
                file.td("path").preformatted().innerHTML(path)
                file.td().preformatted().text(delete_count and "-%d" % delete_count or "")
                file.td().preformatted().text(delete_count and "+%d" % insert_count or "")

            row.td("status").text()

    def condition(table_name):
        if batch_id:
            return "%s.batch=%d" % (table_name, batch_id)
        else:
            return "review=%d AND %s.batch IS NULL AND %s.uid=%d" % (review.id, table_name, table_name, author.id)

    cursor.execute(
        """SELECT reviewfiles.file, SUM(deleted), SUM(inserted)
                        FROM reviewfiles
                        JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id)
                       WHERE %s
                         AND reviewfilechanges.to='reviewed'
                    GROUP BY reviewfiles.file"""
        % condition("reviewfilechanges")
    )
    renderFiles("Reviewed:")

    cursor.execute(
        """SELECT reviewfiles.file, SUM(deleted), SUM(inserted)
                        FROM reviewfiles
                        JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id)
                       WHERE %s
                         AND reviewfilechanges.to='pending'
                    GROUP BY reviewfiles.file"""
        % condition("reviewfilechanges")
    )
    renderFiles("Unreviewed:")

    def renderChains(title, replies):
        all_chains = [review_comment.CommentChain.fromId(db, id, user, review=review) for (id,) in rows]

        for chain in all_chains:
            chain.loadComments(db, user)

        issue_chains = filter(lambda chain: chain.type == "issue", all_chains)
        draft_issues = filter(lambda chain: chain.state == "draft", issue_chains)
        open_issues = filter(lambda chain: chain.state == "open", issue_chains)
        addressed_issues = filter(lambda chain: chain.state == "addressed", issue_chains)
        closed_issues = filter(lambda chain: chain.state == "closed", issue_chains)
        note_chains = filter(lambda chain: chain.type == "note", all_chains)
        draft_notes = filter(lambda chain: chain.state == "draft" and chain != batch_chain, note_chains)
        open_notes = filter(lambda chain: chain.state == "open" and chain != batch_chain, note_chains)

        def renderChains(target, chains):
            for chain in chains:
                row = target.tr("comment")
                row.td("author").text(chain.user.fullname)
                row.td("title").a(href="showcomment?chain=%d" % chain.id).innerHTML(chain.leader())
                row.td("when").text(chain.when())

        if draft_issues or open_issues or addressed_issues or closed_issues:
            chains = target.table("paleyellow comments", align="center", cellspacing=0)
            chains.tr().td("h1", colspan=3).h1().text(title)

            if draft_issues:
                chains.tr(id="draft-issues").td("h2", colspan=3).h2().text("Draft Issues").a(
                    href="showcomments?review=%d&filter=draft-issues" % review.id
                ).text("[display all]")
                renderChains(chains, draft_issues)

            if batch_id is not None or replies:
                if open_issues:
                    h2 = chains.tr(id="open-issues").td("h2", colspan=3).h2().text("Still Open Issues")
                    if batch_id:
                        h2.a(href="showcomments?review=%d&filter=open-issues&batch=%d" % (review.id, batch_id)).text(
                            "[display all]"
                        )
                    renderChains(chains, open_issues)

                if addressed_issues:
                    h2 = chains.tr(id="addressed-issues").td("h2", colspan=3).h2().text("Now Addressed Issues")
                    if batch_id:
                        h2.a(
                            href="showcomments?review=%d&filter=addressed-issues&batch=%d" % (review.id, batch_id)
                        ).text("[display all]")
                    renderChains(chains, addressed_issues)

                if closed_issues:
                    h2 = chains.tr(id="closed-issues").td("h2", colspan=3).h2().text("Now Closed Issues")
                    if batch_id:
                        h2.a(href="showcomments?review=%d&filter=closed-issues&batch=%d" % (review.id, batch_id)).text(
                            "[display all]"
                        )
                    renderChains(chains, closed_issues)

        if draft_notes or open_notes:
            chains = target.table("paleyellow comments", align="center", cellspacing=0)
            chains.tr().td("h1", colspan=3).h1().text(title)

            if draft_notes:
                chains.tr(id="draft-notes").td("h2", colspan=3).h2().text("Draft Notes").a(
                    href="showcomments?review=%d&filter=draft-notes" % review.id
                ).text("[display all]")
                renderChains(chains, draft_notes)

            if open_notes:
                h2 = chains.tr(id="notes").td("h2", colspan=3).h2().text("Notes")
                if batch_id:
                    h2.a(href="showcomments?review=%d&filter=open-notes&batch=%d" % (review.id, batch_id)).text(
                        "[display all]"
                    )
                renderChains(chains, open_notes)

    cursor.execute("SELECT id FROM commentchains WHERE %s AND type='issue'" % condition("commentchains"))
    rows = cursor.fetchall()

    if rows:
        renderChains("Raised Issues", False)

    cursor.execute("SELECT id FROM commentchains WHERE %s AND type='note'" % condition("commentchains"))
    rows = cursor.fetchall()

    if rows:
        renderChains("Written Notes", False)

    cursor.execute(
        """SELECT commentchains.id
                        FROM commentchains
                        JOIN comments ON (comments.chain=commentchains.id)
                       WHERE %s
                         AND comments.id!=commentchains.first_comment"""
        % condition("comments")
    )
    rows = cursor.fetchall()

    if rows:
        renderChains("Replied To", True)

    return document
Exemple #19
0
def renderFilterChanges(req, db, user):
    review_id = page.utils.getParameter(req, "review", filter=int)
    first_sha1 = page.utils.getParameter(req, "first", None)
    last_sha1 = page.utils.getParameter(req, "last", None)

    cursor = db.cursor()

    review = dbutils.Review.fromId(db, review_id)

    root_directories = {}
    root_files = {}

    def processFile(file_id):
        components = dbutils.describe_file(db, file_id).split("/")
        directories, files = root_directories, root_files
        for directory_name in components[:-1]:
            directories, files = directories.setdefault(
                directory_name, ({}, {}))
        files[components[-1]] = file_id

    if first_sha1 and last_sha1:
        sha1 = last_sha1
        changesets = []

        cursor.execute(
            """SELECT commits.sha1
                            FROM commits
                            JOIN changesets ON (changesets.child=commits.id)
                            JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id)
                           WHERE reviewchangesets.review=%s""", (review.id, ))

        first_commit = gitutils.Commit.fromSHA1(db, review.repository,
                                                first_sha1)
        last_commit = gitutils.Commit.fromSHA1(db, review.repository,
                                               last_sha1)

        if len(first_commit.parents) != 1:
            raise page.utils.DisplayMessage(
                "Filtering failed!",
                "First selected commit is a merge commit.  Please go back and select a different range of commits.",
                review=review)

        from_commit = gitutils.Commit.fromSHA1(db, review.repository,
                                               first_commit.parents[0])
        to_commit = last_commit

        commits = log.commitset.CommitSet.fromRange(db, from_commit, to_commit)

        if not commits:
            raise page.utils.DisplayMessage(
                "Filtering failed!",
                "The range of commits selected includes merges with ancestors not included in the range.  Please go back and select a different range of commits.",
                review=review)

        cursor.execute(
            """SELECT DISTINCT reviewfiles.file
                            FROM reviewfiles
                            JOIN changesets ON (changesets.id=reviewfiles.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                           WHERE reviewfiles.review=%s
                             AND commits.sha1=ANY (%s)""",
            (review.id, [commit.sha1 for commit in commits]))
    else:
        cursor.execute("SELECT DISTINCT file FROM reviewfiles WHERE review=%s",
                       (review.id, ))

    for (file_id, ) in cursor:
        processFile(file_id)

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    page.utils.generateHeader(
        body,
        db,
        user,
        lambda target: review_utils.renderDraftItems(db, user, review, target),
        extra_links=[("r/%d" % review.id, "Back to Review", True)])

    document.addExternalStylesheet("resource/filterchanges.css")
    document.addExternalScript("resource/filterchanges.js")
    document.addInternalScript(user.getJS())
    document.addInternalScript(review.getJS())

    if first_sha1 and last_sha1:
        document.addInternalScript(
            "var commitRange = { first: %s, last: %s };" %
            (htmlutils.jsify(first_sha1), htmlutils.jsify(last_sha1)))
    else:
        document.addInternalScript("var commitRange = null;")

    target = body.div("main")

    basic = target.table('filter paleyellow', align='center', cellspacing=0)
    basic.col(width='10%')
    basic.col(width='60%')
    basic.col(width='30%')
    row = basic.tr("header")
    row.td('h1', colspan=2).h1().text("Filter Changes")
    row.td('h1 button').button("display").text("Display Diff")

    row = basic.tr("headings")
    row.td("select").text("Include")
    row.td("path").text("Path")
    row.td().text()

    def outputDirectory(base, name, directories, files):
        if name:
            level = base.count("/")
            row = basic.tr("directory", critic_level=level)
            row.td("select").input(type="checkbox")
            if level > 1:
                row.td("path").preformatted().innerHTML((" " *
                                                         (len(base) - 2)) +
                                                        "&#8230;/" + name +
                                                        "/")
            else:
                row.td("path").preformatted().innerHTML(base + name + "/")
            row.td().text()
        else:
            level = 0

        for directory_name in sorted(directories.keys()):
            outputDirectory(base + name + "/" if name else "", directory_name,
                            directories[directory_name][0],
                            directories[directory_name][1])

        for file_name in sorted(files.keys()):
            row = basic.tr("file",
                           critic_file_id=files[file_name],
                           critic_level=level + 1)
            row.td("select").input(type="checkbox")
            row.td("path").preformatted().innerHTML(
                (" " * (len(base + name) - 1)) + "&#8230;/" +
                htmlutils.htmlify(file_name))
            row.td().text()

    outputDirectory("", "", root_directories, root_files)

    row = basic.tr("footer")
    row.td('spacer', colspan=3)

    row = basic.tr("footer")
    row.td('button', colspan=3).button("display").text("Display Diff")

    if user.getPreference(db, "ui.keyboardShortcuts"):
        page.utils.renderShortcuts(body, "filterchanges")

    return document
Exemple #20
0
def renderShowFile(req, db, user):
    cursor = db.cursor()

    sha1 = req.getParameter("sha1")
    path = req.getParameter("path")
    line = req.getParameter("line", None)
    review_id = req.getParameter("review", None, filter=int)

    default_tabify = "yes" if user.getPreference(
        db, "commit.diff.visualTabs") else "no"
    tabify = req.getParameter("tabify", default_tabify) == "yes"

    if line is None:
        first, last = None, None
    else:
        if "-" in line:
            first, last = map(int, line.split("-"))
        else:
            first = last = int(line)

        context = req.getParameter(
            "context", user.getPreference(db, "commit.diff.contextLines"), int)

        first_with_context = max(1, first - context)
        last_with_context = last + context

    if user.getPreference(db, "commit.diff.compactMode"):
        default_compact = "yes"
    else:
        default_compact = "no"

    compact = req.getParameter("compact", default_compact) == "yes"

    if path[0] == '/':
        full_path = path
        if path != "/": path = path[1:]
    else:
        full_path = "/" + path
        if not path: path = "/"

    if review_id is None:
        review = None
        review_arg = ""
        repository_arg = req.getParameter("repository", "")
        if repository_arg:
            repository = gitutils.Repository.fromParameter(db, repository_arg)
        else:
            repository = gitutils.Repository.fromSHA1(db, sha1)
        repository_arg = "&repository=%d" % repository.id
    else:
        review = dbutils.Review.fromId(db, review_id)
        review_arg = "&review=%d" % review_id
        repository_arg = ""
        repository = review.repository

    document = htmlutils.Document(req)

    html = document.html()
    head = html.head()
    body = html.body()

    if review:
        page.utils.generateHeader(body,
                                  db,
                                  user,
                                  lambda target: review_utils.renderDraftItems(
                                      db, user, review, target),
                                  extra_links=[("r/%d" % review.id,
                                                "Back to Review", True)])
    else:
        page.utils.generateHeader(body, db, user)

    document.addExternalStylesheet("resource/showfile.css")
    document.addInternalStylesheet(
        htmlutils.stripStylesheet(
            user.getResource(db, "syntax.css")[1], compact))

    commit = gitutils.Commit.fromSHA1(db, repository, sha1)
    file_sha1 = commit.getFileSHA1(full_path)
    file_id = dbutils.find_file(db, path=path)

    file = diff.File(file_id, path, None, file_sha1, repository)

    # A new file ID might have been added to the database, so need to commit.
    db.commit()

    if file.canHighlight():
        requestHighlights(repository,
                          {file.new_sha1: (file.path, file.getLanguage())})

    file.loadNewLines(True, request_highlight=True)

    if review:
        document.addInternalScript(user.getJS())
        document.addInternalScript(review.getJS())
        document.addInternalScript(
            "var changeset = { parent: { id: %(id)d, sha1: %(sha1)r }, child: { id: %(id)d, sha1: %(sha1)r } };"
            % {
                'id': commit.getId(db),
                'sha1': commit.sha1
            })
        document.addInternalScript(
            "var files = { %(id)d: { new_sha1: %(sha1)r }, %(sha1)r: { id: %(id)d, side: 'n' } };"
            % {
                'id': file_id,
                'sha1': file_sha1
            })
        document.addExternalStylesheet("resource/review.css")
        document.addExternalScript("resource/review.js")

        cursor.execute(
            """SELECT DISTINCT id
                          FROM commentchains
                            JOIN commentchainlines ON (id=chain)
                          WHERE review=%s
                            AND file=%s
                            AND sha1=%s
                            AND ((commentchains.state!='draft' OR commentchains.uid=%s)
                                 AND commentchains.state!='empty')
                          GROUP BY id""",
            [review.id, file_id, file_sha1, user.id])

        chains = []
        comment_chain_script = ""

        for (chain_id, ) in cursor.fetchall():
            chain = review_comment.CommentChain.fromId(db,
                                                       chain_id,
                                                       user,
                                                       review=review)
            chain.loadComments(db, user)

            comment_chain_script += "commentChains.push(%s);\n" % chain.getJSConstructor(
                file_sha1)

        if comment_chain_script:
            document.addInternalScript(comment_chain_script)

    document.addExternalStylesheet("resource/comment.css")
    document.addExternalScript("resource/comment.js")
    document.addExternalScript("resource/showfile.js")

    if tabify:
        document.addExternalStylesheet("resource/tabify.css")
        document.addExternalScript("resource/tabify.js")
        tabwidth = file.getTabWidth()
        indenttabsmode = file.getIndentTabsMode()

    if user.getPreference(db, "commit.diff.highlightIllegalWhitespace"):
        document.addInternalStylesheet(
            user.getResource(db, "whitespace.css")[1], compact)

    if first is not None:
        document.addInternalScript(
            "var firstSelectedLine = %d, lastSelectedLine = %d;" %
            (first, last))

    target = body.div("main")
    target.script(type="text/javascript").text("calculateTabWidth();")

    table = target.table('file show expanded paleyellow',
                         align='center',
                         cellspacing=0)

    columns = table.colgroup()
    columns.col('edge').empty()
    columns.col('linenr').empty()
    columns.col('line').empty()
    columns.col('middle').empty()
    columns.col('middle').empty()
    columns.col('line').empty()
    columns.col('linenr').empty()
    columns.col('edge').empty()

    thead = table.thead()
    cell = thead.tr().td('h1', colspan=8)
    h1 = cell.h1()

    h1.a("root",
         href="showtree?sha1=%s&path=/%s%s" %
         (sha1, review_arg, repository_arg)).text("root")
    h1.span().text('/')

    components = path.split("/")
    for index, component in enumerate(components[:-1]):
        h1.a(href="showtree?sha1=%s&path=%s%s%s" %
             (sha1, "/".join(components[:index + 1]), review_arg,
              repository_arg)).text(component)
        h1.span().text('/')

    if first is not None:
        h1.a(href="showfile?sha1=%s&path=%s%s%s" %
             (sha1, "/".join(components), review_arg, repository_arg)).text(
                 components[-1])
    else:
        h1.text(components[-1])

    h1.span("right").a(href="/download/%s?repository=%s&sha1=%s" %
                       (path, repository.name, file_sha1)).text("[download]")

    table.tbody('spacer top').tr('spacer top').td(colspan=8).text()

    tbody = table.tbody("lines")

    yield document.render(stop=tbody, pretty=not compact)

    for linenr, line in enumerate(file.newLines(True)):
        linenr = linenr + 1
        highlight_class = ""

        if first is not None:
            if not (first_with_context <= linenr <= last_with_context):
                continue
            if linenr == first:
                highlight_class += " first-selected"
            if linenr == last:
                highlight_class += " last-selected"

        if tabify:
            line = htmlutils.tabify(line, tabwidth, indenttabsmode)

        line = line.replace("\r", "<i class='cr'></i>")

        row = tbody.tr("line context single",
                       id="f%do%dn%d" % (file.id, linenr, linenr))
        row.td("edge").text()
        row.td("linenr old").text(linenr)
        row.td("line single whole%s" % highlight_class,
               id="f%dn%d" % (file.id, linenr),
               colspan=4).innerHTML(line)
        row.td("linenr new").text(linenr)
        row.td("edge").text()

        if linenr % 500:
            yield document.render(stop=tbody, pretty=not compact)

    table.tbody('spacer bottom').tr('spacer bottom').td(colspan=8).text()

    yield document.render(pretty=not compact)