184 lines
5.7 KiB
Python
184 lines
5.7 KiB
Python
|
import unicodedata
|
||
|
import urllib
|
||
|
from collections import namedtuple as nt
|
||
|
from dataclasses import dataclass
|
||
|
from json import loads
|
||
|
import logging
|
||
|
|
||
|
import pygal
|
||
|
import pyrqlite.dbapi2 as dbapi2
|
||
|
import requests
|
||
|
|
||
|
connection = dbapi2.connect(
|
||
|
host="10.61.0.1",
|
||
|
user="root",
|
||
|
port=4001,
|
||
|
password="",
|
||
|
)
|
||
|
|
||
|
|
||
|
def remove_accents(input_str):
|
||
|
nfkd_form = unicodedata.normalize("NFKD", input_str)
|
||
|
return "".join([c for c in nfkd_form if not unicodedata.combining(c)])
|
||
|
|
||
|
|
||
|
@dataclass
|
||
|
class Género:
|
||
|
nombre: str = ""
|
||
|
masculinidad: float = 0
|
||
|
|
||
|
|
||
|
def split_por_genero(nombres):
|
||
|
no_clasificados = set()
|
||
|
# Veamos cuales de estos nombres ya están clasificados
|
||
|
for nombre in nombres:
|
||
|
clasificador = remove_accents(
|
||
|
nombre.nombre.split()[0]
|
||
|
) # genderize no aprecia acentos para AR
|
||
|
genero = Género.get(nombre=clasificador)
|
||
|
if not genero:
|
||
|
# No está clasificado
|
||
|
no_clasificados.add(urllib.parse.quote(clasificador))
|
||
|
|
||
|
if no_clasificados:
|
||
|
print(f"Tengo {len(no_clasificados)} sin clasificar")
|
||
|
no_clasificados = list(no_clasificados)
|
||
|
# Averiguar los no clasificados
|
||
|
# Partimos en bloques de a 10 (API de genderize)
|
||
|
for i in range(len(no_clasificados) // 10 + 1):
|
||
|
chunk = no_clasificados[i * 10 : (i + 1) * 10]
|
||
|
url = f'https://api.genderize.io/?name[]={"&name[]=".join(chunk)}&country_id=AR'
|
||
|
clasificados = requests.get(url)
|
||
|
for resultado in clasificados.json():
|
||
|
if not resultado["name"]:
|
||
|
continue # No me importa
|
||
|
if resultado["gender"] == "male":
|
||
|
masc = resultado["probability"]
|
||
|
elif resultado["gender"] == "female":
|
||
|
masc = 1 - resultado["probability"]
|
||
|
else:
|
||
|
# Probablemente un acento o algo así
|
||
|
print(f"Raro:{resultado}")
|
||
|
masc = None
|
||
|
# Metemos en la base
|
||
|
print(f"Clasificando {resultado}: {masc}")
|
||
|
Género(nombre=resultado["name"], masculinidad=masc)
|
||
|
|
||
|
nombres_f = []
|
||
|
nombres_m = []
|
||
|
for nombre in nombres:
|
||
|
clasificador = remove_accents(nombre.nombre.split()[0])
|
||
|
genero = Género.get(nombre=clasificador)
|
||
|
if not genero or genero.masculinidad is None: # No clasificado, en ambos
|
||
|
nombres_f.append(nombre)
|
||
|
nombres_m.append(nombre)
|
||
|
elif 0.4 < genero.masculinidad:
|
||
|
nombres_m.append(nombre)
|
||
|
elif 0.6 > genero.masculinidad:
|
||
|
nombres_f.append(nombre)
|
||
|
return {"f": nombres_f, "m": nombres_m}
|
||
|
|
||
|
|
||
|
def handle(req):
|
||
|
"""handle a request to the function
|
||
|
Args:
|
||
|
req (str): request body
|
||
|
|
||
|
{
|
||
|
p: prefijo del nombre,
|
||
|
g: genero del nombre,
|
||
|
a: año de nacimiento
|
||
|
}
|
||
|
|
||
|
"""
|
||
|
|
||
|
|
||
|
try:
|
||
|
data = loads(req)
|
||
|
except Exception as e:
|
||
|
data = {}
|
||
|
|
||
|
try:
|
||
|
prefijo = data.get("p") or None
|
||
|
genero = data.get("g") or None
|
||
|
try:
|
||
|
año = int(data.get("a"))
|
||
|
except Exception:
|
||
|
año = None
|
||
|
except Exception as e:
|
||
|
prefijo = genero = año = None
|
||
|
|
||
|
if prefijo is not None:
|
||
|
prefijo = prefijo.strip().lower()
|
||
|
|
||
|
|
||
|
if genero not in ("f", "m"):
|
||
|
genero = None
|
||
|
|
||
|
if prefijo is None and año is None: # Totales globales
|
||
|
with connection.cursor() as cursor:
|
||
|
sql = """
|
||
|
SELECT total, nombre
|
||
|
FROM totales
|
||
|
ORDER BY total DESC
|
||
|
LIMIT 50
|
||
|
"""
|
||
|
cursor.execute(sql)
|
||
|
datos = [(r["total"], r["nombre"]) for r in cursor.fetchall()]
|
||
|
|
||
|
elif prefijo is None and año is not None: # Totales por año
|
||
|
with connection.cursor() as cursor:
|
||
|
sql = """
|
||
|
SELECT contador, nombre
|
||
|
FROM nombres
|
||
|
WHERE
|
||
|
anio = :anio
|
||
|
ORDER BY contador DESC
|
||
|
LIMIT 50
|
||
|
"""
|
||
|
cursor.execute(sql, {'anio': año})
|
||
|
datos = [(r["contador"], r["nombre"]) for r in cursor.fetchall()]
|
||
|
|
||
|
elif prefijo is not None and año is None:
|
||
|
with connection.cursor() as cursor:
|
||
|
sql = """
|
||
|
SELECT contador, nombre
|
||
|
FROM nombres
|
||
|
WHERE
|
||
|
nombre LIKE :nombre
|
||
|
ORDER BY contador DESC
|
||
|
LIMIT 50
|
||
|
"""
|
||
|
cursor.execute(sql, {"nombre": "{prefijo}%"})
|
||
|
datos = [(r["contador"], r["nombre"]) for r in cursor.fetchall()]
|
||
|
else:
|
||
|
with connection.cursor() as cursor:
|
||
|
sql = """
|
||
|
SELECT contador, nombre
|
||
|
FROM nombres
|
||
|
WHERE
|
||
|
anio = :anio AND
|
||
|
nombre LIKE :nombre
|
||
|
ORDER BY contador DESC
|
||
|
LIMIT 50
|
||
|
"""
|
||
|
cursor.execute(sql, {"anio": año, "nombre": "{prefijo}%"})
|
||
|
datos = [(r["contador"], r["nombre"]) for r in cursor.fetchall()]
|
||
|
|
||
|
if genero:
|
||
|
datos = split_por_genero(datos)[genero]
|
||
|
|
||
|
datos = datos[:10]
|
||
|
|
||
|
chart = pygal.HorizontalBar(height=400, show_legend=False, show_y_labels=True)
|
||
|
chart.x_labels = [nombre.title() for _, nombre in datos[::-1]]
|
||
|
if len(datos) > 1:
|
||
|
chart.title = f"¿Puede ser ... {datos[0][1].title()}? ¿O capaz que {datos[1][1].title()}? ¡Contáme más!"
|
||
|
elif len(datos) == 1:
|
||
|
chart.title = f"¡Hola {datos[0][1].title()}!"
|
||
|
elif len(datos) < 1:
|
||
|
chart.title = "¡No esssistís!"
|
||
|
chart.add("", [contador for contador, _ in datos[::-1]])
|
||
|
|
||
|
return chart.render(is_unicode=True), 200, {"Content-Typei": "image/svg+xml"}
|