Advertisement
  1. Code
  2. Python

Creando una Lista de revisión de código con Python: mantener la información de revisión

Scroll to top
Read Time: 9 min
This post is part of a series called Building a Python Code Review Scheduler.
Building a Python Code Review Scheduler: Sending Review Requests
Building a Python Code Review Scheduler: Review Follow-Up

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

En la segunda parte de esta serie, se vio cómo recopilar la información de los registros git de los commit y enviar solicitudes de revisión a los desarrolladores aleatorios seleccionados de la lista de miembros del proyecto.

En esta parte, verá cómo guardar la información de revisión de código para realizar el seguimiento cada vez que se ejecuta el programador de código. También verá cómo leer mensajes de correo electrónico para comprobar si el revisor ha respondido a la solicitud de revisión.

Introducción

Comience clonando el código fuente de la segunda parte de la serie.

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

Modifique el archivo config.json para incluir algunas direcciones de correo electrónico relevantes, manteniendo la dirección de correo electrónico royagasthyan@gmail.com. Es porque el git tiene commits relacionadas con esa dirección de correo electrónico particular y se requiere para que el código se ejecute correctamente. Modifique las credenciales SMTP en el archivo schedule.py:

1
FROM_EMAIL      = "your_email_address@gmail.com"
2
FROM_PWD        = "your_password"

Navegue hasta el directorio de proyecto CodeReviewer e intente ejecutar el siguiente comando en el terminal.

1
python scheduler.py -n 20 -p "project_x"

Debe enviar la solicitud de revisión de código a  desarrolladores aleatorios para su revisión.

Mantener la información de solicitud de revisión

Para dar seguimiento a la información de la solicitud de revisión, debe guardarla en algún lugar para referencia. Puede seleccionar dónde desea conservar la información de solicitud de revisión de código. Puede ser cualquier base de datos o puede ser un archivo. Para el bien de este tutorial, mantendremos la información de la solicitud de revisión dentro de un archivo reviewer.json. Cada vez que se ejecute el planificador, comprobará el archivo de información para dar seguimiento a las solicitudes de revisión que no se han respondido.

Cree un método llamado save_review_info que guarde la información de la solicitud de revisión dentro de un archivo. Dentro del método save_review_info, cree un objeto info con el revisor, el asunto y una Id única.

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

Para una Id única, importe el módulo Python uuid.

1
import uuid

También necesita el módulo de Python datetime para obtener la fecha actual. Importe el módulo de datetime de Python.

1
import datetime

Necesita inicializar el archivo reviewer.json cuando se inicie el programa si aún no existe.

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

Si el archivo no existe, debe crear un archivo llamado reviewer.json y rellenarlo con una array JSON vacía como se ve en el código anterior.

Este método se llamará cada vez que se envíe una solicitud de revisión. Por lo tanto, dentro del método save_review_info, abra el archivo reviewer.json en modo de lectura y lea el contenido. Añada la nueva información de contenido al contenido existente y vuelva a escribirla en el archivo reviewer.json. Así es como se vería el código:

1
def save_review_info(reviewer, subject):
2
    info = {'reviewer':reviewer,'subject':subject,'id':str(uuid.uuid4()),'sendDate':str(datetime.date.today())}
3
4
    with open('reviewer.json','r') as infile:
5
        review_data = json.load(infile)
6
7
    review_data.append(info)
8
9
    with open('reviewer.json','w') as outfile:
10
        json.dump(review_data,outfile)

Dentro del método schedule_review_request, antes de enviar el correo de solicitud de revisión de código, llame al método save_review_info para guardar la información de revisión.

1
def schedule_review_request(commits):
2
    date = time.strftime("%Y-%m-%d")
3
    
4
    for commit in commits:
5
        reviewer = select_reviewer(commit.Author, project_members)
6
        subject = date + " Code Review [commit:" + commit.Id + "]"
7
        body = "Hello '" + reviewer + "', you have been selected to review the code for commit\n"
8
        body += "done by '" + commit.Author + "'.\n"
9
        body += "\n"
10
        
11
        body += format_review_commit(commit)
12
13
        save_review_info(reviewer,subject);
14
15
        send_email(reviewer,subject,body)

Guarde los cambios anteriores y ejecute el programa  planificador. Una vez que se ha ejecutado el planificador, debería poder ver el archivo reviewer.json dentro del directorio del proyecto con la información de solicitud de revisión de código. Así es como se vería:

1
[{
2
    "reviewer": "samson1987@gmail.com",
3
    "id": "8ca7da84-9da7-4a17-9843-be293ea8202c",
4
    "sendDate": "2017-02-24",
5
    "subject": "2017-02-24 Code Review [commit:16393106c944981f57b2b48a9180a33e217faacc]"
6
}, {
7
    "reviewer": "roshanjames@gmail.com",
8
    "id": "68765291-1891-4b50-886e-e30ab41a8810",
9
    "sendDate": "2017-02-24",
10
    "subject": "2017-02-24 Code Review [commit:04d11e21fb625215c5e672a93d955f4a176e16e4]"
11
}]

Lectura de los datos de correo electrónico

Ha recogido toda la información de solicitud de revisión de código y la ha guardado en el archivo reviewer.json. Ahora, cada vez que se ejecuta el planificador, debe comprobar su bandeja de entrada de correo para ver si el revisor ha respondido a la solicitud de revisión de código. Así que primero debes definir un método para leer tu bandeja de entrada de Gmail.

Cree un método llamado read_email que toma el número de días para comprobar la bandeja de entrada como un parámetro. Utilizarás el módulo imaplib de Python para leer la bandeja de entrada de correo electrónico. Importe el módulo imaplib de Python:

1
import imaplib

Para leer el correo electrónico con el módulo imaplib, primero debe crear el servidor.

1
email_server = imaplib.IMAP4_SSL(SERVER)

Inicie sesión en el servidor utilizando la dirección de correo electrónico y la contraseña:

1
email_server.login(FROM_EMAIL,FROM_PWD)

Una vez conectado, seleccione la bandeja de entrada para leer los correos electrónicos:

1
email_server.select('inbox')

Estará leyendo los mensajes de correo electrónico durante el último número n de días desde que se envió la solicitud de revisión de código. Importe el módulo timedelta de Python.

1
import timedelta

Crear la fecha del correo electrónico como se muestra:

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

Utilizando formatted_date, busque en el servidor de correo electrónico los mensajes de correo electrónico.

1
typ, data = email_server.search(None, '(SINCE "' + formatted_date + '")')

Devolverá los identificadores únicos para cada correo electrónico y, utilizando los ID únicos, podrá obtener los detalles del correo electrónico.

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

Ahora usará el first_email_id y el last_email_id para iterar a través de los correos electrónicos y buscar el asunto y la dirección "from" de los correos electrónicos.

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

data contendrá el contenido del correo electrónico, por lo que iterar la parte de datos y comprobar una tupla. Estará haciendo uso del módulo de correo electrónico Python para extraer los detalles. Así que importa el módulo email de Python.

1
import email

Puede extraer el asunto del correo electrónico y la dirección "from" como se muestra:

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

Aquí está el método completo read_email:

1
def read_email(num_days):
2
    try:
3
        email_server = imaplib.IMAP4_SSL(SERVER)
4
        email_server.login(FROM_EMAIL,FROM_PWD)
5
        email_server.select('inbox')
6
7
        email_date = datetime.date.today() - timedelta(days=num_days)
8
        formatted_date = email_date.strftime('%d-%b-%Y')
9
10
        typ, data = email_server.search(None, '(SINCE "' + formatted_date + '")')
11
        ids = data[0]
12
13
        id_list = ids.split()
14
15
        first_email_id = int(id_list[0])
16
        last_email_id = int(id_list[-1])
17
18
        for i in range(last_email_id,first_email_id, -1):
19
            typ, data = email_server.fetch(i, '(RFC822)' )
20
21
            for response_part in data:
22
                if isinstance(response_part, tuple):
23
                    msg = email.message_from_string(response_part[1])
24
                    print 'From: ' + msg['from']
25
                    print '\n'
26
                    print 'Subject: ' + msg['subject']
27
                    print '\n'
28
                    print '------------------------------------------------'
29
30
    except Exception, e:
31
        print str(e)

Guarde los cambios anteriores e intente ejecutar el método read_email anterior:

1
read_email(1)

Debe imprimir el asunto del correo electrónico y la dirección "from" en el terminal.

Email Reading From GmailEmail Reading From GmailEmail Reading From Gmail

Ahora vamos a recopilar la dirección "from" y el asunto en una lista de email_info y devolver los datos.

1
email_info = []

En lugar de imprimir el asunto y la dirección "from", agregue los datos a la lista email_info y devuelva la lista email_info.

1
email_info.append({'From':msg['from'],'Subject':msg['subject'].replace("\r\n","")})

Aquí está el método read_email modificado:

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

Añadir registro para la gestión de errores

El manejo de errores es un aspecto importante del desarrollo de software. Es muy útil durante la fase de depuración para rastrear errores. Si no tiene ningún manejo de errores, entonces se hace realmente difícil de rastrear el error. Puesto que tiene un par de nuevos métodos, creo que es el momento adecuado para agregar el manejo de errores al código del planificador.

Para comenzar con el manejo de errores, necesitará el módulo logging de Python y la clase RotatingFileHandler. Importarlos como se muestra:

1
import logging
2
from logging.handlers import RotatingFileHandler

Una vez que tenga las importaciones necesarias, inicialice el logger como se muestra:

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

En el código anterior, inicializó el logger y estableció el nivel de registro en INFO.

Cree un manejador de registro de archivos rotatorio que creará un nuevo archivo cada vez que el archivo de registro haya alcanzado un tamaño máximo.

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

Adjuntar el logHandler al objeto logger.

1
logger.addHandler(logHandler)

Añadamos el registrador de errores para registrar errores cuando se captura una excepción. En la parte de excepción del método read_email, agregue el código siguiente:

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

La primera línea registra el mensaje de error con la fecha y hora actuales en el archivo de registro. La segunda línea registra el seguimiento stack trace en el error.

Del mismo modo, puede agregar el tratamiento de errores a la parte principal del código. Así es como se vería el código con el manejo de errores:

1
try:
2
    commits = process_commits()
3
4
    if len(commits) == 0:
5
        print 'No commits found '
6
    else:
7
        schedule_review_request(commits)
8
except Exception,e:
9
    print 'Error occurred. Check log for details.'
10
    logger.error(str(datetime.datetime.now()) + " - Error while reading mail : " + str(e) + "\n")
11
    logger.exception(str(e))

Conclusion

En esta parte de la serie, archivó la información de solicitud de revisión en el archivo reviewer.json. También creó un método para leer los correos electrónicos. Utilizará ambas funciones para dar seguimiento a las solicitudes de revisión de código en la parte final de esta serie.

Además, no dude en ver lo que tenemos disponible para la venta y para estudiar en el marketplace, y no dude en hacer cualquier pregunta y proporcionar su valiosos comentarios usando el feed a continuación.

El código fuente de este tutorial está disponible en GitHub.

Háganos saber sus pensamientos y sugerencias en los comentarios a continuación.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.