def process(self, db, user, review_id, remark=None): cursor = db.cursor() profiler = profiling.Profiler() profiler.check("start") review = dbutils.Review.fromId(db, review_id) profiler.check("create review") was_accepted = review.state == "open" and review.accepted(db) profiler.check("accepted before") if remark and remark.strip(): chain_id = createCommentChain(db, user, review, 'note') createComment(db, user, chain_id, remark, first=True) else: chain_id = None # Create a batch that groups all submitted changes together. cursor.execute( "INSERT INTO batches (review, uid, comment) VALUES (%s, %s, %s) RETURNING id", (review.id, user.id, chain_id)) batch_id = cursor.fetchone()[0] profiler.check("batches++") # Reject all draft file approvals where the affected review file isn't in # the state it was in when the change was drafted. cursor.execute( """UPDATE reviewfilechanges SET state='rejected', time=now() WHERE uid=%s AND state='draft' AND file IN (SELECT id FROM reviewfiles JOIN reviewfilechanges ON (reviewfilechanges.file=reviewfiles.id) WHERE reviewfiles.review=%s AND reviewfilechanges.uid=%s AND reviewfilechanges.state='draft' AND reviewfilechanges.from_state!=reviewfiles.state)""", (user.id, review.id, user.id)) profiler.check("reviewfilechanges reject state changes") # Then perform the remaining draft file approvals by updating the state of # the corresponding review file. cursor.execute( """UPDATE reviewfiles SET state='reviewed', reviewer=%s, time=now() WHERE review=%s AND id IN (SELECT file FROM reviewfilechanges WHERE uid=%s AND state='draft' AND to_state='reviewed')""", (user.id, review.id, user.id)) profiler.check("reviewfiles pending=>reviewed") # Then perform the remaining draft file disapprovals by updating the state # of the corresponding review file. cursor.execute( """UPDATE reviewfiles SET state='pending', reviewer=NULL, time=now() WHERE review=%s AND id IN (SELECT file FROM reviewfilechanges WHERE uid=%s AND state='draft' AND to_state='pending')""", (review.id, user.id)) profiler.check("reviewfiles reviewed=>pending") # Finally change the state of just performed approvals from draft to # 'performed'. cursor.execute( """UPDATE reviewfilechanges SET batch=%s, state='performed', time=now() WHERE uid=%s AND state='draft' AND file IN (SELECT id FROM reviewfiles WHERE reviewfiles.review=%s)""", (batch_id, user.id, review.id)) profiler.check("reviewfilechanges draft=>performed") # Find all chains with draft comments being submitted that the current user # isn't associated with via the commentchainusers table, and associate the # user with them. cursor.execute( """SELECT DISTINCT commentchains.id, commentchainusers.uid IS NULL FROM commentchains JOIN comments ON (comments.chain=commentchains.id) LEFT OUTER JOIN commentchainusers ON (commentchainusers.chain=commentchains.id AND commentchainusers.uid=comments.uid) WHERE commentchains.review=%s AND comments.uid=%s AND comments.state='draft'""", (review.id, user.id)) for chain_id, need_associate in cursor.fetchall(): if need_associate: cursor.execute( "INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)", (chain_id, user.id)) profiler.check("commentchainusers++") # Find all chains with draft comments being submitted and add a record for # every user associated with the chain to read the comment. cursor.execute( """INSERT INTO commentstoread (uid, comment) SELECT commentchainusers.uid, comments.id FROM commentchains, commentchainusers, comments WHERE commentchains.review=%s AND commentchainusers.chain=commentchains.id AND commentchainusers.uid!=comments.uid AND comments.chain=commentchains.id AND comments.uid=%s AND comments.state='draft'""", (review.id, user.id)) profiler.check("commentstoread++") # Associate all users associated with a draft comment chain to # the review (if they weren't already.) cursor.execute( """SELECT DISTINCT commentchainusers.uid FROM commentchains JOIN commentchainusers ON (commentchainusers.chain=commentchains.id) LEFT OUTER JOIN reviewusers ON (reviewusers.review=commentchains.review AND reviewusers.uid=commentchainusers.uid) WHERE commentchains.review=%s AND commentchains.uid=%s AND commentchains.state='draft' AND reviewusers.uid IS NULL""", (review.id, user.id)) for (user_id, ) in cursor.fetchall(): cursor.execute( "INSERT INTO reviewusers (review, uid) VALUES (%s, %s)", (review.id, user_id)) # Change state on all draft commentchains by the user in the review to 'open'. cursor.execute( """UPDATE commentchains SET batch=%s, state='open', time=now() WHERE commentchains.review=%s AND commentchains.uid=%s AND commentchains.state='draft'""", (batch_id, review.id, user.id)) profiler.check("commentchains draft=>open") # Reject all draft comment chain changes where the affected comment # chain isn't in the state it was in when the change was drafted, or has # been morphed into a note since the change was drafted. cursor.execute( """UPDATE commentchainchanges SET state='rejected', time=now() WHERE uid=%s AND state='draft' AND from_state IS NOT NULL AND chain IN (SELECT id FROM commentchains JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id AND (commentchainchanges.from_state!=commentchains.state OR commentchainchanges.from_last_commit!=commentchains.last_commit OR commentchains.type!='issue')) WHERE commentchains.review=%s AND commentchainchanges.uid=%s AND commentchainchanges.state='draft')""", (user.id, review.id, user.id)) profiler.check("commentchainchanges reject state changes") # Reject all draft comment chain changes where the affected comment chain # type isn't what it was in when the change was drafted. cursor.execute( """UPDATE commentchainchanges SET state='rejected', time=now() WHERE uid=%s AND state='draft' AND from_type IS NOT NULL AND chain IN (SELECT id FROM commentchains JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id AND commentchainchanges.from_type!=commentchains.type) WHERE commentchains.review=%s AND commentchainchanges.uid=%s AND commentchainchanges.state='draft')""", (user.id, review.id, user.id)) profiler.check("commentchainchanges reject type changes") # Reject all draft comment chain changes where the affected comment chain # addressed_by isn't what it was in when the change was drafted. cursor.execute( """UPDATE commentchainchanges SET state='rejected', time=now() WHERE uid=%s AND state='draft' AND from_addressed_by IS NOT NULL AND chain IN (SELECT id FROM commentchains JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id AND commentchainchanges.from_addressed_by!=commentchains.addressed_by) WHERE commentchains.review=%s AND commentchainchanges.uid=%s AND commentchainchanges.state='draft')""", (user.id, review.id, user.id)) profiler.check("commentchainchanges reject addressed_by changes") # Then perform the remaining draft comment chain changes by updating the # state of the corresponding comment chain. # Perform open->closed changes, including setting 'closed_by'. cursor.execute( """UPDATE commentchains SET state='closed', closed_by=%s WHERE review=%s AND id IN (SELECT chain FROM commentchainchanges WHERE uid=%s AND state='draft' AND to_state='closed')""", (user.id, review.id, user.id)) profiler.check("commentchains closed") # Perform (closed|addressed)->open changes, including resetting 'closed_by' and # 'addressed_by' to NULL. cursor.execute( """SELECT commentchainchanges.to_last_commit, commentchains.id FROM commentchains JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id) WHERE commentchains.review=%s AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.to_state='open'""", (review.id, user.id)) cursor.executemany( """UPDATE commentchains SET state='open', last_commit=%s, closed_by=NULL, addressed_by=NULL WHERE id=%s""", cursor.fetchall()) profiler.check("commentchains reopen") # Perform addressed->addressed changes, i.e. updating 'addressed_by'. cursor.execute( """SELECT commentchainchanges.to_addressed_by, commentchains.id FROM commentchains JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id) WHERE commentchains.review=%s AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.to_addressed_by IS NOT NULL""", (review.id, user.id)) cursor.executemany( """UPDATE commentchains SET addressed_by=%s WHERE id=%s""", cursor.fetchall()) profiler.check("commentchains reopen (partial)") # Perform type changes. cursor.execute( """SELECT commentchainchanges.to_type, commentchains.id FROM commentchains JOIN commentchainchanges ON (commentchainchanges.chain=commentchains.id) WHERE commentchains.review=%s AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.to_type IS NOT NULL""", (review.id, user.id)) cursor.executemany( """UPDATE commentchains SET type=%s WHERE id=%s""", cursor.fetchall()) profiler.check("commentchains type change") # Finally change the state of just performed changes from draft to # 'performed'. cursor.execute( """UPDATE commentchainchanges SET batch=%s, state='performed', time=now() WHERE uid=%s AND state='draft' AND chain IN (SELECT id FROM commentchains WHERE review=%s)""", (batch_id, user.id, review.id)) profiler.check("commentchainchanges draft=>performed") # Change state on all draft commentchainlines by the user in the review to 'current'. cursor.execute( """UPDATE commentchainlines SET state='current', time=now() WHERE uid=%s AND state='draft' AND chain IN (SELECT id FROM commentchains WHERE review=%s)""", (user.id, review.id)) profiler.check("commentchainlines draft=>current") # Change state on all draft comments by the user in the review to 'current'. cursor.execute( """UPDATE comments SET batch=%s, state='current', time=now() WHERE comments.uid=%s AND comments.state='draft' AND chain IN (SELECT id FROM commentchains WHERE review=%s)""", (batch_id, user.id, review.id)) profiler.check("comments draft=>current") # Associate the submitting user with the review if he isn't already. cursor.execute("SELECT 1 FROM reviewusers WHERE review=%s AND uid=%s", (review.id, user.id)) if not cursor.fetchone(): cursor.execute( "INSERT INTO reviewusers (review, uid) VALUES (%s, %s)", (review.id, user.id)) generate_emails = profiler.start("generate emails") is_accepted = review.state == "open" and review.accepted(db) pending_mails = generateMailsForBatch(db, batch_id, was_accepted, is_accepted, profiler=profiler) generate_emails.stop() review.incrementSerial(db) db.commit() profiler.check("commit transaction") sendPendingMails(pending_mails) profiler.check("finished") if user.getPreference(db, "debug.profiling.submitChanges"): return OperationResult(batch_id=batch_id, serial=review.serial, profiling=profiler.output()) else: return OperationResult(batch_id=batch_id, serial=review.serial)
def process(self, db, user, review_id, remark=None): cursor = db.cursor() profiler = profiling.Profiler() profiler.check("start") review = dbutils.Review.fromId(db, review_id, load_commits=False) profiler.check("create review") was_accepted = review.state == "open" and review.accepted(db) profiler.check("accepted before") if remark: chain_id = createCommentChain(db, user, review, 'note') createComment(db, user, chain_id, remark, first=True) else: chain_id = None # Create a batch that groups all submitted changes together. cursor.execute("INSERT INTO batches (review, uid, comment) VALUES (%s, %s, %s) RETURNING id", (review.id, user.id, chain_id)) batch_id = cursor.fetchone()[0] profiler.check("batches++") # Reject all draft file approvals where the affected review file isn't in # the state it was in when the change was drafted. cursor.execute("""UPDATE reviewfilechanges SET state='rejected', time=now() FROM reviewfiles WHERE reviewfiles.review=%s AND reviewfiles.id=reviewfilechanges.file AND reviewfilechanges.uid=%s AND reviewfilechanges.state='draft' AND reviewfilechanges.from!=reviewfiles.state""", (review.id, user.id)) profiler.check("reviewfilechanges reject state changes") # Then perform the remaining draft file approvals by updating the state of # the corresponding review file. cursor.execute("""UPDATE reviewfiles SET state='reviewed', reviewer=reviewfilechanges.uid, time=now() FROM reviewfilechanges WHERE reviewfiles.review=%s AND reviewfilechanges.uid=%s AND reviewfilechanges.state='draft' AND reviewfilechanges.file=reviewfiles.id AND reviewfilechanges.from=reviewfiles.state AND reviewfilechanges.to='reviewed'""", (review.id, user.id)) profiler.check("reviewfiles pending=>reviewed") # Then perform the remaining draft file disapprovals by updating the state # of the corresponding review file. cursor.execute("""UPDATE reviewfiles SET state='pending', reviewer=NULL, time=now() FROM reviewfilechanges WHERE reviewfiles.review=%s AND reviewfilechanges.uid=%s AND reviewfilechanges.state='draft' AND reviewfilechanges.file=reviewfiles.id AND reviewfilechanges.from=reviewfiles.state AND reviewfilechanges.to='pending'""", (review.id, user.id)) profiler.check("reviewfiles reviewed=>pending") # Finally change the state of just performed approvals from draft to # 'performed'. cursor.execute("""UPDATE reviewfilechanges SET batch=%s, state='performed', time=now() FROM reviewfiles WHERE reviewfiles.review=%s AND reviewfiles.id=reviewfilechanges.file AND reviewfilechanges.uid=%s AND reviewfilechanges.state='draft' AND reviewfilechanges.to=reviewfiles.state""", (batch_id, review.id, user.id)) profiler.check("reviewfilechanges draft=>performed") # Find all chains with draft comments being submitted that the current user # isn't associated with via the commentchainusers table, and associate the # user with them. cursor.execute("""SELECT DISTINCT commentchains.id, commentchainusers.uid IS NULL FROM commentchains JOIN comments ON (comments.chain=commentchains.id) LEFT OUTER JOIN commentchainusers ON (commentchainusers.chain=commentchains.id AND commentchainusers.uid=comments.uid) WHERE commentchains.review=%s AND comments.uid=%s AND comments.state='draft'""", (review.id, user.id)) for chain_id, need_associate in cursor.fetchall(): if need_associate: cursor.execute("INSERT INTO commentchainusers (chain, uid) VALUES (%s, %s)", (chain_id, user.id)) profiler.check("commentchainusers++") # Find all chains with draft comments being submitted and add a record for # every user associated with the chain to read the comment. cursor.execute("""INSERT INTO commentstoread (uid, comment) SELECT commentchainusers.uid, comments.id FROM commentchains, commentchainusers, comments WHERE commentchains.review=%s AND commentchainusers.chain=commentchains.id AND commentchainusers.uid!=comments.uid AND comments.chain=commentchains.id AND comments.uid=%s AND comments.state='draft'""", (review.id, user.id)) profiler.check("commentstoread++") # Associate all users associated with a draft comment chain to # the review (if they weren't already.) cursor.execute("""SELECT DISTINCT commentchainusers.uid FROM commentchains JOIN commentchainusers ON (commentchainusers.chain=commentchains.id) LEFT OUTER JOIN reviewusers ON (reviewusers.review=commentchains.review AND reviewusers.uid=commentchainusers.uid) WHERE commentchains.review=%s AND commentchains.uid=%s AND commentchains.state='draft' AND reviewusers.uid IS NULL""", (review.id, user.id)) for (user_id,) in cursor.fetchall(): cursor.execute("INSERT INTO reviewusers (review, uid) VALUES (%s, %s)", (review.id, user_id)) # Change state on all draft commentchains by the user in the review to 'open'. cursor.execute("""UPDATE commentchains SET batch=%s, state='open', time=now() WHERE commentchains.review=%s AND commentchains.uid=%s AND commentchains.state='draft'""", (batch_id, review.id, user.id)) profiler.check("commentchains draft=>open") # Reject all draft comment chain changes where the affected comment chain # isn't in the state it was in when the change was drafted. cursor.execute("""UPDATE commentchainchanges SET state='rejected', time=now() FROM commentchains WHERE commentchains.review=%s AND commentchainchanges.chain=commentchains.id AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.from_state IS NOT NULL AND (commentchainchanges.from_state!=commentchains.state OR commentchainchanges.from_last_commit!=commentchains.last_commit)""", (review.id, user.id)) profiler.check("commentchainchanges reject state changes") # Reject all draft comment chain changes where the affected comment chain # type isn't what it was in when the change was drafted. cursor.execute("""UPDATE commentchainchanges SET state='rejected', time=now() FROM commentchains WHERE commentchains.review=%s AND commentchainchanges.chain=commentchains.id AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.from_type IS NOT NULL AND commentchainchanges.from_type!=commentchains.type""", (review.id, user.id)) profiler.check("commentchainchanges reject type changes") # Then perform the remaining draft comment chain changes by updating the # state of the corresponding comment chain. # Perform open->closed changes, including setting 'closed_by'. cursor.execute("""UPDATE commentchains SET state='closed', closed_by=commentchainchanges.uid FROM commentchainchanges WHERE commentchains.review=%s AND commentchainchanges.chain=commentchains.id AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.to_state='closed'""", (review.id, user.id)) profiler.check("commentchains closed") # Perform (closed|addressed)->open changes, including resetting 'closed_by' and # 'addressed_by' to NULL. cursor.execute("""UPDATE commentchains SET state='open', last_commit=commentchainchanges.to_last_commit, closed_by=NULL, addressed_by=NULL FROM commentchainchanges WHERE commentchains.review=%s AND commentchainchanges.chain=commentchains.id AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.to_state='open'""", (review.id, user.id)) profiler.check("commentchains reopen") # Perform type changes. cursor.execute("""UPDATE commentchains SET type=commentchainchanges.to_type FROM commentchainchanges WHERE commentchains.review=%s AND commentchainchanges.chain=commentchains.id AND commentchainchanges.uid=%s AND commentchainchanges.state='draft' AND commentchainchanges.from_type=commentchains.type AND commentchainchanges.to_type IS NOT NULL""", (review.id, user.id)) profiler.check("commentchains type change") # Finally change the state of just performed changes from draft to # 'performed'. cursor.execute("""UPDATE commentchainchanges SET batch=%s, state='performed', time=now() FROM commentchains WHERE commentchains.review=%s AND commentchainchanges.chain=commentchains.id AND commentchainchanges.uid=%s AND commentchainchanges.state='draft'""", (batch_id, review.id, user.id)) profiler.check("commentchainchanges draft=>performed") # Change state on all draft commentchainlines by the user in the review to 'current'. cursor.execute("""UPDATE commentchainlines SET state='current', time=now() FROM commentchains WHERE commentchains.review=%s AND commentchainlines.chain=commentchains.id AND commentchainlines.uid=%s AND commentchainlines.state='draft'""", (review.id, user.id)) profiler.check("commentchainlines draft=>current") # Change state on all draft comments by the user in the review to 'current'. cursor.execute("""UPDATE comments SET batch=%s, state='current', time=now() FROM commentchains WHERE commentchains.review=%s AND comments.chain=commentchains.id AND comments.uid=%s AND comments.state='draft'""", (batch_id, review.id, user.id)) profiler.check("comments draft=>current") # Associate the submitting user with the review if he isn't already. cursor.execute("SELECT 1 FROM reviewusers WHERE review=%s AND uid=%s", (review.id, user.id)) if not cursor.fetchone(): cursor.execute("INSERT INTO reviewusers (review, uid) VALUES (%s, %s)", (review.id, user.id)) generate_emails = profiler.start("generate emails") is_accepted = review.state == "open" and review.accepted(db) pending_mails = generateMailsForBatch(db, batch_id, was_accepted, is_accepted, profiler=profiler) generate_emails.stop() review.incrementSerial(db) db.commit() profiler.check("commit transaction") sendPendingMails(pending_mails) profiler.check("finished") if user.getPreference(db, "debug.profiling.submitChanges"): return OperationResult(serial=review.serial, profiling=profiler.output()) else: return OperationResult(serial=review.serial)
db.close() db = None try: if len(sys.argv) > 1: init() for command in sys.argv[1:]: 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 = review_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 = review_utils.generateMailsForAssignmentsTransaction(db, transaction_id) else: print "unknown command: %s" % command sys.exit(1) if pending_mails is not None: sys.stdout.write(json_encode(pending_mails) + "\n") finish() finally: abort()