Example #1
0
def getReviewersAndWatchers(db, repository, commits=None, changesets=None, reviewfilters=None,
                            applyfilters=True, applyparentfilters=False):
    """getReviewersAndWatchers(db, commits=None, changesets=None) -> tuple

Returns a tuple containing two dictionaries, each mapping file IDs to
dictionaries mapping user IDs to sets of changeset IDs.  The first dictionary
defines the reviwers of each file, the second dictionary defines the watchers of
each file.  For any changes in a file for which no reviewer is identified, None
is used as a key in the dictionary instead of a real user ID."""

    if changesets is None:
        changesets = []
        changeset_utils.createChangesets(db, repository, commits)
        for commit in commits:
            changesets.extend(changeset_utils.createChangeset(db, None, repository, commit, do_highlight=False))

    cursor = db.cursor()

    filters = Filters()
    filters.setFiles(db, list(getFileIdsFromChangesets(changesets)))

    if applyfilters:
        filters.load(db, repository=repository, recursive=applyparentfilters)

    if reviewfilters:
        filters.addFilters(reviewfilters)

    reviewers = {}
    watchers = {}

    for changeset in changesets:
        author_user_ids = changeset.child.author.getUserIds(db) if changeset.child else set()

        cursor.execute("SELECT DISTINCT file FROM fileversions WHERE changeset=%s", (changeset.id,))

        for (file_id,) in cursor:
            reviewers_found = False

            for user_id, (filter_type, delegate) in filters.listUsers(file_id).items():
                if filter_type == 'reviewer':
                    if user_id not in author_user_ids:
                        reviewer_user_ids = [user_id]
                    elif delegate:
                        reviewer_user_ids = []
                        for delegate_user_name in delegate.split(","):
                            delegate_user = dbutils.User.fromName(db, delegate_user_name)
                            reviewer_user_ids.append(delegate_user.id)
                    else:
                        reviewer_user_ids = []

                    for reviewer_user_id in reviewer_user_ids:
                        reviewers.setdefault(file_id, {}).setdefault(reviewer_user_id, set()).add(changeset.id)
                        reviewers_found = True
                else:
                    watchers.setdefault(file_id, {}).setdefault(user_id, set()).add(changeset.id)

            if not reviewers_found:
                reviewers.setdefault(file_id, {}).setdefault(None, set()).add(changeset.id)

    return reviewers, watchers
Example #2
0
def getReviewersAndWatchers(db, repository, commits=None, changesets=None, reviewfilters=None,
                            applyfilters=True, applyparentfilters=False):
    """getReviewersAndWatchers(db, commits=None, changesets=None) -> tuple

Returns a tuple containing two dictionaries, each mapping file IDs to
dictionaries mapping user IDs to sets of changeset IDs.  The first dictionary
defines the reviwers of each file, the second dictionary defines the watchers of
each file.  For any changes in a file for which no reviewer is identified, None
is used as a key in the dictionary instead of a real user ID."""

    if changesets is None:
        changesets = []
        changeset_utils.createChangesets(db, repository, commits)
        for commit in commits:
            changesets.extend(changeset_utils.createChangeset(db, None, repository, commit, do_highlight=False))

    cursor = db.cursor()

    filters = Filters()
    filters.setFiles(db, list(getFileIdsFromChangesets(changesets)))

    if applyfilters:
        filters.load(db, repository=repository, recursive=applyparentfilters)

    if reviewfilters:
        filters.addFilters(reviewfilters)

    reviewers = {}
    watchers = {}

    for changeset in changesets:
        author_user_ids = changeset.child.author.getUserIds(db) if changeset.child else set()

        cursor.execute("SELECT DISTINCT file FROM fileversions WHERE changeset=%s", (changeset.id,))

        for (file_id,) in cursor:
            reviewers_found = False

            for user_id, (filter_type, delegate) in filters.listUsers(file_id).items():
                if filter_type == 'reviewer':
                    if user_id not in author_user_ids:
                        reviewer_user_ids = [user_id]
                    elif delegate:
                        reviewer_user_ids = []
                        for delegate_user_name in delegate.split(","):
                            delegate_user = dbutils.User.fromName(db, delegate_user_name)
                            reviewer_user_ids.append(delegate_user.id)
                    else:
                        reviewer_user_ids = []

                    for reviewer_user_id in reviewer_user_ids:
                        reviewers.setdefault(file_id, {}).setdefault(reviewer_user_id, set()).add(changeset.id)
                        reviewers_found = True
                else:
                    watchers.setdefault(file_id, {}).setdefault(user_id, set()).add(changeset.id)

            if not reviewers_found:
                reviewers.setdefault(file_id, {}).setdefault(None, set()).add(changeset.id)

    return reviewers, watchers
Example #3
0
def renderShowFilters(req, db, user):
    path = req.getParameter("path", "/")
    repo_name = req.getParameter("repository",
                                 user.getPreference(db, "defaultRepository"))
    repository = gitutils.Repository.fromParameter(db, repo_name)

    show_path = path

    if path.endswith("/") or repository.getHead(db).isDirectory(path):
        path = path.rstrip("/") + "/dummy.txt"

    file_id = dbutils.find_file(db, path=path)

    filters = reviewing.filters.Filters()
    filters.setFiles(db, [file_id])
    filters.load(db, repository=repository, recursive=True)

    reviewers = []
    watchers = []

    for user_id, (filter_type,
                  _delegate) in filters.listUsers(file_id).items():
        if filter_type == 'reviewer': reviewers.append(user_id)
        else: watchers.append(user_id)

    result = "Path: %s\n" % show_path

    reviewers_found = False
    watchers_found = False

    for reviewer_id in sorted(reviewers):
        if not reviewers_found:
            result += "\nReviewers:\n"
            reviewers_found = True

        reviewer = dbutils.User.fromId(db, reviewer_id)
        result += "  %s <%s>\n" % (reviewer.fullname, reviewer.email)

    for watcher_id in sorted(watchers):
        if not watchers_found:
            result += "\nWatchers:\n"
            watchers_found = True

        watcher = dbutils.User.fromId(db, watcher_id)
        result += "  %s <%s>\n" % (watcher.fullname, watcher.email)

    if not reviewers_found and not watchers_found:
        result += "\nNo matching filters found.\n"

    return page.utils.ResponseBody(result, content_type="text/plain")
Example #4
0
def renderShowFilters(req, db, user):
    path = req.getParameter("path", "/")
    repo_name = req.getParameter(
        "repository", user.getPreference(db, "defaultRepository"))
    repository = gitutils.Repository.fromParameter(db, repo_name)

    show_path = path

    if path.endswith("/") or repository.getHead(db).isDirectory(path):
        path = path.rstrip("/") + "/dummy.txt"

    file_id = dbutils.find_file(db, path=path)

    filters = reviewing.filters.Filters()
    filters.setFiles(db, [file_id])
    filters.load(db, repository=repository, recursive=True)

    reviewers = []
    watchers = []

    for user_id, (filter_type, _delegate) in filters.listUsers(file_id).items():
        if filter_type == 'reviewer': reviewers.append(user_id)
        else: watchers.append(user_id)

    result = "Path: %s\n" % show_path

    reviewers_found = False
    watchers_found = False

    for reviewer_id in sorted(reviewers):
        if not reviewers_found:
            result += "\nReviewers:\n"
            reviewers_found = True

        reviewer = dbutils.User.fromId(db, reviewer_id)
        result += "  %s <%s>\n" % (reviewer.fullname, reviewer.email)

    for watcher_id in sorted(watchers):
        if not watchers_found:
            result += "\nWatchers:\n"
            watchers_found = True

        watcher = dbutils.User.fromId(db, watcher_id)
        result += "  %s <%s>\n" % (watcher.fullname, watcher.email)

    if not reviewers_found and not watchers_found:
        result += "\nNo matching filters found.\n"

    return page.utils.ResponseBody(result, content_type="text/plain")
Example #5
0
                data = json_decode(sys.stdin.readline())
                batch_id = data["batch_id"]
                was_accepted = data["was_accepted"]
                is_accepted = data["is_accepted"]
                pending_mails = reviewing.utils.generateMailsForBatch(db, batch_id, was_accepted, is_accepted)
            elif command == "generate-mails-for-assignments-transaction":
                data = json_decode(sys.stdin.readline())
                transaction_id = data["transaction_id"]
                pending_mails = reviewing.utils.generateMailsForAssignmentsTransaction(db, transaction_id)
            elif command == "apply-filters":
                data = json_decode(sys.stdin.readline())
                filters = reviewing.filters.Filters()
                user = dbutils.User.fromId(db, data["user_id"]) if "user_id" in data else None
                if "review_id" in data:
                    review = dbutils.Review.fromId(db, data["review_id"], load_commits=False)
                    filters.setFiles(db, review=review)
                    filters.load(db, review=review, user=user,
                                 added_review_filters=data.get("added_review_filters", []),
                                 removed_review_filters=data.get("removed_review_filters", []))
                else:
                    repository = gitutils.Repository.fromId(db, data["repository_id"])
                    filters.setFiles(db, file_ids=data["file_ids"])
                    filters.load(db, repository=repository, recursive=data.get("recursive", False), user=user)
                sys.stdout.write(json_encode(filters.data) + "\n")
            else:
                print "unknown command: %s" % command
                sys.exit(1)

            if pending_mails is not None:
                sys.stdout.write(json_encode(pending_mails) + "\n")
Example #6
0
def main():
    parser = argparse.ArgumentParser()

    parser.add_argument("-u", dest="user_id", type=int)
    parser.add_argument("-l", dest="auth_labels", action="append", default=[])
    parser.add_argument("command", nargs="*")

    arguments = parser.parse_args()

    try:
        init(arguments.user_id, arguments.auth_labels)

        for command in arguments.command:
            pending_mails = None

            if command == "generate-mails-for-batch":
                data = json_decode(sys.stdin.readline())
                batch_id = data["batch_id"]
                was_accepted = data["was_accepted"]
                is_accepted = data["is_accepted"]
                pending_mails = reviewing.utils.generateMailsForBatch(db, batch_id, was_accepted, is_accepted)
            elif command == "generate-mails-for-assignments-transaction":
                data = json_decode(sys.stdin.readline())
                transaction_id = data["transaction_id"]
                pending_mails = reviewing.utils.generateMailsForAssignmentsTransaction(db, transaction_id)
            elif command == "apply-filters":
                data = json_decode(sys.stdin.readline())
                filters = reviewing.filters.Filters()
                user = dbutils.User.fromId(db, data["user_id"]) if "user_id" in data else None
                if "review_id" in data:
                    review = dbutils.Review.fromId(db, data["review_id"])
                    filters.setFiles(db, review=review)
                    filters.load(db, review=review, user=user,
                                 added_review_filters=data.get("added_review_filters", []),
                                 removed_review_filters=data.get("removed_review_filters", []))
                else:
                    repository = gitutils.Repository.fromId(db, data["repository_id"])
                    filters.setFiles(db, file_ids=data["file_ids"])
                    filters.load(db, repository=repository, recursive=data.get("recursive", False), user=user)
                sys.stdout.write(json_encode(filters.data) + "\n")
            elif command == "generate-custom-mails":
                pending_mails = []
                for data in json_decode(sys.stdin.readline()):
                    from_user = dbutils.User.fromId(db, data["sender"])
                    if data.get("recipients"):
                        recipients = [dbutils.User.fromId(db, user_id)
                                      for user_id in data["recipients"]]
                    else:
                        recipients = None
                    subject = data["subject"]
                    headers = data.get("headers")
                    body = data["body"]
                    if "review_id" in data:
                        review = dbutils.Review.fromId(db, data["review_id"])
                    else:
                        review = None
                    pending_mails.extend(sendCustomMail(
                        from_user, recipients, subject, headers, body, review))
            elif command == "set-review-state":
                data = json_decode(sys.stdin.readline())
                error = ""
                try:
                    user = dbutils.User.fromId(db, data["user_id"])
                    review = dbutils.Review.fromId(db, data["review_id"])
                    if review.state != data["old_state"]:
                        error = "invalid old state"
                    elif data["new_state"] == "open":
                        review.reopen(db, user)
                    elif data["new_state"] == "closed":
                        review.close(db, user)
                    elif data["new_state"] == "dropped":
                        review.drop(db, user)
                    else:
                        error = "invalid new state"
                except dbutils.NoSuchUser:
                    error = "invalid user id"
                except dbutils.NoSuchReview:
                    error = "invalid review id"
                except Exception as error:
                    error = str(error)
                sys.stdout.write(error + "\n")
            elif command in HANDLERS:
                data_in = json_decode(sys.stdin.readline())
                data_out = HANDLERS[command](data_in)
                sys.stdout.write(json_encode(data_out) + "\n")
            else:
                sys.stdout.write(json_encode("unknown command: %s" % command) + "\n")
                sys.exit(0)

            if pending_mails is not None:
                sys.stdout.write(json_encode(pending_mails) + "\n")

        finish()
    except Exception:
        sys.stdout.write(json_encode(traceback.format_exc()) + "\n")
    finally:
        abort()
Example #7
0
    def process(self, db, user, repository_id=None, filter_id=None):
        if user.isAnonymous():
            return OperationFailureMustLogin()

        cursor = db.cursor()

        if filter_id is not None:
            cursor.execute(
                """SELECT repository, path, type, delegate
                                FROM filters
                               WHERE id=%s""",
                (filter_id,),
            )
            repository_id, filter_path, filter_type, filter_delegate = cursor.fetchone()

        if repository_id is None:
            cursor.execute(
                """SELECT reviews.id, applyfilters, applyparentfilters, branches.repository
                                FROM reviews
                                JOIN branches ON (reviews.branch=branches.id)
                               WHERE reviews.state!='closed'"""
            )
        else:
            cursor.execute(
                """SELECT reviews.id, applyfilters, applyparentfilters, branches.repository
                                FROM reviews
                                JOIN branches ON (reviews.branch=branches.id)
                               WHERE reviews.state!='closed'
                                 AND branches.repository=%s""",
                (repository_id,),
            )

        repositories = {}

        # list(review_file_id)
        assign_changes = []

        # set(review_id)
        assigned_reviews = set()

        # set(review_id)
        watched_reviews = set()

        for review_id, applyfilters, applyparentfilters, repository_id in cursor.fetchall():
            if repository_id in repositories:
                repository = repositories[repository_id]
            else:
                repository = gitutils.Repository.fromId(db, repository_id)
                repositories[repository_id] = repository

            review = reviewing.filters.Filters.Review(review_id, applyfilters, applyparentfilters, repository)
            filters = reviewing.filters.Filters()

            filters.setFiles(db, review=review)

            if filter_id is not None:
                filters.addFilter(user.id, filter_path, filter_type, filter_delegate)
            else:
                filters.load(db, review=review, user=user)

            cursor.execute(
                """SELECT commits.id, usergitemails.uid, reviewfiles.file, reviewfiles.id
                                FROM commits
                                JOIN gitusers ON (gitusers.id=commits.author_gituser)
                     LEFT OUTER JOIN usergitemails ON (usergitemails.email=gitusers.email)
                                JOIN changesets ON (changesets.child=commits.id)
                                JOIN reviewfiles ON (reviewfiles.changeset=changesets.id)
                     LEFT OUTER JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id
                                                     AND reviewuserfiles.uid=%s)
                               WHERE reviewfiles.review=%s
                                 AND reviewuserfiles.uid IS NULL""",
                (user.id, review_id),
            )

            for commit_id, author_id, file_id, review_file_id in cursor.fetchall():
                if author_id != user.id:
                    association = filters.getUserFileAssociation(user.id, file_id)

                    if association == "reviewer":
                        assign_changes.append(review_file_id)
                        assigned_reviews.add(review_id)
                    elif association == "watcher":
                        watched_reviews.add(review_id)

        cursor.execute(
            """SELECT reviews.id
                            FROM reviews
                 LEFT OUTER JOIN reviewusers ON (reviewusers.review=reviews.id
                                             AND reviewusers.uid=%s)
                           WHERE reviews.id=ANY (%s)
                             AND reviewusers.uid IS NULL""",
            (user.id, list(assigned_reviews) + list(watched_reviews)),
        )

        new_reviews = set(review_id for (review_id,) in cursor)

        cursor.executemany(
            """INSERT INTO reviewusers (review, uid)
                                   VALUES (%s, %s)""",
            [(review_id, user.id) for review_id in new_reviews],
        )

        cursor.executemany(
            """INSERT INTO reviewuserfiles (file, uid)
                                   VALUES (%s, %s)""",
            [(review_file_id, user.id) for review_file_id in assign_changes],
        )

        db.commit()

        watched_reviews &= new_reviews
        watched_reviews -= assigned_reviews

        cursor.execute(
            """SELECT id, summary
                            FROM reviews
                           WHERE id=ANY (%s)""",
            (list(assigned_reviews | watched_reviews),),
        )

        return OperationResult(
            assigned_reviews=sorted(assigned_reviews), watched_reviews=sorted(watched_reviews), summaries=dict(cursor)
        )
Example #8
0
def addReviewFilters(db, creator, user, review, reviewer_paths, watcher_paths):
    cursor = db.cursor()

    cursor.execute("INSERT INTO reviewassignmentstransactions (review, assigner) VALUES (%s, %s) RETURNING id", (review.id, creator.id))
    transaction_id = cursor.fetchone()[0]

    def add(filter_type, paths):
        for path in paths:
            cursor.execute("""SELECT id, type
                                FROM reviewfilters
                               WHERE review=%s
                                 AND uid=%s
                                 AND path=%s""",
                           (review.id, user.id, path))

            row = cursor.fetchone()

            if row:
                old_filter_id, old_filter_type = row

                if old_filter_type == filter_type:
                    continue
                else:
                    cursor.execute("""DELETE FROM reviewfilters
                                            WHERE id=%s""",
                                   (old_filter_id,))
                    cursor.execute("""INSERT INTO reviewfilterchanges (transaction, uid, path, type, created)
                                           VALUES (%s, %s, %s, %s, false)""",
                                   (transaction_id, user.id, path, old_filter_type))

            cursor.execute("""INSERT INTO reviewfilters (review, uid, path, type, creator)
                                   VALUES (%s, %s, %s, %s, %s)""",
                           (review.id, user.id, path, filter_type, creator.id))
            cursor.execute("""INSERT INTO reviewfilterchanges (transaction, uid, path, type, created)
                                   VALUES (%s, %s, %s, %s, true)""",
                           (transaction_id, user.id, path, filter_type))

    add("reviewer", reviewer_paths)
    add("watcher", watcher_paths)

    filters = Filters()
    filters.setFiles(db, review=review)
    filters.load(db, review=review, user=user)

    if user not in review.reviewers and user not in review.watchers and user not in review.owners:
        cursor.execute("""INSERT INTO reviewusers (review, uid, type)
                          VALUES (%s, %s, 'manual')""",
                       (review.id, user.id,))

    delete_files = set()
    insert_files = set()

    if watcher_paths:
        # Unassign changes currently assigned to the affected user.
        cursor.execute("""SELECT reviewfiles.id, reviewfiles.file
                            FROM reviewfiles
                            JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id)
                           WHERE reviewfiles.review=%s
                             AND reviewuserfiles.uid=%s""",
                       (review.id, user.id))

        for review_file_id, file_id in cursor:
            if not filters.isReviewer(user.id, file_id):
                delete_files.add(review_file_id)

    if reviewer_paths:
        # Assign changes currently not assigned to the affected user.
        cursor.execute("""SELECT reviewfiles.id, reviewfiles.file
                            FROM reviewfiles
                            JOIN changesets ON (changesets.id=reviewfiles.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                            JOIN gitusers ON (gitusers.id=commits.author_gituser)
                 LEFT OUTER JOIN usergitemails USING (email)
                 LEFT OUTER JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id AND reviewuserfiles.uid=%s)
                           WHERE reviewfiles.review=%s
                             AND (usergitemails.uid IS NULL OR usergitemails.uid!=%s)
                             AND reviewuserfiles.uid IS NULL""",
                       (user.id, review.id, user.id))

        for review_file_id, file_id in cursor:
            if filters.isReviewer(user.id, file_id):
                insert_files.add(review_file_id)

    if delete_files:
        cursor.executemany("DELETE FROM reviewuserfiles WHERE file=%s AND uid=%s",
                           izip(delete_files, repeat(user.id)))
        cursor.executemany("INSERT INTO reviewassignmentchanges (transaction, file, uid, assigned) VALUES (%s, %s, %s, false)",
                           izip(repeat(transaction_id), delete_files, repeat(user.id)))

    if insert_files:
        cursor.executemany("INSERT INTO reviewuserfiles (file, uid) VALUES (%s, %s)",
                           izip(insert_files, repeat(user.id)))
        cursor.executemany("INSERT INTO reviewassignmentchanges (transaction, file, uid, assigned) VALUES (%s, %s, %s, true)",
                           izip(repeat(transaction_id), insert_files, repeat(user.id)))

    return generateMailsForAssignmentsTransaction(db, transaction_id)
Example #9
0
def createReview(db, user, repository, commits, branch_name, summary, description, from_branch_name=None, via_push=False, reviewfilters=None, applyfilters=True, applyparentfilters=False, recipientfilters=None):
    cursor = db.cursor()

    if via_push:
        applyparentfilters = bool(user.getPreference(db, 'review.applyUpstreamFilters'))

    branch = dbutils.Branch.fromName(db, repository, branch_name)

    if branch is not None:
        raise OperationFailure(
            code="branchexists",
            title="Invalid review branch name",
            message="""\
<p>There is already a branch named <code>%s</code> in the repository.  You have
to select a different name.</p>

<p>If you believe the existing branch was created during an earlier (failed)
attempt to create this review, you can try to delete it from the repository
using the command<p>

<pre>  git push &lt;remote&gt; :%s</pre>

<p>and then press the "Submit Review" button on this page again."""
            % (htmlutils.htmlify(branch_name), htmlutils.htmlify(branch_name)),
            is_html=True)

    if not commits:
        raise OperationFailure(
            code="nocommits",
            title="No commits specified",
            message="You need at least one commit to create a review.")

    commitset = log_commitset.CommitSet(commits)
    heads = commitset.getHeads()

    if len(heads) != 1:
        # There is really no plausible way for this error to occur.
        raise OperationFailure(
            code="disconnectedtree",
            title="Disconnected tree",
            message=("The specified commits do do not form a single connected "
                     "tree.  Creating a review of them is not supported."))

    head = heads.pop()

    if len(commitset.getTails()) != 1:
        tail_id = None
    else:
        tail_id = gitutils.Commit.fromSHA1(db, repository, commitset.getTails().pop()).getId(db)

    if not via_push:
        try:
            repository.createBranch(branch_name, head.sha1)
        except gitutils.GitCommandError as error:
            raise OperationFailure(
                code="branchfailed",
                title="Failed to create review branch",
                message=("<p><b>Output from git:</b></p>"
                         "<code style='padding-left: 1em'>%s</code>"
                         % htmlutils.htmlify(error.output)),
                is_html=True)

    try:
        cursor.execute("INSERT INTO branches (repository, name, head, tail, type) VALUES (%s, %s, %s, %s, 'review') RETURNING id", [repository.id, branch_name, head.getId(db), tail_id])

        branch_id = cursor.fetchone()[0]
        reachable_values = [(branch_id, commit.getId(db)) for commit in commits]

        cursor.executemany("INSERT INTO reachable (branch, commit) VALUES (%s, %s)", reachable_values)

        cursor.execute("INSERT INTO reviews (type, branch, state, summary, description, applyfilters, applyparentfilters) VALUES ('official', %s, 'open', %s, %s, %s, %s) RETURNING id", (branch_id, summary, description, applyfilters, applyparentfilters))

        review = dbutils.Review.fromId(db, cursor.fetchone()[0])

        cursor.execute("INSERT INTO reviewusers (review, uid, owner) VALUES (%s, %s, TRUE)", (review.id, user.id))

        if reviewfilters is not None:
            cursor.executemany("""INSERT INTO reviewfilters (review, uid, path, type, creator)
                                       VALUES (%s, %s, %s, %s, %s)""",
                               [(review.id, filter_user_id, filter_path, filter_type, user.id)
                                for filter_user_id, filter_path, filter_type, filter_delegate in reviewfilters])

        is_opt_in = False

        if recipientfilters is not None:
            cursor.executemany("INSERT INTO reviewrecipientfilters (review, uid, include) VALUES (%s, %s, %s)",
                               [(review.id, filter_user_id, filter_include)
                                for filter_user_id, filter_include in recipientfilters])

            for filter_user_id, filter_include in recipientfilters:
                if filter_user_id is None and not filter_include:
                    is_opt_in = True

        addCommitsToReview(db, user, review, commits, new_review=True)

        if from_branch_name is not None:
            cursor.execute("UPDATE branches SET review=%s WHERE repository=%s AND name=%s", (review.id, repository.id, from_branch_name))

        # Reload to get list of changesets added by addCommitsToReview().
        review = dbutils.Review.fromId(db, review.id)

        pending_mails = []
        recipients = review.getRecipients(db)
        for to_user in recipients:
            pending_mails.extend(mail.sendReviewCreated(db, user, to_user, recipients, review))

        if not is_opt_in:
            recipient_by_id = dict((to_user.id, to_user) for to_user in recipients)

            cursor.execute("""SELECT userpreferences.uid, userpreferences.repository,
                                     userpreferences.filter, userpreferences.integer
                                FROM userpreferences
                     LEFT OUTER JOIN filters ON (filters.id=userpreferences.filter)
                               WHERE userpreferences.item='review.defaultOptOut'
                                 AND userpreferences.uid=ANY (%s)
                                 AND (userpreferences.filter IS NULL
                                   OR filters.repository=%s)
                                 AND (userpreferences.repository IS NULL
                                   OR userpreferences.repository=%s)""",
                           (recipient_by_id.keys(), repository.id, repository.id))

            user_settings = {}
            has_filter_settings = False

            for user_id, repository_id, filter_id, integer in cursor:
                settings = user_settings.setdefault(user_id, [None, None, {}])
                value = bool(integer)

                if repository_id is None and filter_id is None:
                    settings[0] = value
                elif repository_id is not None:
                    settings[1] = value
                else:
                    settings[2][filter_id] = value
                    has_filter_settings = True

            if has_filter_settings:
                filters = Filters()
                filters.setFiles(db, review=review)

            for user_id, (global_default, repository_default, filter_settings) in user_settings.items():
                to_user = recipient_by_id[user_id]
                opt_out = None

                if repository_default is not None:
                    opt_out = repository_default
                elif global_default is not None:
                    opt_out = global_default

                if filter_settings:
                    # Policy:
                    #
                    # If all of the user's filters that matched files in the
                    # review have review.defaultOptOut enabled, then opt out.
                    # When determining this, any review filters of the user's
                    # that match files in the review count as filters that don't
                    # have the review.defaultOptOut enabled.
                    #
                    # If any of the user's filters that matched files in the
                    # review have review.defaultOptOut disabled, then don't opt
                    # out.  When determining this, review filters are ignored.
                    #
                    # Otherwise, ignore the filter settings, and go with either
                    # the user's per-repository or global setting (as set
                    # above.)

                    filters.load(db, review=review, user=to_user)

                    # A set of filter ids.  If None is in the set, the user has
                    # one or more review filters in the review.  (These do not
                    # have ids.)
                    active_filters = filters.getActiveFilters(to_user)

                    for filter_id in active_filters:
                        if filter_id is None:
                            continue
                        elif filter_id in filter_settings:
                            if not filter_settings[filter_id]:
                                opt_out = False
                                break
                        else:
                            break
                    else:
                        if None not in active_filters:
                            opt_out = True

                if opt_out:
                    cursor.execute("""INSERT INTO reviewrecipientfilters (review, uid, include)
                                           VALUES (%s, %s, FALSE)""",
                                   (review.id, to_user.id))

        db.commit()

        mail.sendPendingMails(pending_mails)

        return review
    except:
        if not via_push:
            repository.run("branch", "-D", branch_name)
        raise
Example #10
0
def addReviewFilters(db, creator, user, review, reviewer_paths, watcher_paths):
    cursor = db.cursor()

    cursor.execute(
        "INSERT INTO reviewassignmentstransactions (review, assigner) VALUES (%s, %s) RETURNING id",
        (review.id, creator.id))
    transaction_id = cursor.fetchone()[0]

    def add(filter_type, paths):
        for path in paths:
            cursor.execute(
                """SELECT id, type
                                FROM reviewfilters
                               WHERE review=%s
                                 AND uid=%s
                                 AND path=%s""", (review.id, user.id, path))

            row = cursor.fetchone()

            if row:
                old_filter_id, old_filter_type = row

                if old_filter_type == filter_type:
                    continue
                else:
                    cursor.execute(
                        """DELETE FROM reviewfilters
                                            WHERE id=%s""", (old_filter_id, ))
                    cursor.execute(
                        """INSERT INTO reviewfilterchanges (transaction, uid, path, type, created)
                                           VALUES (%s, %s, %s, %s, false)""",
                        (transaction_id, user.id, path, old_filter_type))

            cursor.execute(
                """INSERT INTO reviewfilters (review, uid, path, type, creator)
                                   VALUES (%s, %s, %s, %s, %s)""",
                (review.id, user.id, path, filter_type, creator.id))
            cursor.execute(
                """INSERT INTO reviewfilterchanges (transaction, uid, path, type, created)
                                   VALUES (%s, %s, %s, %s, true)""",
                (transaction_id, user.id, path, filter_type))

    add("reviewer", reviewer_paths)
    add("watcher", watcher_paths)

    filters = Filters()
    filters.setFiles(db, review=review)
    filters.load(db, review=review, user=user)

    if user not in review.reviewers and user not in review.watchers and user not in review.owners:
        cursor.execute(
            """INSERT INTO reviewusers (review, uid, type)
                          VALUES (%s, %s, 'manual')""", (
                review.id,
                user.id,
            ))

    delete_files = set()
    insert_files = set()

    if watcher_paths:
        # Unassign changes currently assigned to the affected user.
        cursor.execute(
            """SELECT reviewfiles.id, reviewfiles.file
                            FROM reviewfiles
                            JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id)
                           WHERE reviewfiles.review=%s
                             AND reviewuserfiles.uid=%s""",
            (review.id, user.id))

        for review_file_id, file_id in cursor:
            if not filters.isReviewer(user.id, file_id):
                delete_files.add(review_file_id)

    if reviewer_paths:
        # Assign changes currently not assigned to the affected user.
        cursor.execute(
            """SELECT reviewfiles.id, reviewfiles.file
                            FROM reviewfiles
                            JOIN changesets ON (changesets.id=reviewfiles.changeset)
                            JOIN commits ON (commits.id=changesets.child)
                            JOIN gitusers ON (gitusers.id=commits.author_gituser)
                 LEFT OUTER JOIN usergitemails ON (usergitemails.email=gitusers.email
                                               AND usergitemails.uid=%s)
                 LEFT OUTER JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id
                                                 AND reviewuserfiles.uid=%s)
                           WHERE reviewfiles.review=%s
                             AND usergitemails.uid IS NULL
                             AND reviewuserfiles.uid IS NULL""",
            (user.id, user.id, review.id))

        for review_file_id, file_id in cursor:
            if filters.isReviewer(user.id, file_id):
                insert_files.add(review_file_id)

    if delete_files:
        cursor.executemany(
            "DELETE FROM reviewuserfiles WHERE file=%s AND uid=%s",
            izip(delete_files, repeat(user.id)))
        cursor.executemany(
            "INSERT INTO reviewassignmentchanges (transaction, file, uid, assigned) VALUES (%s, %s, %s, false)",
            izip(repeat(transaction_id), delete_files, repeat(user.id)))

    if insert_files:
        cursor.executemany(
            "INSERT INTO reviewuserfiles (file, uid) VALUES (%s, %s)",
            izip(insert_files, repeat(user.id)))
        cursor.executemany(
            "INSERT INTO reviewassignmentchanges (transaction, file, uid, assigned) VALUES (%s, %s, %s, true)",
            izip(repeat(transaction_id), insert_files, repeat(user.id)))

    return generateMailsForAssignmentsTransaction(db, transaction_id)
Example #11
0
def createReview(db,
                 user,
                 repository,
                 commits,
                 branch_name,
                 summary,
                 description,
                 from_branch_name=None,
                 via_push=False,
                 reviewfilters=None,
                 applyfilters=True,
                 applyparentfilters=False,
                 recipientfilters=None):
    cursor = db.cursor()

    if via_push:
        applyparentfilters = bool(
            user.getPreference(db, 'review.applyUpstreamFilters'))

    branch = dbutils.Branch.fromName(db, repository, branch_name)

    if branch is not None:
        raise OperationFailure(
            code="branchexists",
            title="Invalid review branch name",
            message="""\
<p>There is already a branch named <code>%s</code> in the repository.  You have
to select a different name.</p>

<p>If you believe the existing branch was created during an earlier (failed)
attempt to create this review, you can try to delete it from the repository
using the command<p>

<pre>  git push &lt;remote&gt; :%s</pre>

<p>and then press the "Submit Review" button on this page again.""" %
            (htmlutils.htmlify(branch_name), htmlutils.htmlify(branch_name)),
            is_html=True)

    if not commits:
        raise OperationFailure(
            code="nocommits",
            title="No commits specified",
            message="You need at least one commit to create a review.")

    commitset = log_commitset.CommitSet(commits)
    heads = commitset.getHeads()

    if len(heads) != 1:
        # There is really no plausible way for this error to occur.
        raise OperationFailure(
            code="disconnectedtree",
            title="Disconnected tree",
            message=("The specified commits do do not form a single connected "
                     "tree.  Creating a review of them is not supported."))

    head = heads.pop()

    if len(commitset.getTails()) != 1:
        tail_id = None
    else:
        tail_id = gitutils.Commit.fromSHA1(
            db, repository,
            commitset.getTails().pop()).getId(db)

    if not via_push:
        try:
            repository.createBranch(branch_name, head.sha1)
        except gitutils.GitCommandError as error:
            raise OperationFailure(
                code="branchfailed",
                title="Failed to create review branch",
                message=("<p><b>Output from git:</b></p>"
                         "<code style='padding-left: 1em'>%s</code>" %
                         htmlutils.htmlify(error.output)),
                is_html=True)

    createChangesetsForCommits(db, commits)

    try:
        cursor.execute(
            "INSERT INTO branches (repository, name, head, tail, type) VALUES (%s, %s, %s, %s, 'review') RETURNING id",
            [repository.id, branch_name,
             head.getId(db), tail_id])

        branch_id = cursor.fetchone()[0]
        reachable_values = [(branch_id, commit.getId(db))
                            for commit in commits]

        cursor.executemany(
            "INSERT INTO reachable (branch, commit) VALUES (%s, %s)",
            reachable_values)

        cursor.execute(
            "INSERT INTO reviews (type, branch, state, summary, description, applyfilters, applyparentfilters) VALUES ('official', %s, 'open', %s, %s, %s, %s) RETURNING id",
            (branch_id, summary, description, applyfilters,
             applyparentfilters))

        review = dbutils.Review.fromId(db, cursor.fetchone()[0])

        cursor.execute(
            "INSERT INTO reviewusers (review, uid, owner) VALUES (%s, %s, TRUE)",
            (review.id, user.id))

        if reviewfilters is not None:
            cursor.executemany(
                """INSERT INTO reviewfilters (review, uid, path, type, creator)
                                       VALUES (%s, %s, %s, %s, %s)""",
                [(review.id, filter_user_id, filter_path, filter_type, user.id)
                 for filter_user_id, filter_path, filter_type, filter_delegate
                 in reviewfilters])

        is_opt_in = False

        if recipientfilters is not None:
            cursor.executemany(
                "INSERT INTO reviewrecipientfilters (review, uid, include) VALUES (%s, %s, %s)",
                [(review.id, filter_user_id, filter_include)
                 for filter_user_id, filter_include in recipientfilters])

            for filter_user_id, filter_include in recipientfilters:
                if filter_user_id is None and not filter_include:
                    is_opt_in = True

        addCommitsToReview(db, user, review, commits, new_review=True)

        if from_branch_name is not None:
            cursor.execute(
                "UPDATE branches SET review=%s WHERE repository=%s AND name=%s",
                (review.id, repository.id, from_branch_name))

        # Reload to get list of changesets added by addCommitsToReview().
        review = dbutils.Review.fromId(db, review.id)

        pending_mails = []
        recipients = review.getRecipients(db)
        for to_user in recipients:
            pending_mails.extend(
                mail.sendReviewCreated(db, user, to_user, recipients, review))

        if not is_opt_in:
            recipient_by_id = dict(
                (to_user.id, to_user) for to_user in recipients)

            cursor.execute(
                """SELECT userpreferences.uid, userpreferences.repository,
                                     userpreferences.filter, userpreferences.integer
                                FROM userpreferences
                     LEFT OUTER JOIN filters ON (filters.id=userpreferences.filter)
                               WHERE userpreferences.item='review.defaultOptOut'
                                 AND userpreferences.uid=ANY (%s)
                                 AND (userpreferences.filter IS NULL
                                   OR filters.repository=%s)
                                 AND (userpreferences.repository IS NULL
                                   OR userpreferences.repository=%s)""",
                (recipient_by_id.keys(), repository.id, repository.id))

            user_settings = {}
            has_filter_settings = False

            for user_id, repository_id, filter_id, integer in cursor:
                settings = user_settings.setdefault(user_id, [None, None, {}])
                value = bool(integer)

                if repository_id is None and filter_id is None:
                    settings[0] = value
                elif repository_id is not None:
                    settings[1] = value
                else:
                    settings[2][filter_id] = value
                    has_filter_settings = True

            if has_filter_settings:
                filters = Filters()
                filters.setFiles(db, review=review)

            for user_id, (global_default, repository_default,
                          filter_settings) in user_settings.items():
                to_user = recipient_by_id[user_id]
                opt_out = None

                if repository_default is not None:
                    opt_out = repository_default
                elif global_default is not None:
                    opt_out = global_default

                if filter_settings:
                    # Policy:
                    #
                    # If all of the user's filters that matched files in the
                    # review have review.defaultOptOut enabled, then opt out.
                    # When determining this, any review filters of the user's
                    # that match files in the review count as filters that don't
                    # have the review.defaultOptOut enabled.
                    #
                    # If any of the user's filters that matched files in the
                    # review have review.defaultOptOut disabled, then don't opt
                    # out.  When determining this, review filters are ignored.
                    #
                    # Otherwise, ignore the filter settings, and go with either
                    # the user's per-repository or global setting (as set
                    # above.)

                    filters.load(db, review=review, user=to_user)

                    # A set of filter ids.  If None is in the set, the user has
                    # one or more review filters in the review.  (These do not
                    # have ids.)
                    active_filters = filters.getActiveFilters(to_user)

                    for filter_id in active_filters:
                        if filter_id is None:
                            continue
                        elif filter_id in filter_settings:
                            if not filter_settings[filter_id]:
                                opt_out = False
                                break
                        else:
                            break
                    else:
                        if None not in active_filters:
                            opt_out = True

                if opt_out:
                    cursor.execute(
                        """INSERT INTO reviewrecipientfilters (review, uid, include)
                                           VALUES (%s, %s, FALSE)""",
                        (review.id, to_user.id))

        db.commit()

        mail.sendPendingMails(pending_mails)

        return review
    except:
        if not via_push:
            repository.run("branch", "-D", branch_name)
        raise
Example #12
0
def assignChanges(db,
                  user,
                  review,
                  commits=None,
                  changesets=None,
                  update=False):
    cursor = db.cursor()

    if changesets is None:
        assert commits is not None

        changesets = []

        for commit in commits:
            changesets.extend(
                changeset_utils.createChangeset(db, user, review.repository,
                                                commit))

    applyfilters = review.applyfilters
    applyparentfilters = review.applyparentfilters

    reviewers, watchers = getReviewersAndWatchers(
        db,
        review.repository,
        changesets=changesets,
        reviewfilters=review.getReviewFilters(db),
        applyfilters=applyfilters,
        applyparentfilters=applyparentfilters)

    cursor.execute("SELECT uid FROM reviewusers WHERE review=%s",
                   (review.id, ))

    reviewusers = set([user_id for (user_id, ) in cursor])
    reviewusers_values = set()
    reviewuserfiles_values = set()

    reviewuserfiles_existing = {}

    if update:
        cursor.execute(
            """SELECT reviewuserfiles.uid, reviewfiles.changeset, reviewfiles.file
                            FROM reviewfiles
                            JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id)
                           WHERE reviewfiles.review=%s""", (review.id, ))
        for user_id, changeset_id, file_id in cursor:
            reviewuserfiles_existing[(user_id, changeset_id, file_id)] = True

    new_reviewers = set()
    new_watchers = set()

    cursor.execute(
        """SELECT DISTINCT uid
                        FROM reviewfiles
                        JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id)
                       WHERE review=%s""", (review.id, ))
    old_reviewers = set([user_id for (user_id, ) in cursor])

    for file_id, file_users in reviewers.items():
        for user_id, user_changesets in file_users.items():
            if user_id:
                new_reviewers.add(user_id)

                if user_id not in reviewusers:
                    reviewusers.add(user_id)
                    reviewusers_values.add((review.id, user_id))
                for changeset_id in user_changesets:
                    if (user_id, changeset_id,
                            file_id) not in reviewuserfiles_existing:
                        reviewuserfiles_values.add(
                            (user_id, review.id, changeset_id, file_id))

    for file_id, file_users in watchers.items():
        for user_id, user_changesets in file_users.items():
            if user_id:
                if user_id not in reviewusers:
                    new_watchers.add(user_id)
                    reviewusers.add(user_id)
                    reviewusers_values.add((review.id, user_id))

    new_reviewers -= old_reviewers
    new_watchers -= old_reviewers | new_reviewers

    cursor.executemany("INSERT INTO reviewusers (review, uid) VALUES (%s, %s)",
                       reviewusers_values)
    cursor.executemany(
        "INSERT INTO reviewuserfiles (file, uid) SELECT id, %s FROM reviewfiles WHERE review=%s AND changeset=%s AND file=%s",
        reviewuserfiles_values)

    if configuration.extensions.ENABLED:
        cursor.execute(
            """SELECT id, uid, extension, path
                            FROM extensionhookfilters
                           WHERE repository=%s""", (review.repository.id, ))

        rows = cursor.fetchall()

        if rows:
            if commits is None:
                commits = set()
                for changeset in changesets:
                    commits.add(changeset.child)
                commits = list(commits)

            filters = Filters()
            filters.setFiles(db, list(getFileIdsFromChangesets(changesets)))

            for filter_id, user_id, extension_id, path in rows:
                filters.addFilter(user_id, path, None, None, filter_id)

            for filter_id, file_ids in filters.matched_files.items():
                extensions.role.filterhook.queueFilterHookEvent(
                    db, filter_id, review, user, commits, file_ids)

    return new_reviewers, new_watchers
Example #13
0
def assignChanges(db, user, review, commits=None, changesets=None, update=False):
    cursor = db.cursor()

    if changesets is None:
        assert commits is not None

        changesets = []

        for commit in commits:
            changesets.extend(changeset_utils.createChangeset(db, user, review.repository, commit))

    applyfilters = review.applyfilters
    applyparentfilters = review.applyparentfilters

    reviewers, watchers = getReviewersAndWatchers(db, review.repository, changesets=changesets, reviewfilters=review.getReviewFilters(db),
                                                  applyfilters=applyfilters, applyparentfilters=applyparentfilters)

    cursor.execute("SELECT uid FROM reviewusers WHERE review=%s", (review.id,))

    reviewusers = set([user_id for (user_id,) in cursor])
    reviewusers_values = set()
    reviewuserfiles_values = set()

    reviewuserfiles_existing = {}

    if update:
        cursor.execute("""SELECT reviewuserfiles.uid, reviewfiles.changeset, reviewfiles.file
                            FROM reviewfiles
                            JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id)
                           WHERE reviewfiles.review=%s""", (review.id,))
        for user_id, changeset_id, file_id in cursor:
            reviewuserfiles_existing[(user_id, changeset_id, file_id)] = True

    new_reviewers = set()
    new_watchers = set()

    cursor.execute("""SELECT DISTINCT uid
                        FROM reviewfiles
                        JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id)
                       WHERE review=%s""", (review.id,))
    old_reviewers = set([user_id for (user_id,) in cursor])

    for file_id, file_users in reviewers.items():
        for user_id, user_changesets in file_users.items():
            if user_id:
                new_reviewers.add(user_id)

                if user_id not in reviewusers:
                    reviewusers.add(user_id)
                    reviewusers_values.add((review.id, user_id))
                for changeset_id in user_changesets:
                    if (user_id, changeset_id, file_id) not in reviewuserfiles_existing:
                        reviewuserfiles_values.add((user_id, review.id, changeset_id, file_id))

    for file_id, file_users in watchers.items():
        for user_id, user_changesets in file_users.items():
            if user_id:
                if user_id not in reviewusers:
                    new_watchers.add(user_id)
                    reviewusers.add(user_id)
                    reviewusers_values.add((review.id, user_id))

    new_reviewers -= old_reviewers
    new_watchers -= old_reviewers | new_reviewers

    cursor.executemany("INSERT INTO reviewusers (review, uid) VALUES (%s, %s)", reviewusers_values)
    cursor.executemany("INSERT INTO reviewuserfiles (file, uid) SELECT id, %s FROM reviewfiles WHERE review=%s AND changeset=%s AND file=%s", reviewuserfiles_values)

    if configuration.extensions.ENABLED:
        cursor.execute("""SELECT id, uid, extension, path
                            FROM extensionhookfilters
                           WHERE repository=%s""",
                       (review.repository.id,))

        rows = cursor.fetchall()

        if rows:
            if commits is None:
                commits = set()
                for changeset in changesets:
                    commits.add(changeset.child)
                commits = list(commits)

            filters = Filters()
            filters.setFiles(db, list(getFileIdsFromChangesets(changesets)))

            for filter_id, user_id, extension_id, path in rows:
                filters.addFilter(user_id, path, None, None, filter_id)

            for filter_id, file_ids in filters.matched_files.items():
                extensions.role.filterhook.queueFilterHookEvent(
                    db, filter_id, review, user, commits, file_ids)

    return new_reviewers, new_watchers
Example #14
0
     data = json_decode(sys.stdin.readline())
     batch_id = data["batch_id"]
     was_accepted = data["was_accepted"]
     is_accepted = data["is_accepted"]
     pending_mails = reviewing.utils.generateMailsForBatch(db, batch_id, was_accepted, is_accepted)
 elif command == "generate-mails-for-assignments-transaction":
     data = json_decode(sys.stdin.readline())
     transaction_id = data["transaction_id"]
     pending_mails = reviewing.utils.generateMailsForAssignmentsTransaction(db, transaction_id)
 elif command == "apply-filters":
     data = json_decode(sys.stdin.readline())
     filters = reviewing.filters.Filters()
     user = dbutils.User.fromId(db, data["user_id"]) if "user_id" in data else None
     if "review_id" in data:
         review = dbutils.Review.fromId(db, data["review_id"], load_commits=False)
         filters.setFiles(db, review=review)
         filters.load(db, review=review, user=user,
                      added_review_filters=data.get("added_review_filters", []),
                      removed_review_filters=data.get("removed_review_filters", []))
     else:
         repository = gitutils.Repository.fromId(db, data["repository_id"])
         filters.setFiles(db, file_ids=data["file_ids"])
         filters.load(db, repository=repository, recursive=data.get("recursive", False), user=user)
     sys.stdout.write(json_encode(filters.data) + "\n")
 elif command == "generate-custom-mails":
     pending_mails = []
     for data in json_decode(sys.stdin.readline()):
         from_user = dbutils.User.fromId(db, data["sender"])
         if data.get("recipients"):
             recipients = [dbutils.User.fromId(db, user_id)
                           for user_id in data["recipients"]]
Example #15
0
    def process(self, db, user, repository_id=None, filter_id=None):
        if user.isAnonymous():
            return OperationFailureMustLogin()

        cursor = db.cursor()

        if filter_id is not None:
            cursor.execute(
                """SELECT repository, path, type, delegate
                                FROM filters
                               WHERE id=%s""", (filter_id, ))
            repository_id, filter_path, filter_type, filter_delegate = cursor.fetchone(
            )

        if repository_id is None:
            cursor.execute(
                """SELECT reviews.id, applyfilters, applyparentfilters, branches.repository
                                FROM reviews
                                JOIN branches ON (reviews.branch=branches.id)
                               WHERE reviews.state!='closed'""")
        else:
            cursor.execute(
                """SELECT reviews.id, applyfilters, applyparentfilters, branches.repository
                                FROM reviews
                                JOIN branches ON (reviews.branch=branches.id)
                               WHERE reviews.state!='closed'
                                 AND branches.repository=%s""",
                (repository_id, ))

        repositories = {}

        # list(review_file_id)
        assign_changes = []

        # set(review_id)
        assigned_reviews = set()

        # set(review_id)
        watched_reviews = set()

        for review_id, applyfilters, applyparentfilters, repository_id in cursor.fetchall(
        ):
            if repository_id in repositories:
                repository = repositories[repository_id]
            else:
                repository = gitutils.Repository.fromId(db, repository_id)
                repositories[repository_id] = repository

            review = reviewing.filters.Filters.Review(review_id, applyfilters,
                                                      applyparentfilters,
                                                      repository)
            filters = reviewing.filters.Filters()

            filters.setFiles(db, review=review)

            if filter_id is not None:
                filters.addFilter(user.id, filter_path, filter_type,
                                  filter_delegate, filter_id)
            else:
                filters.load(db, review=review, user=user)

            cursor.execute(
                """SELECT commits.id, reviewfiles.file, reviewfiles.id
                                FROM commits
                                JOIN gitusers ON (gitusers.id=commits.author_gituser)
                     LEFT OUTER JOIN usergitemails ON (usergitemails.email=gitusers.email
                                                   AND usergitemails.uid=%s)
                                JOIN changesets ON (changesets.child=commits.id)
                                JOIN reviewfiles ON (reviewfiles.changeset=changesets.id)
                     LEFT OUTER JOIN reviewuserfiles ON (reviewuserfiles.file=reviewfiles.id
                                                     AND reviewuserfiles.uid=%s)
                               WHERE reviewfiles.review=%s
                                 AND usergitemails.uid IS NULL
                                 AND reviewuserfiles.uid IS NULL""",
                (user.id, user.id, review_id))

            for commit_id, file_id, review_file_id in cursor.fetchall():
                association = filters.getUserFileAssociation(user.id, file_id)

                if association == 'reviewer':
                    assign_changes.append(review_file_id)
                    assigned_reviews.add(review_id)
                elif association == 'watcher':
                    watched_reviews.add(review_id)

        cursor.execute(
            """SELECT reviews.id
                            FROM reviews
                 LEFT OUTER JOIN reviewusers ON (reviewusers.review=reviews.id
                                             AND reviewusers.uid=%s)
                           WHERE reviews.id=ANY (%s)
                             AND reviewusers.uid IS NULL""",
            (user.id, list(assigned_reviews) + list(watched_reviews)))

        new_reviews = set(review_id for (review_id, ) in cursor)

        cursor.executemany(
            """INSERT INTO reviewusers (review, uid)
                                   VALUES (%s, %s)""",
            [(review_id, user.id) for review_id in new_reviews])

        cursor.executemany(
            """INSERT INTO reviewuserfiles (file, uid)
                                   VALUES (%s, %s)""",
            [(review_file_id, user.id) for review_file_id in assign_changes])

        db.commit()

        watched_reviews &= new_reviews
        watched_reviews -= assigned_reviews

        cursor.execute(
            """SELECT id, summary
                            FROM reviews
                           WHERE id=ANY (%s)""",
            (list(assigned_reviews | watched_reviews), ))

        return OperationResult(assigned_reviews=sorted(assigned_reviews),
                               watched_reviews=sorted(watched_reviews),
                               summaries=dict(cursor))