In the third part of this series, you saw how to save the code review request information for follow-up. You created a method called read_email
to fetch the emails from the inbox to check if a reviewer has responded to the code review request. You also implemented error handling in the code review scheduler code.
In this part of the series, you'll use the saved code review information and the information from the emails to check if the reviewer has responded to the review request. If a request has not been responded to, you'll send a follow-up email to the reviewer.
Getting Started
Start by cloning the source code from the third part of the tutorial series.
git clone https://github.com/royagasthyan/CodeReviewer-Part3 CodeReviewer
Modify the config.json
file to include some relevant email addresses, keeping the [email protected]
email address. It's because git has commits related to this particular email address which are required for the code to execute as expected. Modify the SMTP
credentials in the schedule.py
file:
FROM_EMAIL = "[email protected]" FROM_PWD = "your_password"
Navigate to the project directory CodeReviewer
and try to execute the following command in the terminal.
python scheduler.py -n 20 -p "project_x"
It should send the code review request to random developers for review and create a reviewer.json
file with review information.
Implementing a Follow-Up Request
Let's start by creating a follow-up request method called followup_request
. Inside the followup_request
method, read the reviewer.json
file and keep the content in a list. Here is how the code looks:
with open('reviewer.json','r') as jfile: review_info = json.load(jfile)
Next, pull in the email information using the read_email
method that you implemented in the last tutorial.
email_info = read_email(no_days)
If the reviewer has responded to the review request, there should be an email with the same subject matter and a Re:
tag prefixed to it. So iterate through the review information list and compare the review subject with the email subject to see if the reviewer has responded to the request.
for review in review_info: review_replied = false expected_subject = 'RE: ' + review['subject'] for email in email_info: if expected_subject == email['subject']: review_replied = True print 'Reviewer has responded' break;
As seen in the above code, you iterated through the review_info
list and checked the review information subject against the email subject to see if the reviewer has responded.
Now, once the reviewer has responded to the code review request, you don't need to keep the particular review information in the reviewer.json
file. So create a Python method called Delete_Info
to remove the particular review information from the reviewer.json
file. Here is how Delete_Info
looks:
def Delete_Info(info, id): for i in xrange(len(info)): if info[i]['id'] == id: info.pop(i) break return info
As seen in the above code, you have iterated through the review information list and deleted the entry which matches the Id. After removing the information from the file, return the list.
You need to call the Delete_Info
method when a particular piece of review information is replied to. When calling the Delete_Info
method, you need to pass a copy of the review_info
so that the original info list isn't altered. You'll need the original review information list for comparison later. So import the copy
Python module to create a copy of the original review information list.
from copy import copy
Create a copy of the review_info
list.
review_info_copy = copy(review_info)
When deleting the review information that has been responded to from the original list, pass the copy list to the Delete_Info
method.
review_info_copy = Delete_Info(review_info_copy,review['id'])
Here is the followup_request
method:
def followup_request(): with open('reviewer.json','r') as jfile: review_info = json.load(jfile) review_info_copy = copy(review_info) email_info = read_email(no_days) for review in review_info: review_replied = False expected_subject = 'Re: ' + review['subject'] for email in email_info: if expected_subject == email['Subject']: review_replied = True review_info_copy = Delete_Info(review_info_copy,review['id']) break;
Now, once the review_info
list has been iterated, you need to check if there are any changes in the reviewer.json
file. If any existing review information has been removed, you need to update the reviewer.json
file appropriately. So check if review_info_copy
and review_info
are the same, and update the reviewer.json
file.
if review_info_copy != review_info: with open('reviewer.json','w') as outfile: json.dump(review_info_copy,outfile)
Here is the complete followup_request
method:
def followup_request(): with open('reviewer.json','r') as jfile: review_info = json.load(jfile) review_info_copy = copy(review_info) email_info = read_email(no_days) for review in review_info: review_replied = False expected_subject = 'Re: ' + review['subject'] for email in email_info: if expected_subject == email['Subject']: review_replied = True review_info_copy = Delete_Info(review_info_copy,review['id']) break; if review_info_copy != review_info: with open('reviewer.json','w') as outfile: json.dump(review_info_copy,outfile)
Make a call to the followup_request
method to follow up on the review requests that have already been sent.
try: commits = process_commits() # Added the follow Up Method followup_request() if len(commits) == 0: print 'No commits found ' else: schedule_review_request(commits) except Exception,e: print 'Error occurred. Check log for details.' logger.error(str(datetime.datetime.now()) + " - Error occurred : " + str(e) + "\n") logger.exception(str(e))
Save the above changes. To test the follow-up functionality, delete the reviewer.json
file from the project directory. Now run the scheduler so that code review requests are sent to random developers. Check if that information has been saved in the reviewer.json
file.
Ask the particular developer to respond to the code review request by replying to the email. Now run the scheduler again, and this time the scheduler program should be able to find the response and remove it from the reviewer.json
file.
Sending Reminder Emails
Once the reviewer has responded to the code review request emails, that information needs to be removed from the reviewer.json
file since you don't need to track it further. If the reviewer has not yet responded to the code review request, you need to send a follow-up mail to remind him or her about the review request.
The code review scheduler would run on a daily basis. When it's run, you first need to check if it's been a certain time since the developer has responded to the review request. In the project configuration, you can set a review period during which, if the reviewer has not responded, the scheduler would send a reminder email.
Let' start by adding a configuration in the project config. Add a new config called followup_frequency
in the config file.
{ "name": "project_x", "git_url": "https://github.com/royagasthyan/project_x", "followup_frequency":2, "members": [ "[email protected]", "[email protected]", "[email protected]", "[email protected]" ] }
So, when the reviewer has not responded for followup_frequency
number of days, you'll send a reminder email. Read the configuration into a global variable while reading the configurations:
for p in main_config: if p['name'] == project: project_url = p['git_url'] project_members = p['members'] followup_frequency = p['followup_frequency'] break
Inside the followup_request
method, send a reminder email when the reviewer has not replied to the follow-up requests for followup_frequency
number of days. Calculate the number of days since the review was sent.
review_date = datetime.datetime.strptime(review['sendDate'],'%Y-%m-%d') today = datetime.datetime.today() days_since_review = (today - review_date).days
If the number of days is greater than the follow-up frequency date in the configurations, send the reminder email.
if not review_replied: if days_since_review > followup_frequency: send_email(review['reviewer'],'Reminder: ' + review['subject'],'\nYou have not responded to the review request\n')
Here is the complete followup_request
method:
def followup_request(): with open('reviewer.json','r') as jfile: review_info = json.load(jfile) review_info_copy = copy(review_info) email_info = read_email(no_days) for review in review_info: review_date = datetime.datetime.strptime(review['sendDate'],'%Y-%m-%d') today = datetime.datetime.today() days_since_review = (today - review_date).days review_replied = False expected_subject = 'Re: ' + review['subject'] for email in email_info: if expected_subject == email['Subject']: review_replied = True review_info_copy = Delete_Info(review_info_copy,review['id']) break; if not review_replied: if days_since_review > followup_frequency: send_email(review['reviewer'],'Reminder: ' + review['subject'],'\nYou have not responded to the review request\n') if review_info_copy != review_info: with open('reviewer.json','w') as outfile: json.dump(review_info_copy,outfile)
Wrapping It Up
In this tutorial, you saw how to implement the logic to follow up on code review requests. You also added the functionality to send a reminder email if the reviewer hasn't responded to the email for a certain number of days.
This Python code reviewer can be further enhanced to suit your needs. Do fork the repository and add new features, and let us know in the comments below.
Source code from this tutorial is available on GitHub.
Comments