Статьи

Построение планировщика проверки кода Python: хранение информации о проверке

Во второй части этой серии вы увидели, как собирать информацию о коммитах из журналов git и отправлять запросы на просмотр случайным разработчикам, выбранным из списка участников проекта.

В этой части вы узнаете, как сохранять информацию о проверке кода для отслеживания при каждом запуске планировщика кода. Вы также узнаете, как читать электронные письма, чтобы проверить, ответил ли рецензент на запрос о рецензировании.

Начните с клонирования исходного кода из второй части серии руководств.

1
git clone https://github.com/royagasthyan/CodeReviewer-Part2 CodeReviewer

Измените файл config.json включив в него некоторые соответствующие адреса электронной почты, оставив [email protected] электронной почты [email protected] . Это потому, что у git есть коммиты, связанные с конкретным адресом электронной почты, который требуется для того, чтобы код выполнялся как ожидалось Измените учетные данные SMTP в файле schedule.py :

1
2
FROM_EMAIL = «[email protected]»
FROM_PWD = «your_password»

Перейдите в каталог проекта CodeReviewer и попробуйте выполнить следующую команду в терминале.

1
python scheduler.py -n 20 -p «project_x»

Он должен отправить запрос на проверку кода случайным разработчикам для проверки.

Чтобы получить информацию о запросе на проверку, вам нужно хранить его где-нибудь для справки. Вы можете выбрать, где вы хотите хранить информацию о проверке кода. Это может быть любая база данных или файл. Для этого руководства мы будем хранить информацию о запросе на просмотр в файле reviewer.json . Каждый раз, когда запускается планировщик, он проверяет информационный файл, чтобы отслеживать запросы на рецензирование, на которые не были получены ответы.

Создайте метод с именем save_review_info который будет сохранять информацию запроса на просмотр в файле. Внутри метода save_review_info создайте info объект с рецензентом, субъектом и уникальным идентификатором.

1
2
def save_review_info(reviewer, subject):
   info = {‘reviewer’:reviewer,’subject’:subject,’id’:str(uuid.uuid4()),’sendDate’:str(datetime.date.today())}

Для уникального идентификатора импортируйте модуль Python uuid .

1
import uuid

Вам также понадобится Python-модуль datetime чтобы получить текущую дату. Импортируйте модуль datetime Python.

1
import datetime

Вам нужно инициализировать файл reviewer.json при запуске программы, если он еще не существует.

1
2
3
if not os.path.exists(‘reviewer.json’):
   with open(‘reviewer.json’,’w+’) as outfile:
       json.dump([],outfile)

Если файл не существует, вам нужно создать файл с именем reviewer.json и заполнить его пустым массивом JSON, как показано в приведенном выше коде.

Этот метод будет вызываться при каждой отправке запроса на проверку. Итак, внутри метода save_review_info откройте файл reviewer.json в режиме чтения и прочитайте его содержимое. Добавьте информацию о новом контенте в существующий контент и запишите ее обратно в файл reviewer.json . Вот как будет выглядеть код:

01
02
03
04
05
06
07
08
09
10
def save_review_info(reviewer, subject):
   info = {‘reviewer’:reviewer,’subject’:subject,’id’:str(uuid.uuid4()),’sendDate’:str(datetime.date.today())}
 
   with open(‘reviewer.json’,’r’) as infile:
       review_data = json.load(infile)
 
   review_data.append(info)
 
   with open(‘reviewer.json’,’w’) as outfile:
       json.dump(review_data,outfile)

Внутри метода schedule_review_request перед отправкой письма с save_review_info на save_review_info метод save_review_info для сохранения информации о проверке.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
def schedule_review_request(commits):
   date = time.strftime(«%Y-%m-%d»)
    
   for commit in commits:
       reviewer = select_reviewer(commit.Author, project_members)
       subject = date + » Code Review [commit:» + commit.Id + «]»
       body = «Hello ‘» + reviewer + «‘, you have been selected to review the code for commit\n»
       body += «done by ‘» + commit.Author + «‘.\n»
       body += «\n»
        
       body += format_review_commit(commit)
 
       save_review_info(reviewer,subject);
 
       send_email(reviewer,subject,body)

Сохраните вышеуказанные изменения и запустите программу планировщика. После запуска планировщика вы сможете просмотреть файл reviewer.json в каталоге проекта с информацией о запросе на проверку кода. Вот как это будет выглядеть:

01
02
03
04
05
06
07
08
09
10
11
[{
    «reviewer»: «[email protected]»,
    «id»: «8ca7da84-9da7-4a17-9843-be293ea8202c»,
    «sendDate»: «2017-02-24»,
    «subject»: «2017-02-24 Code Review [commit:16393106c944981f57b2b48a9180a33e217faacc]»
}, {
    «reviewer»: «[email protected]»,
    «id»: «68765291-1891-4b50-886e-e30ab41a8810»,
    «sendDate»: «2017-02-24»,
    «subject»: «2017-02-24 Code Review [commit:04d11e21fb625215c5e672a93d955f4a176e16e4]»
}]

Вы собрали всю информацию о запросе проверки кода и сохранили ее в файле reviewer.json . Теперь каждый раз, когда запускается планировщик, вам нужно проверять почтовый ящик, чтобы увидеть, ответил ли рецензент на запрос проверки кода. Итак, сначала вам нужно определить метод для чтения вашего почтового ящика Gmail.

Создайте метод read_email который принимает количество дней, чтобы проверить входящие в качестве параметра. Вы будете использовать Python-модуль imaplib для чтения почтового ящика. Импортируйте imaplib Python imaplib :

1
import imaplib

Чтобы прочитать письмо с imaplib модуля imaplib , вам сначала нужно создать сервер.

1
email_server = imaplib.IMAP4_SSL(SERVER)

Войдите на сервер, используя адрес электронной почты и пароль:

1
email_server.login(FROM_EMAIL,FROM_PWD)

После входа в систему выберите папку «Входящие», чтобы прочитать сообщения электронной почты:

1
email_server.select(‘inbox’)

Вы будете читать электронные письма в течение последних n дней с момента отправки запроса на проверку кода. Импортируйте timedelta Python timedelta .

1
import timedelta

Создайте дату электронной почты как показано:

1
2
email_date = datetime.date.today() — timedelta(days=num_days)
formatted_date = email_date.strftime(‘%d-%b-%Y’)

Используя formatted_date , formatted_date поиск сообщений на почтовом сервере.

1
typ, data = email_server.search(None, ‘(SINCE «‘ + formatted_date + ‘»)’)

Он будет возвращать уникальные идентификаторы для каждого электронного письма, и, используя уникальные идентификаторы, вы можете получить данные электронной почты.

1
2
3
4
5
6
ids = data[0]
 
id_list = ids.split()
 
first_email_id = int(id_list[0])
last_email_id = int(id_list[-1])

Теперь вы будете использовать first_email_id и last_email_id чтобы перебирать электронные письма и извлекать тему и адрес «от» писем.

1
2
for i in range(last_email_id,first_email_id, -1):
   typ, data = email_server.fetch(i, ‘(RFC822)’ )

data будут содержать содержимое электронной почты, поэтому выполните итерацию части данных и проверьте наличие кортежа. Вы будете использовать почтовый модуль Python для извлечения деталей. Поэтому импортируйте email модуль Python.

1
import email

Вы можете извлечь тему письма и адрес «от», как показано ниже:

1
2
3
4
5
6
7
8
for response_part in data:
   if isinstance(response_part, tuple):
       msg = email.message_from_string(response_part[1])
       print ‘From: ‘ + msg[‘from’]
       print ‘\n’
       print ‘Subject: ‘ + msg[‘subject’]
       print ‘\n’
       print ‘————————————————‘

Вот полный метод read_email :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def read_email(num_days):
   try:
       email_server = imaplib.IMAP4_SSL(SERVER)
       email_server.login(FROM_EMAIL,FROM_PWD)
       email_server.select(‘inbox’)
 
       email_date = datetime.date.today() — timedelta(days=num_days)
       formatted_date = email_date.strftime(‘%d-%b-%Y’)
 
       typ, data = email_server.search(None, ‘(SINCE «‘ + formatted_date + ‘»)’)
       ids = data[0]
 
       id_list = ids.split()
 
       first_email_id = int(id_list[0])
       last_email_id = int(id_list[-1])
 
       for i in range(last_email_id,first_email_id, -1):
           typ, data = email_server.fetch(i, ‘(RFC822)’ )
 
           for response_part in data:
               if isinstance(response_part, tuple):
                   msg = email.message_from_string(response_part[1])
                   print ‘From: ‘ + msg[‘from’]
                   print ‘\n’
                   print ‘Subject: ‘ + msg[‘subject’]
                   print ‘\n’
                   print ‘————————————————‘
 
   except Exception, e:
       print str(e)

Сохраните вышеуказанные изменения и попробуйте запустить вышеописанный метод read_email :

1
read_email(1)

Он должен напечатать тему электронного письма и адрес «от» на терминале.

Чтение электронной почты из Gmail

Теперь давайте соберем адрес «от» и тему в список email_info и вернем данные.

1
email_info = []

Вместо того, чтобы печатать тему и адрес «от», добавьте данные в список email_info и email_info список email_info .

1
email_info.append({‘From’:msg[‘from’],’Subject’:msg[‘subject’].replace(«\r\n»,»»)})

Вот модифицированный метод read_email :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def read_email(num_days):
   try:
       email_info = []
       email_server = imaplib.IMAP4_SSL(SERVER)
       email_server.login(FROM_EMAIL,FROM_PWD)
       email_server.select(‘inbox’)
 
       email_date = datetime.date.today() — timedelta(days=num_days)
       formatted_date = email_date.strftime(‘%d-%b-%Y’)
 
       typ, data = email_server.search(None, ‘(SINCE «‘ + formatted_date + ‘»)’)
       ids = data[0]
 
       id_list = ids.split()
 
       first_email_id = int(id_list[0])
       last_email_id = int(id_list[-1])
 
       for i in range(last_email_id,first_email_id, -1):
           typ, data = email_server.fetch(i, ‘(RFC822)’ )
 
           for response_part in data:
               if isinstance(response_part, tuple):
                   msg = email.message_from_string(response_part[1])
                   email_info.append({‘From’:msg[‘from’],’Subject’:msg[‘subject’].replace(«\r\n»,»»)})
 
   except Exception, e:
       print str(e)
 
   return email_info

Обработка ошибок является важным аспектом разработки программного обеспечения. Это действительно полезно на этапе отладки для отслеживания ошибок. Если у вас нет обработки ошибок, то становится очень сложно отследить ошибку. Поскольку вы используете несколько новых методов, я думаю, что сейчас самое время добавить обработку ошибок в код планировщика.

Чтобы начать работать с ошибками, вам понадобится модуль Python для logging и класс RotatingFileHandler . Импортируйте их, как показано:

1
2
import logging
from logging.handlers import RotatingFileHandler

Как только у вас будет необходимый импорт, инициализируйте регистратор, как показано:

1
2
logger = logging.getLogger(«Code Review Log»)
logger.setLevel(logging.INFO)

В приведенном выше коде вы инициализировали регистратор и установите уровень журнала на INFO.

Создайте вращающийся обработчик журнала файлов, который будет создавать новый файл каждый раз, когда размер файла журнала достигает максимального размера.

1
logHandler = RotatingFileHandler(‘app.log’,maxBytes=3000,backupCount=2)

Присоедините logHandler к объекту logger .

1
logger.addHandler(logHandler)

Давайте добавим регистратор ошибок, чтобы регистрировать ошибки при обнаружении исключения. В части read_email метода read_email добавьте следующий код:

1
2
logger.error(str(datetime.datetime.now()) + » — Error while reading mail : » + str(e) + «\n»)
logger.exception(str(e))

Первая строка записывает сообщение об ошибке с текущей датой и временем в файл журнала. Во второй строке записывается трассировка стека до ошибки.

Точно так же вы можете добавить обработку ошибок в основную часть кода. Вот как будет выглядеть код с обработкой ошибок:

01
02
03
04
05
06
07
08
09
10
11
try:
    commits = process_commits()
 
    if len(commits) == 0:
        print ‘No commits found ‘
    else:
        schedule_review_request(commits)
except Exception,e:
    print ‘Error occurred.
    logger.error(str(datetime.datetime.now()) + » — Error while reading mail : » + str(e) + «\n»)
    logger.exception(str(e))

В этой части серии вы отложили информацию о запросе на проверку в файле reviewer.json . Вы также создали метод для чтения электронных писем. Вы будете использовать обе эти функции для выполнения запросов на проверку кода в заключительной части этой серии.

Кроме того, не стесняйтесь посмотреть, что у нас есть в наличии для продажи и для изучения на рынке , и не стесняйтесь задавать любые вопросы и предоставлять ценные отзывы, используя канал ниже.

Исходный код из этого урока доступен на GitHub .

Дайте нам знать ваши мысли и предложения в комментариях ниже.