<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 GitHub API v3 - Uso de labels
Para los siguientes ejercicios usaremos el repositorio del curso Programación Avanzada de la Pontificia Universidad Católica de Chile para el segundo semestre del 2015. Ve el repo aqui.
Una herramienta interesante para saber el comportamiento de un repositorio es buscar la cantidad de issues (abiertas + cerradas) para cada label disponible
Step1:  Antes que todo, debemos preguntarnos
Step2:  Como podemos notar, es posible que tengamos más de una página de issues, por lo que tendremos que acceder al header y obtener la url a la siguiente página, repitiendo la operación hasta llegar a la última. Para facilitar esto, podemos agregar el parametro page a la url indicando el número de la página que queremos. ¿Cómo sabremos cuándo detenernos? Cuando en el header, en Link, no exista una url a la siguiente página ("next")
Step3:  Guardemos la información que nos interesa en objetos de una clase Issue, extraeremos los nombres de las labels con la funcion labels y guardaremos a estos objetos en una lista llamada ISSUES_LIST
Step4:  Pediremos todas las issues e iremos guardando los objetos correspondientes
Step5:  Ya tenemos nuestra lista de issues. Ahora, veamos cuales son las labels que se han usado
Step6:  Empecemos con los gráficos. Para esto usaremos matplotlib
Step7:  Vamos a desplegar un grafo donde cada nodo es un label y cada arista representa las uniones de labels (cuando una issue tiene más de un label). El tamaño de los nodos dependerá de la cantidad de issues etiquetadas con aquel label y el grosor de las aristas dependerá de la cantidad de issues en la que ambos labels estuvieron. Si en alguna issue hubo mas de dos etiquetas, consideraremos los pares de labels y no un conjunto.
Para esto, necesitamos
Step8:  Para la siguiente sección usaremos nextworkx para graficar la red de nodos
<ASSISTANT_TASK:>
Python Code:
# Primero tenemos que importar las librerias que usaremos para recopilar datos
import base64
import json
import requests
# Si queremos imprimir los json de respuesta 
# de una forma mas agradable a la vista podemos usar
def print_pretty(jsonstring, indent=4, sort_keys=False):
    print(json.dumps(jsonstring, indent=indent, sort_keys=sort_keys))
    
# Importamos nuestras credenciales
with open("credentials") as f:
    credentials = tuple(f.read().splitlines())
    
# Constantes que usaremos
OWNER = "IIC2233-2016-1"
REPO = "syllabus"
Explanation: GitHub API v3 - Uso de labels
Para los siguientes ejercicios usaremos el repositorio del curso Programación Avanzada de la Pontificia Universidad Católica de Chile para el segundo semestre del 2015. Ve el repo aqui.
Una herramienta interesante para saber el comportamiento de un repositorio es buscar la cantidad de issues (abiertas + cerradas) para cada label disponible:
End of explanation
url = "https://api.github.com/repos/{}/{}/issues".format(OWNER, REPO)
params = {
    "state": "closed",
    "sort": "created",
    "direction": "asc",
    "page": 1
}
req = requests.get(url, params=params, auth=credentials)
print(req.status_code)
# Para obtener el json asociado: req.json()
# Pero queremos imprimirlo de una manera mas legible
for issue in req.json():
    print(issue["number"], issue["title"])
Explanation: Antes que todo, debemos preguntarnos: ¿a qué queremos acceso? Así podemos definir los scopes que necesitamos en nuestro token y también podremos facilitar nuestro flujo de trabajo
Obtener issues
Obtener labels
Guardar cantidades para cada label
Graficar (opcional)
Para hacer esto usaremos la sección de la API de GitHub para issues. Para obtener las issues de un repositorio...
GET /repos/:owner/:repo/issues
End of explanation
# Si esta fuese la ultima pagina no existiria `rel="next"`
print(dict(req.headers)["Link"])
print("¿Es la ultima pagina? {}".format("No" if 'rel="next"' in dict(req.headers)["Link"] else "Si"))
Explanation: Como podemos notar, es posible que tengamos más de una página de issues, por lo que tendremos que acceder al header y obtener la url a la siguiente página, repitiendo la operación hasta llegar a la última. Para facilitar esto, podemos agregar el parametro page a la url indicando el número de la página que queremos. ¿Cómo sabremos cuándo detenernos? Cuando en el header, en Link, no exista una url a la siguiente página ("next")
End of explanation
class Issue:
    
    def __init__(self, number, title, labels):
        self.number = number
        self.title = title
        self.labels = labels
        
def labels(labels_json):
    return [label_item["name"] for label_item in labels_json]
ISSUES_LIST = list()
Explanation: Guardemos la información que nos interesa en objetos de una clase Issue, extraeremos los nombres de las labels con la funcion labels y guardaremos a estos objetos en una lista llamada ISSUES_LIST
End of explanation
url = "https://api.github.com/repos/{}/{}/issues".format(OWNER, REPO)
params = {
    "state": "closed",
    "sort": "created",
    "direction": "asc",
    "page": 1
}
# Primera pagina
req = requests.get(url, params=params, auth=credentials)
for issue in req.json():
    ISSUES_LIST.append(Issue(issue["number"], issue["title"], labels(issue["labels"])))
# Paginas siguientes
while 'rel="next"' in dict(req.headers)["Link"]:
    print("Page: {} ready".format(params["page"]))
    params["page"] += 1
    req = requests.get(url, params=params, auth=credentials)
    for issue in req.json():
        ISSUES_LIST.append(Issue(issue["number"], issue["title"], labels(issue["labels"])))
print("Tenemos {} issues en la lista".format(len(ISSUES_LIST)))
Explanation: Pediremos todas las issues e iremos guardando los objetos correspondientes:
End of explanation
LABELS = list(label for issue in ISSUES_LIST for label in issue.labels)
print(set(LABELS))
Explanation: Ya tenemos nuestra lista de issues. Ahora, veamos cuales son las labels que se han usado
End of explanation
%matplotlib inline
Explanation: Empecemos con los gráficos. Para esto usaremos matplotlib:
End of explanation
# Cantidad de issues por label
LABEL_ISSUES = {label: LABELS.count(label) for label in LABELS}
# Imprimiendo de mayor a menor numero de issues...
print("Top {}".format(min(5, len(LABEL_ISSUES))))
for v, k in sorted(((v,k) for k,v in LABEL_ISSUES.items()), reverse=True)[:min(5, len(LABEL_ISSUES))]:
    print("{}: {}".format(k, v))
# Todos los pares de labels
from itertools import combinations
LABEL_PAIRS = dict()
PAIRS = list()
for issue in ISSUES_LIST:
    combs = combinations(issue.labels, 2)
    for pair in combs:
        k = "{} + {}".format(*pair)
        if k not in LABEL_PAIRS:
            LABEL_PAIRS[k] = 0
            PAIRS.append(pair)
        LABEL_PAIRS[k] += 1
        
# Imprimiendo de mayor a menor numero de issues
print("Top {}".format(min(5, len(LABEL_PAIRS))))
for v, k in sorted(((v,k) for k,v in LABEL_PAIRS.items()), reverse=True)[:min(5, len(LABEL_PAIRS))]:
    print("{}: {}".format(k, v))
Explanation: Vamos a desplegar un grafo donde cada nodo es un label y cada arista representa las uniones de labels (cuando una issue tiene más de un label). El tamaño de los nodos dependerá de la cantidad de issues etiquetadas con aquel label y el grosor de las aristas dependerá de la cantidad de issues en la que ambos labels estuvieron. Si en alguna issue hubo mas de dos etiquetas, consideraremos los pares de labels y no un conjunto.
Para esto, necesitamos:
End of explanation
from pylab import rcParams
rcParams['figure.figsize'] = 8, 8
import matplotlib.pylab as plt
import networkx as nx
# Crear grafo
G = nx.Graph()
# Max peso
mp = LABEL_PAIRS[max(LABEL_PAIRS, key=LABEL_PAIRS.get)]
# Crear nodos
G.add_nodes_from(LABEL_ISSUES.keys())
# Crear arcos
for o_pair in LABEL_PAIRS.keys():
    pair = o_pair.split(' + ')
    G.add_edge(*pair, 
               weight=mp-LABEL_PAIRS[o_pair],
               width=mp-LABEL_PAIRS[o_pair])
    
# Asignar color
n_colors = list()
for node in G.nodes():
    color = 'white'
    if 'Tarea' in node:
        color = 'blue'
    elif node in ['Tengo un error', 'Setup']:
        color = 'purple'
    elif node in ['Actividades', 'Ayudantía', 'Ayudantia', 'Interrogación', 'Interrogacion', 'Materia', 'Material']:
        color = 'green'
    elif node in ['Código', 'Codigo', 'Git']:
        color = 'yellow'
    elif node in ['Duplicada', 'Invalida']:
        color = 'grey'
    elif node in ['IMPORTANTE']:
        color = 'red'
    n_colors.append(color)
    
# Asignar tamaños de nodos
sizes = list()
for node in G.nodes():
    sizes.append(20 * LABEL_ISSUES[node])
    
# Asignar grosores de aarcos
average = round(sum(LABEL_PAIRS.values()) / (0.75 * len(LABEL_PAIRS)))
styles = list()
widths = list()
for edge in G.edges():
    k = "{} + {}".format(edge[0], edge[1])
    if k not in LABEL_PAIRS:
        k = "{} + {}".format(edge[1], edge[0])
    if LABEL_PAIRS[k] < average:
        styles.append('dashed')
        widths.append(LABEL_PAIRS[k] + average)
    else:
        styles.append('solid')
        widths.append(LABEL_PAIRS[k] + 1)
    
# Colores de arcos
e_colors = list(i + 10 for i in range(len(G.edges())))
    
# Desplegar graficos
nx.draw(G,        
        edge_color=e_colors,
        edge_cmap=plt.cm.Blues,
        node_color=n_colors,
        node_size=sizes,
        style=styles,
        width=widths,
        with_labels=True)
nx.draw_circular(G,        
        edge_color=e_colors,
        edge_cmap=plt.cm.Blues,
        node_color=n_colors,
        node_size=sizes,
        style=styles,
        width=widths,
        with_labels=True)
Explanation: Para la siguiente sección usaremos nextworkx para graficar la red de nodos:
End of explanation
<END_TASK>