Utilizador:LipeFontoura/usuários.py

Origem: Wikcionário, o dicionário livre.
# -*- coding: utf8 -*-

# criado por LipeFontoura

import urllib
import datetime
import time
import math
import re

# dados de entrada
tamanho_total = 0

minimo_edicoes_mensais = 10
url_lista_usuarios = "http://pt.wiktionary.org/wiki/Wikcion%C3%A1rio:Lista_de_wikcionaristas_por_n%C3%BAmero_de_edi%C3%A7%C3%B5es"

# remover
img_aumento = u"<span style=\"color: #0c0; font-size: larger;\">▲</span>"
img_parado = u"[[Imagem:Straight Line Steady.svg|10px]]"
img_reducao = u"<span style=\"color: red; font-size: larger;\">▼</span>"

# obtem o dia atual
data_atual = datetime.datetime.utcnow()
str_data = data_atual.strftime("%d/%m/%Y")

# declara as constantes
usuario = 0
administrador = 1
burocrata = 2

total = 0
validas = 1
deletadas = 2

nomes_tipo = [u"Usuário", u"Administrador", u"Burocrata"]
 
prefixo = [u"",u"'''",u"'''"]
sufixo = [u"]]",u"]]'''",u" (###)]]'''"]

# funções de ordenação
primeiro = -1
igual = 0
segundo = 1
def ordenador_total(usuario1, usuario2) :
    if (usuario1.edicoes[total] == usuario2.edicoes[total]) : return igual
    if (usuario1.edicoes[total] > usuario2.edicoes[total]) : return primeiro
    else: return segundo
def ordenador_validas(usuario1, usuario2) :
    if (usuario1.edicoes[validas] == usuario2.edicoes[validas]) : return igual
    if (usuario1.edicoes[validas] > usuario2.edicoes[validas]) : return primeiro
    else: return segundo
def ordenador_primeira_edicao(usuario1, usuario2) :
    if (usuario1.primeira_edicao.toordinal() == usuario2.primeira_edicao.toordinal()) : return igual
    if (usuario1.primeira_edicao.toordinal() > usuario2.primeira_edicao.toordinal()) : return primeiro
    else: return segundo
ordenadores = [ordenador_total, ordenador_validas, ordenador_primeira_edicao]

# declara os nomes dos meses
meses = {u"janeiro":1, u"fevereiro":2, u"março":3, u"abril":4, u"maio":5, u"junho":6, u"julho":7,
         u"agosto":8, u"setembro":9, u"outubro":10, u"novembro":11, u"dezembro":12}

# funções com operações regulares
def re_prim(padrao, cadeia_caracteres) :
    u"""Retorna a primeira correspondência de uma operação regular."""
    resultado_re_prim = re.search(padrao, cadeia_caracteres, re.MULTILINE | re.DOTALL)
    if (resultado_re_prim) : resultado_re_prim = resultado_re_prim.group()
    return resultado_re_prim

def re_tudo(padrao, cadeia_caracteres) :
    u"""Retorna uma lista com todas as correspondências de uma operação regular."""
    return re.findall(padrao, cadeia_caracteres, re.MULTILINE | re.DOTALL)

# funções de data
def obter_data(string) : # [0-9][0-9]h[0-9][0-9]min de [0-9][0-9]? de [^ ]* de [0-9][0-9][0-9][0-9]
    u"""Converte uma string com uma data num objeto de data."""
    return datetime.datetime(year=int(re_prim(u"[0-9][0-9][0-9][0-9]", string)),
                             month=meses[re_prim(u"(?<=de )[^ ]*(?= de [0-9][0-9][0-9][0-9])", string)],
                             day=int(re_prim(u"(?<=min de )[0-9][0-9]?", string)),
                             hour=int(re.search(u"[0-9][0-9](?=h)", string).group()),
                             minute=int(re.search(u"[0-9][0-9](?=min)", string).group()))


# classe que conecta-se à internet
class Navegador(urllib.FancyURLopener) :
    version = "Linux Mozilla"
urllib._urlopener = Navegador()

# comando que baixa o conteúdo de uma página
def baixar_pagina(nome) :
    continuar = True
    tentativas = 1
    max_tentativas = 10
    while continuar :
        try :
            acesso = urllib.urlopen(nome)
            conteudo = acesso.read()
            acesso.close()
            continuar = False
        except IOError :
            print u"Houve um erro ao conectar-se a <" + nome + u">. Tentando novamente..."
            tentativas += 1
        if tentativas > max_tentativas : continuar = False
    return unicode(str(conteudo), "UTF-8")

# classe que registra informações sobre um usuário
class Usuario :
    def __init__(self, nome, link, tipo = usuario, observacao2 = None, observacao4 = None, observacao5 = None, observacao6 = None, posicao_anterior = -1, data_anterior = str_data) :
        u"""Inicializa um novo usuário."""
        self.nome = nome
        self.link = link
        self.observacao1 = None
        self.observacao2 = observacao2
        self.observacao4 = observacao4
        self.observacao5 = observacao5
        self.observacao6 = observacao6
        self.tipo = tipo
        self.edicoes = [0, 0, 0]
        self.posicao_anterior = posicao_anterior
        self.data_anterior = data_anterior
    
    def carregar_dados(self) :
        u"""Carrega dados sobre o usuário, como o total de edições, a sua atividade e sua data de registro."""
        # verifica se o usuário está ativo
        temp = baixar_pagina(u"http://pt.wiktionary.org/w/index.php?title=Especial:Contribui%C3%A7%C3%B5es&limit=" + unicode(minimo_edicoes_mensais) + "&target=" + self.link)
        temp = re_tudo(u"(?<=\">)[0-2][0-9]h[0-5][0-9]min de [1-9][0-9]? de [^ ]* de [0-9][0-9][0-9][0-9](?=</a> \\(<a href\\=\"/w/index.php\\?title\\=)", temp)
        mais_recente = obter_data(temp[0])
        menos_recente = obter_data(temp[minimo_edicoes_mensais - 1])
        del temp
        diferenca_menos_recente = data_atual - menos_recente
        diferenca_mais_recente = data_atual - mais_recente
        if (diferenca_menos_recente.days > 31) :
            self.observacao1 = u"(1)<ref>[[Usuário:" + self.nome + u"|" + self.nome + u"]] "
            if (diferenca_mais_recente.days < 31) :
                # o usuario esta inativo mas contribuiu ultimamente
                dif_meses = int(math.floor(diferenca_menos_recente.days / 30))
                if (dif_meses == 1) : str_meses = u"no último mês"
                else :
                    dif_anos = int(math.floor(diferenca_menos_recente.days / 356))
                    dif_meses = int(math.floor((diferenca_menos_recente.days - 356*dif_anos) / 30))
                    if (dif_anos == 0) : str_meses = u"nos últimos " + unicode(dif_meses) + u" meses"
                    if (dif_anos == 1) :
                        str_meses = u"no último ano"
                        if (dif_meses == 1) : str_meses += u" e um mês"
                        if (dif_meses > 1) : str_meses += u" e " + unicode(dif_meses) + u" meses"
                    if (dif_anos > 1) :
                        str_meses = u"nos últimos " + unicode(dif_anos) + u" anos"
                        if (dif_meses == 1) : str_meses += u" e um mês"
                        if (dif_meses > 1) : str_meses += u" e " + unicode(dif_meses) + u" meses"
                self.observacao1 += u"contribuiu pela última vez em " + unicode(mais_recente.strftime("%d/%m/%Y")) + u", mas não chegou a completar " + unicode(minimo_edicoes_mensais) + u" edições " + str_meses + u".</ref>"
            else :
                # o usuário está completamente inativo
                self.observacao1 += u"não contribui desde " + unicode(mais_recente.strftime("%d/%m/%Y")) + u".</ref>"
        else :
            # o usuário está ativo
            self.observacao1 = None
        # verifica o total de edições do usuário
        temp = baixar_pagina("http://tools.wikimedia.de/~interiot/cgi-bin/count_edits?user=" + self.link + "&dbname=ptwiktionary_p")
        self.edicoes[validas] = int(re_prim("(?<=<tr><th>Total edits</th><td colspan\\='2'>)[1-9][0-9]*", temp))
        self.edicoes[deletadas] = int(re_prim("(?<=<tr><th>Deleted edits</th><td colspan\\='2'>)[0-9]*", temp))
        self.edicoes[total] = self.edicoes[validas] + self.edicoes[deletadas]
        temp = re.search("(?<=<tr><th>First edit</th><td colspan='2'>)[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]", temp).group()
        self.primeira_edicao = datetime.date(day   = int(re_prim("(?<=[0-9][0-9][0-9][0-9]/[0-9][0-9]/)[0-9][0-9]", temp)),
                                             month = int(re_prim("(?<=[0-9][0-9][0-9][0-9]/)[0-9][0-9](?=/[0-9][0-9])", temp)),
                                             year  = int(re_prim("[0-9][0-9][0-9][0-9](?=/[0-9][0-9]/[0-9][0-9])", temp)))
        # cria a string com as observações
        obs_string = ""
        observacoes = [self.observacao1, self.observacao2, self.observacao4, self.observacao5, self.observacao6]
        for observacao in observacoes :
            if observacao :
                if len(obs_string) > 0 : obs_string += u", "
                obs_string += observacao
        if len(obs_string) == 0 : obs_string = "-"
        self.obs_string = obs_string
    def imprimir(self, posicao) :
        comparador = img_parado + u" <small>(0)</small>"
        if (self.posicao_anterior == -1) : self.posicao_anterior = tamanho_total + 1
        if (self.posicao_anterior) :
            if (posicao < self.posicao_anterior) : comparador = img_aumento + u"<small> (+" + unicode(self.posicao_anterior - posicao) + ")</small>"
            if (posicao > self.posicao_anterior) : comparador = img_reducao + u"<small> (–" + unicode(posicao - self.posicao_anterior) + ")</small>"
        else :
            comparador = img_aumento + u"<small> (+" + unicode(posicao) + ")</small>"
        print u"| align=\"center\" | " + comparador
        print u"| " + prefixo[self.tipo] + u"[[Usuário:" + self.nome + u"|" + self.nome + sufixo[self.tipo] + u" "
        print "| " + str(self.edicoes[total]) + " <small>(" + str(self.edicoes[validas]) + " + " + str(self.edicoes[deletadas]) + ")</small>"
        print "| " + self.primeira_edicao.strftime("%d/%m/%Y")
        print "| [http://tools.wikimedia.de/~interiot/cgi-bin/count_edits?user=" + self.link + "&dbname=ptwiktionary_p " + str_data + "]"
        print "| " + self.obs_string
        print "|-"

# classe que salva os dados de usuários e marcadores
class Dados :
    def __init__(self) :
        self.usuarios = []
        self.marcadores = []
    def adicionar_marcador(self, marcador) :
        self.marcadores.append(int(marcador))
    def adicionar_usuario(self, usuario) :
        self.usuarios.append(usuario)

# carrega os usuários e os marcadores a usar
def carregar_dados() :
    dados = Dados()
    # carrega a página com os usuários por número de edições
    print u"Carregando lista de usuários e marcadores numéricos..."
    conteudo = baixar_pagina(url_lista_usuarios)
    # acha os estamentos com cada um dos usuário e com cada um dos marcadores
    usuarios_html = re_tudo(u"<td align\\=\"center\">[1-9][0-9]*</td>.*?(?=</tr>)", conteudo)
    marcadores_html = re_tudo(u"<td colspan\\=\"7\" align\\=\"center\"[^>]*><b>\\+[1-9][0-9]* edições</b></td>", conteudo)
    # lê e interpreta os marcadores
    for marcador_html in marcadores_html :
        marcador = int(re_prim("(?<=<b>)\\+[1-9][0-9]*", marcador_html))
        print u"Marcador " + unicode(marcador) + u" encontrado."
        dados.adicionar_marcador(marcador)
    # lê e interpreta os usuários
    linhas_ocupadas_pela_posicao = 1
    posicao_anterior = 1
    # processa os usuários
    for usuario_html in usuarios_html :
        # separa o código HTML do usuário em linhas
        str_linhas = re_tudo(u"<td[^>]*>.*?</td>", usuario_html)
        soma = 1
        if linhas_ocupadas_pela_posicao <= 1 :
            linhas_ocupadas_pela_posicao = 1
            posicao_anterior = int(re_prim("[1-9][0-9]*(?=</td>)", str_linhas[0]))
            temp = re_prim(str_linhas[0], u"(?<=rowspan\\=)(\"[1-9][0-9]*\"|'[1-9][0-9]*'|[1-9][0-9]*)")
            if temp : linhas_ocupadas_pela_posicao = int(re_prim(u"[1-9][0-9]*", temp))
        else :
            linhas_ocupadas_pela_posicao -= 1
            soma = 0
        # obtém o nome e o tipo do usuário
        nome = re_prim(u"[^>]*(?= \\(###\\)</a></b>)", str_linhas[soma + 1])
        if (nome) : tipo = burocrata
        else :
            nome = re_prim(u"[^>]*(?=</a></b>)", str_linhas[soma + 1])
            if (nome) : tipo = administrador
            else :
                tipo = usuario
                nome = re_prim(u"[^>]*(?=</a>)", str_linhas[soma + 1])
        # procura o link e a data
        link = re_prim("(?<=interiot/cgi-bin/count_edits\\?user\\=)[^&]*(?=\\&dbname=ptwiktionary_p)", str_linhas[soma + 4])
        data_anterior = re_prim("(?<=rel\\=\"nofollow\">)[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9](?=</a>)", str_linhas[soma + 4])
        # obtém as observações sobre o usuário
        observacao2 = re_prim(u"\\(2\\) [^<\\,]*", str_linhas[soma + 5])
        observacao4 = re_prim(u"\\(4\\) [^<\\,]*", str_linhas[soma + 5])
        observacao5 = re_prim(u"(?<=title\\=\").*?(?=\" rel\\=\"nofollow\">\\(5\\)</a>)", str_linhas[soma + 5])
        observacao6 = re_prim(u"(?<=title\\=\").*?(?=\" rel\\=\"nofollow\">\\(6\\)</a>)", str_linhas[soma + 5])
        if (observacao5) : observacao5 = u"[" + observacao5 + u" (5)]"
        if (observacao6) : observacao6 = u"[" + observacao6 + u" (6)]"
        print u"O usuário " + nome + u", que estava na posição " + unicode(posicao_anterior) + u" no dia " + data_anterior + u", foi encontrado."
        dados.adicionar_usuario(Usuario(nome = nome,
                                        link = link,
                                        tipo = tipo,
                                        observacao2 = observacao2,
                                        observacao4 = observacao4,
                                        observacao5 = observacao5,
                                        observacao6 = observacao6,
                                        posicao_anterior = posicao_anterior,
                                        data_anterior = data_anterior))
    return dados

# obtem a lista de usuarios
dados = carregar_dados()
tamanho_total = len(dados.usuarios)

print ""
print u"Começando a carregar os dados..."
for usuario in dados.usuarios :
    usuario.carregar_dados()
    str_tipo = u"Usuário "
    if (usuario.tipo == administrador) : str_tipo = u"Administrador "
    if (usuario.tipo == burocrata) : str_tipo = u"Burocrata "
    print str_tipo + usuario.nome + u" carregado. Realizou " + unicode(usuario.edicoes[total]) + u" (" + unicode(usuario.edicoes[validas]) + u" + " + unicode(usuario.edicoes[deletadas]) + u") edições."
    time.sleep(0.5)

usuarios = dados.usuarios
marcadores = dados.marcadores
marcadores.append(0)
indice = 1

while len(usuarios) > 0:
    selecionados = usuarios
    for ordenador in ordenadores :
        selecionados_anterior = selecionados
        selecionados = None
        for usuario in selecionados_anterior :
            if selecionados :
                comparacao = ordenador(selecionados[0], usuario)
                if (comparacao == igual)    : selecionados.append(usuario)
                if (comparacao == segundo)  : selecionados = [usuario]
            else : selecionados = [usuario]
    # imprime o marcador
    temp = selecionados[0].edicoes[total]
    if (temp < marcadores[0]) :
        while (temp < marcadores[0]) :
            marcadores.remove(marcadores[0])
        print u"| colspan=\"7\" align=\"center\" bgcolor=\"#dcdcdc\" | '''+" + unicode(marcadores[0]) + u" edições'''"
        print "|-"
    # imprime o número
    if len(selecionados) == 1 : print "| align=\"center\" | " + str(indice)
    else : print "| align=\"center\" rowspan\"" + str(len(selecionados)) + "\" | " + str(indice)
    for selecionado in selecionados :
        usuarios.remove(selecionado)
        selecionado.imprimir(indice)
    indice += len(selecionados)