Example #1
0
def createCommentChain(db, user, review, chain_type, commit_id=None, origin=None, file_id=None, parent_id=None, child_id=None, old_sha1=None, new_sha1=None, offset=None, count=None):
    import reviewing.comment.propagate

    if chain_type == "issue" and review.state != "open":
        raise OperationFailure(code="reviewclosed",
                               title="Review is closed!",
                               message="You need to reopen the review before you can raise new issues.")

    cursor = db.cursor()

    if file_id is not None:
        if origin == "old":
            commit = gitutils.Commit.fromId(db, review.repository, parent_id)
        else:
            commit = gitutils.Commit.fromId(db, review.repository, child_id)

        propagation = reviewing.comment.propagate.Propagation(db)

        if not propagation.setCustom(review, commit, file_id, offset, offset + count - 1):
            raise OperationFailure(code="invalidoperation",
                                   title="Invalid operation",
                                   message="It's not possible to create a comment here.")

        propagation.calculateInitialLines()

        cursor.execute("""INSERT INTO commentchains (review, uid, type, origin, file, first_commit, last_commit)
                               VALUES (%s, %s, %s, %s, %s, %s, %s)
                            RETURNING id""",
                       (review.id, user.id, chain_type, origin, file_id, parent_id, child_id))

        chain_id = cursor.fetchone()[0]
        commentchainlines_values = []

        for sha1, (first_line, last_line) in propagation.new_lines.items():
            commentchainlines_values.append((chain_id, user.id, sha1, first_line, last_line))

        cursor.executemany("""INSERT INTO commentchainlines (chain, uid, sha1, first_line, last_line)
                                   VALUES (%s, %s, %s, %s, %s)""",
                           commentchainlines_values)
    elif commit_id is not None:
        commit = gitutils.Commit.fromId(db, review.repository, commit_id)

        cursor.execute("""INSERT INTO commentchains (review, uid, type, first_commit, last_commit)
                               VALUES (%s, %s, %s, %s, %s)
                            RETURNING id""",
                       (review.id, user.id, chain_type, commit_id, commit_id))
        chain_id = cursor.fetchone()[0]

        cursor.execute("""INSERT INTO commentchainlines (chain, uid, sha1, first_line, last_line)
                               VALUES (%s, %s, %s, %s, %s)""",
                       (chain_id, user.id, commit.sha1, offset, offset + count - 1))
    else:
        cursor.execute("""INSERT INTO commentchains (review, uid, type)
                               VALUES (%s, %s, %s)
                            RETURNING id""",
                       (review.id, user.id, chain_type))
        chain_id = cursor.fetchone()[0]

    commentchainusers = set([user.id] + map(int, review.owners))

    if file_id is not None:
        filters = Filters()
        filters.setFiles(db, review=review)
        filters.load(db, review=review)

        for user_id in filters.listUsers(file_id):
            commentchainusers.add(user_id)

    cursor.executemany("INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)", [(chain_id, user_id) for user_id in commentchainusers])

    return chain_id
Example #2
0
def createCommentChain(db, user, review, chain_type, commit_id=None, origin=None, file_id=None, parent_id=None, child_id=None, old_sha1=None, new_sha1=None, offset=None, count=None):
    if chain_type == "issue" and review.state != "open":
        raise OperationFailure(code="reviewclosed",
                               title="Review is closed!",
                               message="You need to reopen the review before you can raise new issues.")

    cursor = db.cursor()

    if file_id is not None and (parent_id == child_id or parent_id is None):
        cursor.execute("""SELECT 1
                            FROM reviewchangesets
                            JOIN fileversions USING (changeset)
                           WHERE reviewchangesets.review=%s
                             AND fileversions.file=%s
                             AND fileversions.old_sha1!='0000000000000000000000000000000000000000'
                             AND fileversions.new_sha1!='0000000000000000000000000000000000000000'""",
                       (review.id, file_id))

        if cursor.fetchone():
            cursor.execute("""SELECT parent, child
                                FROM changesets
                                JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id)
                                JOIN fileversions ON (fileversions.changeset=changesets.id)
                               WHERE fileversions.file=%s
                                 AND fileversions.new_sha1=%s""",
                           (file_id, new_sha1))

            rows = cursor.fetchall()

            if not rows:
                cursor.execute("""SELECT parent, child
                                    FROM changesets
                                    JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id)
                                    JOIN fileversions ON (fileversions.changeset=changesets.id)
                                   WHERE fileversions.file=%s
                                     AND fileversions.old_sha1=%s""",
                               (file_id, new_sha1))

                rows = cursor.fetchall()

            parent = child = None

            for row_parent_id, row_child_id in rows:
                if row_child_id == child_id:
                    parent = gitutils.Commit.fromId(db, review.repository, row_parent_id)
                    child = gitutils.Commit.fromId(db, review.repository, row_child_id)
                    break
                elif row_parent_id == child_id and parent is None:
                    parent = gitutils.Commit.fromId(db, review.repository, row_parent_id)
                    child = gitutils.Commit.fromId(db, review.repository, row_child_id)

            if parent and child:
                url = "/%s/%s..%s?review=%d&file=%d" % (review.repository.name, parent.sha1[:8], child.sha1[:8], review.id, file_id)
                link = ("<p>The link below goes to a diff that can be use to create the comment:</p>" +
                        "<p style='padding-left: 2em'><a href='%s'>%s%s</a></p>") % (url, dbutils.getURLPrefix(db), url)
            else:
                link = ""

            raise OperationFailure(code="notsupported",
                                   title="File changed in review",
                                   message=("<p>Due to limitations in the code used to create comments, " +
                                            "it's only possible to create comments via a diff view if " +
                                            "the commented file has been changed in the review.</p>" +
                                            link),
                                   is_html=True)

        cursor.execute("""INSERT INTO commentchains (review, uid, type, file, first_commit, last_commit)
                               VALUES (%s, %s, %s, %s, %s, %s)
                            RETURNING id""",
                       (review.id, user.id, chain_type, file_id, child_id, child_id))
        chain_id = cursor.fetchone()[0]

        cursor.execute("""INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line)
                               VALUES (%s, %s, %s, %s, %s, %s)""",
                       (chain_id, user.id, child_id, new_sha1, offset, offset + count - 1))
    elif file_id is not None:
        parents_returned = set()

        def getFileParent(new_sha1):
            cursor.execute("""SELECT changesets.id, fileversions.old_sha1
                                FROM changesets, reviewchangesets, fileversions
                               WHERE reviewchangesets.review=%s
                                 AND reviewchangesets.changeset=changesets.id
                                 AND fileversions.changeset=changesets.id
                                 AND fileversions.file=%s
                                 AND fileversions.new_sha1=%s""",
                           [review.id, file_id, new_sha1])
            try:
                changeset_id, old_sha1 = cursor.fetchone()
                if old_sha1 in parents_returned: return None, None
                parents_returned.add(old_sha1)
                return changeset_id, old_sha1
            except:
                return None, None

        children_returned = set()

        def getFileChild(old_sha1):
            cursor.execute("""SELECT changesets.id, fileversions.new_sha1
                                FROM changesets, reviewchangesets, fileversions
                               WHERE reviewchangesets.review=%s
                                 AND reviewchangesets.changeset=changesets.id
                                 AND fileversions.changeset=changesets.id
                                 AND fileversions.file=%s
                                 AND fileversions.old_sha1=%s""",
                           [review.id, file_id, old_sha1])
            try:
                changeset_id, new_sha1 = cursor.fetchone()
                if new_sha1 in children_returned: return None, None
                children_returned.add(new_sha1)
                return changeset_id, new_sha1
            except:
                return None, None

        cursor.execute("""SELECT changesets.id
                            FROM changesets, reviewchangesets, fileversions
                           WHERE reviewchangesets.review=%s
                             AND reviewchangesets.changeset=changesets.id
                             AND changesets.child=%s
                             AND fileversions.changeset=changesets.id
                             AND fileversions.file=%s
                             AND fileversions.old_sha1=%s
                             AND fileversions.new_sha1=%s""",
                       [review.id, child_id, file_id, old_sha1, new_sha1])

        row = cursor.fetchone()

        if not row:
            if origin == "old":
                cursor.execute("""SELECT changesets.id
                                    FROM changesets, reviewchangesets, fileversions
                                   WHERE reviewchangesets.review=%s
                                     AND reviewchangesets.changeset=changesets.id
                                     AND fileversions.changeset=changesets.id
                                     AND fileversions.file=%s
                                     AND fileversions.old_sha1=%s""",
                               [review.id, file_id, old_sha1])
            else:
                cursor.execute("""SELECT changesets.id
                                    FROM changesets, reviewchangesets, fileversions
                                   WHERE reviewchangesets.review=%s
                                     AND reviewchangesets.changeset=changesets.id
                                     AND fileversions.changeset=changesets.id
                                     AND fileversions.file=%s
                                     AND fileversions.new_sha1=%s""",
                               [review.id, file_id, new_sha1])

            row = cursor.fetchone()

        primary_changeset_id = row[0]

        sha1s_older = { }
        sha1s_newer = { old_sha1: (primary_changeset_id, new_sha1) }

        sha1 = new_sha1
        while True:
            changeset_id, next_sha1 = getFileParent(sha1)
            if changeset_id:
                sha1s_older[sha1] = changeset_id, next_sha1
                sha1s_newer[next_sha1] = changeset_id, sha1
                sha1 = next_sha1
            else:
                break

        sha1 = new_sha1
        while True:
            changeset_id, next_sha1 = getFileChild(sha1)
            if changeset_id:
                sha1s_newer[sha1] = changeset_id, next_sha1
                sha1 = next_sha1
            else:
                break

        commentchainlines_values = []
        processed = set()

        def searchOrigin(changeset_id, sha1, search_space, first_line, last_line):
            try:
                while sha1 not in processed:
                    processed.add(sha1)
                    changeset_id, next_sha1 = search_space[sha1]
                    changeset = changeset_load.loadChangeset(db, review.repository, changeset_id, filtered_file_ids=set([file_id]))
                    if len(changeset.child.parents) > 1: break
                    verdict, next_first_line, next_last_line = updateCommentChain(first_line, last_line, changeset.files[0].chunks, forward)
                    if verdict == "modified": break
                    sha1 = next_sha1
                    first_line = next_first_line
                    last_line = next_last_line
            except:
                pass
            return changeset_id, sha1, first_line, last_line

        first_line = offset
        last_line = offset + count - 1

        if origin == 'old':
            changeset_id, sha1, first_line, last_line = searchOrigin(primary_changeset_id, old_sha1, sha1s_older, first_line, last_line)
            commit_id = diff.Changeset.fromId(db, review.repository, changeset_id).parent.id
        else:
            changeset_id, sha1, first_line, last_line = searchOrigin(primary_changeset_id, new_sha1, sha1s_older, first_line, last_line)
            commit_id = diff.Changeset.fromId(db, review.repository, changeset_id).child.id

        commentchainlines_values.append((user.id, commit_id, sha1, first_line, last_line))
        processed = set()
        processed.add(sha1)

        while sha1 in sha1s_newer:
            changeset_id, sha1 = sha1s_newer[sha1]

            if sha1 in processed: break
            else: processed.add(sha1)

            changeset = changeset_load.loadChangeset(db, review.repository, changeset_id, filtered_file_ids=set([file_id]))

            if len(changeset.child.parents) != 1:
                chunks = diff.parse.parseDifferences(review.repository, from_commit=changeset.parent, to_commit=changeset.child, selected_path=dbutils.describe_file(db, file_id)).chunks
            else:
                chunks = changeset.files[0].chunks

            verdict, first_line, last_line = updateCommentChain(first_line, last_line, chunks)

            if verdict == "transfer":
                commentchainlines_values.append((user.id, changeset.child.getId(db), sha1, first_line, last_line))
            else:
                break

        cursor.execute("INSERT INTO commentchains (review, uid, type, origin, file, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING id", [review.id, user.id, chain_type, origin, file_id, parent_id, child_id])
        chain_id = cursor.fetchone()[0]

        try: cursor.executemany("INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)", [(chain_id,) + values for values in commentchainlines_values])
        except: raise Exception, repr(commentchainlines_values)
    elif commit_id is not None:
        commit = gitutils.Commit.fromId(db, review.repository, commit_id)

        cursor.execute("INSERT INTO commentchains (review, uid, type, first_commit, last_commit) VALUES (%s, %s, %s, %s, %s) RETURNING id", [review.id, user.id, chain_type, commit_id, commit_id])
        chain_id = cursor.fetchone()[0]

        cursor.execute("INSERT INTO commentchainlines (chain, uid, commit, sha1, first_line, last_line) VALUES (%s, %s, %s, %s, %s, %s)", (chain_id, user.id, commit_id, commit.sha1, offset, offset + count - 1))
    else:
        cursor.execute("INSERT INTO commentchains (review, uid, type) VALUES (%s, %s, %s) RETURNING id", [review.id, user.id, chain_type])
        chain_id = cursor.fetchone()[0]

    commentchainusers = set([user.id] + map(int, review.owners))

    if file_id is not None:
        filters = Filters()
        filters.load(db, review=review)

        for user_id in filters.listUsers(db, file_id):
            commentchainusers.add(user_id)

    cursor.executemany("INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)", [(chain_id, user_id) for user_id in commentchainusers])

    return chain_id