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"}