Separar nombre completo PHP

En un archivo que mandaron venían nombres de personas en una única columna. Algo así:

Mario Andrés Cares Cabezas

Pero nosotros en la BD tenemos el nombre separado en 3 columnas.

Entonces, hice lo que cualquier programador haría … convertir el tema. Entonces, hice una función que haga la pega.

  1. Crear un array con las excepciones. Básicamente palabras claves en nombres compuestos: “Del Carmen”, “San Martín”, etc.
  2. Dividir el string inicial cortando por “espacios”. Invertirlo
  3. Iterar por cada trocito e ir rellenando un array con los campos.
  4. Indicar que el primer trocito que leamos tiene que ir en el apellido materno.
  5. Limpiar por si quedaron espacios en blanco.

En otras palabras, esto:

array_reverse(explode(" ", strtoupper($nombreCompleto)));
$exceptions = ["DE", "LA", "DEL", "LOS", "SAN", "SANTA"];
$nombre = array( "Materno" => "", "Paterno" => "", "Nombres" => "" );
$agregar_en = "materno";
foreach($chunks as $chunk){
    if($primera_vez){
        $nombre["Materno"] = $chunk . " " . $nombre["Materno"];
        $primera_vez = false;
    }else{
        if(in_array($chunk, $exceptions)){
            if($agregar_en == "materno")
                $nombre["Materno"] = $chunk . " " . $nombre["Materno"];
            elseif($agregar_en == "paterno")
                $nombre["Paterno"] = $chunk . " " . $nombre["Paterno"];
            else
                $nombre["Nombres"] = $chunk . " " . $nombre["Nombres"];
        }else{
            if($agregar_en == "materno"){
                $agregar_en = "paterno";
                $nombre["Paterno"] = $chunk . " " . $nombre["Paterno"];
            }elseif($agregar_en == "paterno"){
                $agregar_en = "nombres";
                $nombre["Nombres"] = $chunk . " " . $nombre["Nombres"];
            }else{
                $nombre["Nombres"] = $chunk . " " . $nombre["Nombres"];
            }
        }
    }
}
$nombre["Materno"] = trim($nombre["Materno"]);
$nombre["Paterno"] = trim($nombre["Paterno"]);
$nombre["Nombres"] = trim($nombre["Nombres"]);

Y eso era suficiente… obtenía algo así

Array
(
    [Materno] => Cabezas
    [Paterno] => Cares
    [Nombres] => Mario Andrés
)

Funciona como debería … el problema llegó cuando apareció un nombre con este formato:

Cares Cabezas Mario Andrés

Aquí tuve que cambiar el algoritmo de arriba:

  1. Agregar un parámetro a la función que me diga si el nombre viene dado vuelta o no. Como es un string, el script no tiene como saber si el trocito que viene es un nombre o un apellido, así que este parámetro es responsabilidad del usuario (o módulo) desde donde se llama a la función.
  2. En base a ese parámetro, si empieza por el apellido, entonces no invierto los trocitos.
  3. En vez de empezar rellenando el apellido Materno, empiezo por el Paterno.
  4. Si no lo invierto, tengo que invertir el orden de ingreso de trocitos a determinado campo. Entonces, primero reviso si alguno de los trocitos es parte de un apellido o nombre compuesto. En base a eso cambio el orden de ingreso.

Resumiendo, quedó así:

public static function getNombreSplit($nombreCompleto, $apellido_primero = false){
    $chunks = ($apellido_primero)
        ? explode(" ", strtoupper($nombreCompleto))
        : array_reverse(explode(" ", strtoupper($nombreCompleto)));
    $exceptions = ["DE", "LA", "DEL", "LOS", "SAN", "SANTA"];
    $existen = array_intersect($chunks, $exceptions);
    $nombre = array( "Materno" => "", "Paterno" => "", "Nombres" => "" );
    $agregar_en = ($apellido_primero)
        ? "paterno"
        : "materno";
    $primera_vez = true;
    if($apellido_primero){
        if(!empty($existen)){
            foreach ($chunks as $chunk) {
                if($primera_vez){
                    $nombre["Paterno"] = $nombre["Paterno"] . " " . $chunk;
                    $primera_vez = false;
                }else{
                    if(in_array($chunk, $exceptions)){
                        if($agregar_en == "paterno")
                            $nombre["Paterno"] = $nombre["Paterno"] . " " . $chunk;
                        elseif($agregar_en == "materno")
                            $nombre["Materno"] = $nombre["Materno"] . " " . $chunk;
                        else
                            $nombre["Nombres"] = $nombre["Nombres"] . " " . $chunk;
                    }else{
                        if($agregar_en == "paterno"){
                            $nombre["Paterno"] = $nombre["Paterno"] . " " . $chunk;
                            $agregar_en = "materno";
                        }elseif($agregar_en == "materno"){
                            $nombre["Materno"] = $nombre["Materno"] . " " . $chunk;
                            $agregar_en = "nombres";
                        }else{
                            $nombre["Nombres"] = $nombre["Nombres"] . " " . $chunk;
                        }
                    }
                }
            }
        }else{
            foreach ($chunks as $chunk) {
                if($primera_vez){
                    $nombre["Paterno"] = $nombre["Paterno"] . " " . $chunk;
                    $primera_vez = false;
                }else{
                    if(in_array($chunk, $exceptions)){
                        if($agregar_en == "paterno")
                            $nombre["Paterno"] = $nombre["Paterno"] . " " . $chunk;
                        elseif($agregar_en == "materno")
                            $nombre["Materno"] = $nombre["Materno"] . " " . $chunk;
                        else
                            $nombre["Nombres"] = $nombre["Nombres"] . " " . $chunk;
                    }else{
                        if($agregar_en == "paterno"){
                            $nombre["Materno"] = $nombre["Materno"] . " " . $chunk;
                            $agregar_en = "materno";
                        }elseif($agregar_en == "materno"){
                            $nombre["Nombres"] = $nombre["Nombres"] . " " . $chunk;
                            $agregar_en = "nombres";
                        }else{
                            $nombre["Nombres"] = $nombre["Nombres"] . " " . $chunk;
                        }
                    }
                }
            }
        }
    }else{
        foreach($chunks as $chunk){
            if($primera_vez){
                $nombre["Materno"] = $chunk . " " . $nombre["Materno"];
                $primera_vez = false;
            }else{
                if(in_array($chunk, $exceptions)){
                    if($agregar_en == "materno")
                        $nombre["Materno"] = $chunk . " " . $nombre["Materno"];
                    elseif($agregar_en == "paterno")
                        $nombre["Paterno"] = $chunk . " " . $nombre["Paterno"];
                    else
                        $nombre["Nombres"] = $chunk . " " . $nombre["Nombres"];
                }else{
                    if($agregar_en == "materno"){
                        $agregar_en = "paterno";
                        $nombre["Paterno"] = $chunk . " " . $nombre["Paterno"];
                    }elseif($agregar_en == "paterno"){
                        $agregar_en = "nombres";
                        $nombre["Nombres"] = $chunk . " " . $nombre["Nombres"];
                    }else{
                        $nombre["Nombres"] = $chunk . " " . $nombre["Nombres"];
                    }
                }
            }
        }
    }
    // LIMPIEZA DE ESPACIOS
    $nombre["Materno"] = trim($nombre["Materno"]);
    $nombre["Paterno"] = trim($nombre["Paterno"]);
    $nombre["Nombres"] = trim($nombre["Nombres"]);
    return $nombre;
}

El problema, ahora, es que muchas partes se repiten … y no me gusta como se ve 8O … pero por apuro, se queda así.

Publicado en Uncategorized | Deja un comentario

Los Usuarios (Parte II)

– Mario, puede abrir mi correo… es para mostrarle algo. Usuario X pass Y

(Yo pensando) Y por qué mejor no me llama a verlo en SU escritorio, estoy al lado ¬¬ Ya, abriendo Chrome en ventana privada.

-Baje más, es de ayer. Mmmm un poquito más. Ese, ese es. Mire ábralo. Me mandaron ese archivo raro que no puedo ver…

Era un .eml, de esos que te adjunta outlook cuando el usuario es un trollazo que no es capaz de responder un mail de manera normal.

-Ah si, es un archivo del outlook. Lo descargo y lo abrimos ahora.

-Se supone que es un excel con una planilla.

-A ver. (abriendo archivo). Ya, tiene algo adjunto … pero es un word… no un excel.

-Nooo ella me dijo que era un excel. Seguro que no es un excel ?

-Si. Seguro. Mire, vea la extensión. (era docx). Pero veamos que tiene dentro. Si, mire, es una tabla. Es esa la planilla que le mandaron ?

-Si, tiene la misma forma. Pucha que fome que no la mandaran en excel. Ya no importa. Mándeme el archivo por favor, el que acaba de abrir.

-Ok. Listo.

(A los 10 segundos)

-Mario, me mandaron una imagen. No es una tabla. Puede verlo ?

(Abriendo archivo de nuevo)

-Si verdad (aquí me reí internamente xDD quién en su sano juicio manda una imagen :D ). Es una imagen. Es una screenshot del excel original.

-Qué ?

-Una foto del archivo original, del excel.

-Pucha pero no me sirve. Puede convertirlo a excel?

(Aquí en este punto la risa estaba a punto de hacerse visible)

-No, no puedo convertirlo a excel si es una imagen… Si fuera un PDF podría ser… pero no una imagen.

-Y si convierte la imagen en PDF, podrá después hacerlo excel?

(wajajajajaj)

-No, no se puede… sigue siendo una imagen (esto lo dije con mi cara más seria que podía tener).

-Pucha, bueno. Gracias.

Publicado en Uncategorized | Deja un comentario

¿ Visual Studio Code ?

La mayoría quedamos marcando ocupado cuando supimos que Microsoft tenía un editor para escribir donde fuera.

Así que corrí al Mac, y descargué la aplicación. Al abrirla … mi reacción fue: “Pero si esto es un SublimeText! ” Y si, es parecido. Otros lo pueden encontrar parecido a Atom. Captura de pantalla 2015-04-30 a las 0.55.42

Entonces dije: “ok, hagamos algo. Quiero un sistema chico en C# que corra en el Mac, y ocupando este nuevo editor”… Y ahí me quedé … no encontraba la opción para crear un nuevo proyecto o para configurarlo D:

Resulta que VSCode está orientado a carpetas/archivos, razón por la cual no podemos “crear” un proyecto directamente desde VSCode. Para “hacer” algo, tenemos que recurrir a yeoman para que nos cree un “proyecto. Había escuchado hablar sobre yerman hace un año atrás en la StarTechConf que se realizó en Chile … y nunca más escuché de él … nunca siquiera lo había usado… así que ahora va una pequeña introducción como para quedar bien :D

Yeoman es una herramienta para, a palabras simples, generar el proceso de “iniciar” un proyecto. Crea el esqueleto de carpetas, descarga dependencias, etc. Todo esto usando Bower y Grunt.

Por ejemplo, en mi caso siempre creo 4 carpetas para mis sistemas: asset, model, view, controller. Descargo bootstrap y jQuery en asset, genero las clases en model, y así. Es un proceso que hago siempre y pierdo unos minutos en hacerlo. Si usara Yeoman, sería automágico :D

Para instalar Yeoman, primero necesitamos node y su npm. Una vez que lo tengamos instalado (npm) seguimos con Yeoman, Grunt, Bower y un generador “especial” para apps de asp.net

npm install -g yo grunt-cli generator-aspnet bower

Y eso es todo, ahora podemos crear un directorio/proyecto usando Yeoman:

yo aspnet

Este generador nos da distintas opciones a elegir. Para una app web, marcan obviamente la que indica Captura de pantalla 2015-04-30 a las 1.33.55 y luego de indicar un nombre nos generará todas las carpetas con el árbol correspondiente. Algo así: Captura de pantalla 2015-04-30 a las 1.35.14

Ahora que ya tenemos el “proyecto” saltamos a VSCode y buscamos la carpeta creada. Automágicamente nos reconoce todo … pero, al menos en mi PC, me daba un error al no “entender” los archivos en C# Captura de pantalla 2015-04-30 a las 1.39.02

Dicha herramienta era OmniSharp, quién se encarga de que cualquier editor tenga el autocompletado, resaltado de errores, y virtualmente todas las “ayudas” que Visual Studio te da al escribir en C#. Pero para que ella funcione, necesitas tener instalado “Mono“. Captura de pantalla 2015-04-30 a las 2.02.44

Ahora, abrimos nuevamente VSCode y no hay errores … pero si un warning Captura de pantalla 2015-04-30 a las 2.03.50

Se refiere a DNX, que son herramientas propias de ASP.NET 5 que permiten que las apps creadas funcionen tanto en Windows como en Mac/Linux.

Para instalarlo, nos vamos a Homebrew:

brew tap aspnet/dnx
brew update
brew install dnvm

y una vez descargado, saltamos nuevamente a VSCode y ahora nos mostrará un aviso de que tenemos dependencias que no encuentra y que hagamos un “restore” para solucionar el problema descargando dichas dependencias.Captura de pantalla 2015-04-30 a las 2.40.45.

Desde la consola, en la carpeta creada por yeoman:

dnu restore

Si no encuentra el comando, es porque les faltó hacer esta parte:

Checking connectivity... done.
==> Checking out branch dev
==> bash -c 'source /usr/local/Cellar/dnvm/1.0.0-dev/libexec/dnvm.sh; dnvm upgra
==> Caveats
Add the following to the ~/.bash_profile, ~/.bashrc or ~/.zshrc file:

  source dnvm.sh

==> Summary
🍺  /usr/local/Cellar/dnvm/1.0.0-dev: 4 files, 56K, built in 14 seconds

Entonces, luego de reiniciar la consola, y correr el dnu restore, tenemos algo así Captura de pantalla 2015-04-30 a las 2.50.34Ahora si, volvemos a VSCode y lo único que nos falta es levantar el “servidor” y probar. Para ello, llamamos el “kicker” con las teclas ⇧⌘P y escribimos kestrel (que es un comando creado en project.son). Éste comando nos abre la consola y levanta el servidor.

Accedemos desde http://localhost:5001 y … nos da el siguiente error Captura de pantalla 2015-04-30 a las 2.57.03

Es un error conocido por la comunidad, por suerte, así que tiene solución:

  1. Dar de baja el servidor
  2. Ejecutar el siguiente comando en la consola el siguiente comando, y luego iniciar nuevamente el servidor
export MONO_MANAGED_WATCHER=disabled

Listo ! Captura de pantalla 2015-04-30 a las 23.25.30

Fuentes:

Publicado en Aplicación | Etiquetado , | Deja un comentario

Mironeando PictureBook (O la extensión que “hackea” a Facebook)

Hace poco salió la noticia de que una extensión para Chrome podía ver las fotos ocultas de la personas en Facebook.

La gente se volvió loca !! Y la verdad es que no es nada grave como para entrar en pánico.

Para tomar un ejemplo, entré al perfil de una persona que publicó algo en un grupo en Facebook. No lo conozco, y no tengo amigos en común y tiene todo bloqueado.

Si voy a ver sus fotos, logro ver algunas pocas.

Aquí entra en juego la extensión. Le das un click y mágicamente te da una grilla con las fotos de la persona. No son todas, y aún no entiendo el criterio que ocupa para mostrar las que sí puedes ver…

Lo único que hace, en realidad, es usar el mismo enlace que Facebook entrega… por lo que depende de Facebook. De ninguna manera es un crackeo ni nada, así que no hay nada que temer.

Lo que hace la extensión es “redirigir” a la siguiente URL

https://www.facebook.com/search/facebook_id_persona/photos-of

Y eso es todo …

Y si no conozco el FacebookID de la persona ?

Lo primero que puedes hacer es ver la URL de la persona. El link del tipo que estoy usando es : https://www.facebook.com/juansebastian.munozgonzalez

En este caso, no me sirve… ya que la persona está usando el alias.

Para ver el alias de la persona, la forma más fácil es:

  1. Click derecho en página y luego: “Inspeccionar Elemento”.
  2. En el panel nuevo que se abre, ir a “Consola” Captura de pantalla 2014-12-30 a las 13.23.05
  3. Ignorar el mensaje de alerta ( 8D )
  4. Copiar esto:
    console.log(document.getElementById("pagelet_timeline_main_column").dataset.gt);
  5. Pegar (Justo abajo donde dice que no hagas caso cuando te digan que copies y pegues… jajajaja ) y da enter.
  6. Sale algo así y listo
    Captura de pantalla 2014-12-30 a las 13.27.01
    ;) Tenemos el FacebookID (profile_owner).

Ahora que tenemos listo, simplemente reemplazamos en la URL anterior

https://www.facebook.com/search/facebook_id_persona/photos-of

Si se preguntan por qué hice el post … es porque me carga cuando dicen: “ooohhhh lograron hackear a Facebook !!! Vamos a morirrr!!!

Y no, no han “hackeado” a facebook …

Publicado en Demases | Deja un comentario

Los Usuarios (Parte I)

Empezamos nueva categoría en el blog dedicada a nuestros queridos usuarios :)

Estaba tranquilo en la oficina viendo un video en YouTube sobre las muertes en Star Wars (Algo así como 2.005.000 millones de muertes) y llegó una usuaria con un papel en la mano.

Dentro de mis funciones, es mi deber minar la BD en busca de personas con enfermedades raras. Obviamente, cuando llegó la usuaria con el papel en mano automáticamente asumí que me iba a pedir los datos de X persona… la primera con ebola en la ciudad de Iquique.

Pero no. En el papel tenía nada más que un link a youtube:

-“Mario, cómo puedo tener este video en mi carpeta?”
-“Mmm crea un link a la página, o lo descarga directo desde YouTube”
-“Ya, quiero eso. Cómo lo descargo?”
-(Yo, con cara de: “para qué me pregunta si me va a pedir a mi que lo haga”) “Mándeme ese mismo enlace por correo y yo lo descargo”
-“Ah, pero no se cómo mandarlo…”
-“De la misma manera en que lo copió para imprimirlo en ésta hoja… y me lo manda por correo”
-“Ya ! Ok, te lo mando ahora”

Y como podrán estar pensando … lo que llegó por correo fue un word adjunto con el link del video dentro …

Captura de pantalla 2014-10-27 a las 9.00.59

Publicado en Usuarios | Deja un comentario

En busca de un cochino sonido en un cochino juego en un cochino programa en un cochino celular

La cosa es así: Escuché el sonido de un pollito, como esos de goma, que me quedó gustando. Quise que me lo mandaran para ponerlo como tono cuando llegara una notificación al celular (WP 8.1 … ahora se puede ;) 8) ). La cosa es que el sonido venía de un juego … no de algo que se pueda enviar fácil y bonito.

Buscando el juego (Mr. Flap) en google encontré su .APK y la descargué.

Los que han desarrollado para android más o menos saben lo que sigue… para el resto, seguir los pasos ;)

  1. Descargar el APK y cambiar la extensión a .zip
  2. Al descomprimir tenemos distintas carpetas, la que nos importa es res o resources.
  3. En mi caso, el sonido se encontraba aquí Captura de pantalla 2014-05-02 a la(s) 11.46.10
  4. Lo siguiente es enviarlo al equipo (En mi caso un Lumia 920) usando el software de Windows:
    1. Únicamente se pueden usar .mp3 o .wma … por lo que, el .wav no me sirve… Lo convierto online aquí.
    2. Lo abro con iTunes, click derecho y obtener información. Al fondo, le agrego el género de ringtoneCaptura de pantalla 2014-05-02 a la(s) 11.59.07
    3. Ahora, el programa me lo reconoce y lo puedo enviar al equipo ;) Captura de pantalla 2014-05-02 a la(s) 12.00.53
  5. Con el archivo en el teléfono, vamos a las configuraciones y luego “Ringtones y Sonidos”:
    1. Bajamos hasta el final y nos lleva a los sonidos por aplicación: wp_ss_20140502_0003
    2. Siguiente, abrimos la aplicación a la que vamos a asignar el sonido: wp_ss_20140502_0001
    3. Por último, buscamos el sonido :D wp_ss_20140502_0002
  6. Y eso fue todo :D Simple para los que hacemos esto… pero un poco más complicado para la gente común y silvestre.

Espero les sirva ;)

Publicado en Tutoriales, Windows Phone | Etiquetado , , | 1 comentario

Flask y CouchDB, Guardando Información (III)

Que siga la conga !

Hoy vamos a tomar datos desde un formulario y guardarlos en CouchDB (En mi caso usando Cloudant)

Entonces, tenemos un formulario así:

<form class="form-horizontal" method="post" action="/Admin/Nuevo/Ramo">
    <fieldset>
        <legend>Nuevo Ramo</legend>
        <div class="form-group">
            <label class="col-md-4 control-label" for="txt_nombre_ramo">Nombre Ramo</label>
            <div class="col-md-5">
                <input id="txt_nombre_ramo" name="txt_nombre_ramo" type="text" placeholder="Ej: Inglés I" class="form-control input-md" required>
            </div>
        </div>
        <div class="form-group">
            <label class="col-md-4 control-label" for="txt_pass_ramo">Contraseña</label>
            <div class="col-md-6">
                <input id="txt_pass_ramo" name="txt_pass_ramo" type="password" class="form-control input-md">
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-4">
                <button id="btn_guardar" name="btn_guardar" class="btn btn-primary">Guardar</button>
            </div>
        </div>
    </fieldset>
</form> 

Como ven, el formulario apunta a: /Admin/Nuevo/Ramo , y la ruta definida es así:

@app.route('/Admin/Nuevo/<Item>', methods=['POST'])
def AdminNuevo(Item):
    from flask import request
    if(Item == 'Ramo'):
        nombre, passw = request.form['txt_nombre_ramo'], request.form['txt_pass_ramo']
        from Auxiliares.validador import Validador
        respuesta = Validador.IngresoRamo(nombre, passw)
        if(respuesta):
            from DAO.ramoDAO import RamoDAO
            rdao = RamoDAO()
            rdao._Guardar(respuesta)
        else:
            return "Campos no pueden quedar vacíos"

Lo nuevo aquí es que, para recibir parámetros enviados desde el formulario, necesitamos usar request. Las variables las obtenemos como: request.form[‘nombre_variable’] .

Lo siguiente que tengo, es una llamada a una función que me valida los campos y devuelve un objeto Ramo o un False (dependiendo):

def IngresoRamo(cls, NombreRamo, PassRamo):
    """Comprobar que no vengan vacíos"""
    if(NombreRamo and PassRamo):
        from Clases.ramo import Ramo
        return Ramo(Nombre = NombreRamo, Pass = PassRamo)
    else:
        return False 

A su vez, y aquí viene lo importante, la clase Ramo tiene lo siguiente:

from couchdbkit import Document, StringProperty

class Ramo(Document):


    Nombre = StringProperty(required = True)
    Pass = StringProperty(required = True)

Importamos Document y StringProperty y creamos la clase Ramo como un Document.

Por último, para guardar en la BD:

from couchdbkit import Server


class RamoDAO():

    server = None
    db = None


    def __init__(self):
        self.server = Server('https://usuario:pass@usuario.cloudant.com')
        self.db = self.server.get_or_create_db('notas_st')


    def _Guardar(self, Ramo):
        Ramo.set_db(self.db)
        Ramo.save()

Y con esto nos conectamos a la BD e insertamos el registro. Si vamos a la página: Captura de pantalla 2014-04-18 a la(s) 23.30.52 Y como pueden ver, couchdbkit mágicamente nos agrega el doc_type dependiendo de la clase del objeto ingresado ;)

Publicado en CouchDB, Python | Etiquetado , , , | Deja un comentario

Flask, Templates (II)

Seguimos !

Una de las características de Flask, es que “hereda” casi todo de Django, pero sin tanto jaleo. A veces no queremos matar moscas a cañonazos :) Una de esas cositas que tiene de Django son las templates o plantillas: Jinja2.

Si bien Jinja2 tiene muchas cosas que incorporar para hacernos la vida más simple, aquí vamos a ver las más básicas… así como para soltarnos :D

Usando la estructura básica de una página en Bootstrap, tendríamos algo así (Base.html):

<!DOCTYPE html>
<html lang="es">
    <head>
        {% block meta %}
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="Aplicación para ver las notas y progreso">
        <meta name="author" content="Mario Cares">
        {% endblock meta %}
        <title>Visor Notas</title>
        {% block css %}
            <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
        {% endblock css %}
    </head>
    <body>
        <div class="container">
        {% block contenido %}{% endblock contenido %}
        </div>
        {% block js %}
            <script src="{{ url_for('static', filename='js/jquery-1.11.0.min.js') }}"></script>
            <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
        {% endblock js %}
    </body>
</html>

Notar que no utilizamos nada complicado, simplemente el esqueleto básico con los css y js que siempre usamos.

Desde arriba, tenemos el primer block. Cada block debe tener un nombre único. En mi caso, el primero se llama meta y encierra todas las etiquetas meta que tiene la página. Y para terminar el block, simplemente un endblock y el nombre correspondiente

Y así hasta el último que es js.

También tenemos un url_for que nos sirve para buscar un archivo en la carpeta static y obtener su ruta relativa sin tener que lidiar con los ../../ de las carpetas.

Ahora, para usar las templates, las llamamos desde nuestro archivo de rutas. Primero tenemos el import desde flask y luego lo llamamos:

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    if True:
        return render_template('Generales/Login.html')


if __name__ == '__main__':
    app.debug = True
    app.run()

Si se fijan, estoy llamando a otra template Login.html y no a Base.html que definimos arriba. Y es aquí donde entra la magia de esto. La estructura de Login.html es así:

{% extends "Generales/Base.html" %}
{% block css %}
    {{ super() }}
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/singin.css') }}">
{% endblock css %}
{% block contenido %}
    <form class="form-signin" role="form" method="post">
        <h2 class="form-signin-heading">Iniciar Sesión</h2>
        <input type="text" name="txt_rut_alumno" class="form-control" placeholder="Rut Alumno" required autofocus>
        <input type="password" name="txt_password" class="form-control" placeholder="Contraseña" required>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Ingresar</button>
    </form>
{% endblock contenido %}

Lo primero que tenemos es un extends, que funciona de la misma manera … estamos diciendo que nuestra plantilla base será … Base.html, y por lo tanto tendremos su estructura y podremos usar los blocks que ella tiene definida.

Es por eso que tenemos un {% block css %}, siendo que dijimos que el nombre era único. Esto significa que podríamos sobreescribir (un @override) el block css de la plantilla madre… Pero, el {{ super() }} nos permite obtener todo lo que el block original tiene, y después agregar todo lo que tengamos nosotros como plantilla hija. En nuestro ejemplo, al block original que tiene 2 css y le vamos a agregar un 3º . Esto será válido únicamente en el archivo Login.html.

Nuestra plantilla Base.html tenía definido el block contenido sin nada dentro. En Login.html sobreescribimos nuestro dicho block con lo que nosotros como plantilla tenemos.

También podemos realizar includes en el caso que queramos agregar el contenido de una plantilla en otra (plantilla Administrador.html):

{% extends "Generales/Base.html" %}
{% block contenido %}
<!-- Nav tabs -->
<ul class="nav nav-tabs">
    <li class="active"><a href="#home" data-toggle="tab">Home</a></li>
    <li><a href="#ramos" data-toggle="tab">Ramos</a></li>
    <li><a href="#alumnos" data-toggle="tab">Alumnos</a></li>
    <li><a href="#settings" data-toggle="tab">Settings</a></li>
</ul>

<!-- Tab panes -->
<div class="tab-content">
    <div class="tab-pane fade in active" id="home">...</div>
    <div class="tab-pane" id="ramos">
        {% include "Admin/AdminRamo.html" %}
    </div>
    <div class="tab-pane fade" id="alumnos">
        {% include "Admin/AdminAlumno.html" %}
    </div>
    <div class="tab-pane" id="settings">...</div>
</div>
{% endblock contenido %}

Por último, podemos enviar variables a nuestras plantillas de la siguiente manera:

@app.route('/')
def index():
    if True:
        return render_template('Generales/Login.html', Mensaje = "Error", OtroMensaje = u"Algo no funcionó")

Donde Mensaje y OtroMensaje son las variables a enviar con el contenido de cada una. OtroMensaje tiene una u antes del string para poder jugar con tildes y caracteres especiales, por lo que además deben agregar # -*- coding: utf-8 -*- al principio del archivo.

Para poder ocupar dichas variables, en nuestra template escribimos:

<h1>{{ Mensaje }}</h1>
<h2>{{ OtroMensaje }}</h2>

Y eso sería todo :D

Publicado en Python | Etiquetado , | Deja un comentario

Empezando con Flask, Routes (I)

Eso.

Aquí en mi ciudad, Iquique (Chile), la semana pasada tuvimos un dos terremotos y la universidad en la que hago clases virtualmente se vino abajo. Uno de los ramos que enseño es para los que recién entran, el año 1 de la carrera de Informática. A ellos les tengo que enseñar Python … y la verdad es que andaba oxidado 8O … así que para “refrescar” la memoria empecé a buscar algún micro-framework para crear aplicaciones web, llegando a Flask.

Aquí en el blog escribí sobre Slim, para PHP. Flask es lo mismo, pero para Python.

Es tan sencillo como:

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

Lanzando por consola, la salida nos dice:
* Running on http://127.0.0.1:5000/
Y a esa dirección debemos ir.

Lo único que tenemos es el “Hello World!” y nada más.

Agregamos una nueva ruta con lo siguiente:

@app.route('/Nuevo')
def Nuevo():
    return "Nuevo :D"

Reiniciamos la aplicación, y al entrar en http://127.0.0.1:5000/Nuevo tenemos la nueva página.

Si declaramos una ruta como: @app.route(‘/Usuario/<nombre>’) , estamos creando una variable nombre, la que cambiará con el link que se llame.

@app.route('/Usuario/<nombre>')
def mostrar_usuario(nombre):
    return 'El nombre del usuario es %s' % nombre

Por defecto, todas las rutas responden al método GET, por lo que si enviamos un formulario por POST a cualquier ruta, no funcionará, ya que la ruta POST aún no ha sido definida.

Para definir rutas, tan simple como:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        logica_para_validar_usuario()
    else:
        mostrar_el_formulario_del_login()

Eso por ahora, seguimos más tarde con las Templates

Publicado en Python | Etiquetado , | 2 comentarios

CouchDB (Parte II)

En la entrada anterior vimos un poco (por decir NADA) de CouchDB, nos conectamos a Cloudant e insertamos un registro … más que eso no hicimos 8O … principalmente porque aún no entiendo casi nada de ésta cosa … es muy enredado :P

Ahora vamos a ver más cosillas.

CouchDB no tiene Tablas o Colecciones

Así, tal cual. Una DB es un contenedor que no tiene divisiones, por lo que tenemos documentos juntos. La idea es que no estén revueltos. Es “extraño” cuando menos… pero bue, así lo hicieron :D

Para “solucionar” el tema, a cada documento (objeto) le agregamos un campo para definir el tipo. Algo así:

public class Usuario {

    private String Nombre,
        Pass,
        Mail,
        Tipo;
    private Date FechaRegistro;

    public Usuario(String nombre, String mail, String pass) throws
            UnsupportedEncodingException, NoSuchAlgorithmException {
        Nombre = nombre;
        setPass(pass);
        Mail = mail;
        Tipo = "Usuario";
        FechaRegistro = new Date();
    }
}

Y con ésto podemos deferenciar entre documentos cuando ocupemos vistas.

Vistas

Más que guardar y mantener la información, lo más importante es como verla o recuperarla. Aquí entran las vistas, ya que CouchDB no “tiene” formas para hacer querys (al estilo MongoDB, por ejemplo).

Siguiendo el ejemplo anterior, tenemos algo así: Captura de pantalla 2014-01-25 a la(s) 13.30.58 Y si queremos crear un login de usuarios ? Porque, ya los tenemos registrados…

Las vistas en realidad son índices secundarios (el primario es el campo _id). Por ejemplo, queremos indexar por el Mail de cada persona: Captura de pantalla 2014-01-25 a la(s) 14.34.15Ésta función lo que hace es recorrer TODOS los documentos en nuestra DB (en el parámetro doc). Luego, comprobamos que indexe únicamente documentos con el tipo Usuario. Por último generamos el key & value: siendo el Mail del documento la llave y el documento en si el valor.

Y terminamos de ésta manera: Captura de pantalla 2014-01-25 a la(s) 14.49.28 Entonces podemos hacer la query preguntando por la key (en éste caso el Mail) : Captura de pantalla 2014-01-25 a la(s) 14.52.03 Y qué pasa si queremos consultar por el mail y la pass ? En ese caso, modificamos la key y quedamos con:

function(doc) {
  if(doc.Tipo && doc.Tipo == "Usuario"){
    emit([doc.Mail, doc.Pass], doc);
  }
}

De ésta manera, tenemos una llave compuesta entre el mail y la pass Captura de pantalla 2014-01-25 a la(s) 15.00.44 Ahora, saltamos al login desde nuestra aplicación.

Vistas desde Java

Tenemos un login con nada más que el mail y la pass Captura de pantalla 2014-01-25 a la(s) 16.10.37 Entonces, en el Servlet tengo algo así:

Map<String, String> Params = new HashMap<String, String>();
Params.put("Target", "Mail");
Params.put("Param", request.getParameter("mail"));
for(Usuario Us : (List <Usuario>)U.GET(Params)){
  out.write(Us.toString());
}

Que, en resumen, le pide al DAO(U) que le entrege todos los usuarios en un List<Usuario> que tengan el Mail ingresado en el formulario (ojo que no usamos la pass).

UsuarioDao (el objeto U de recién) a su vez tiene lo siguiente:

public Object GET(Map<String, String> M) {
  if(M.get("Target").equals("Mail")){
    return dbClient.view("vistas/Por_Mail"). //Nombre de la Vista
      includeDocs(true).
      key(M.get("Param")). //El parámetro key en la vista
      query(Usuario.class); //La clase a la que se va a Des.Serializar
  }
  return null;
} 

Entonces, revisamos qué nos piden (en éste caso, que Target sea Mail -consultar por Mail) y devolvemos el List<Usuario>.

Si estamos consultando a Cloudant, Captura de pantalla 2014-01-25 a la(s) 16.24.45 fijarse en Nombre del contenedor de Vistas y Nombre de Vista. Juntando éstos dos formamos el nombre “completo” de la vista.

Listo :D Captura de pantalla 2014-01-25 a la(s) 16.29.37Y si queremos llamar la vista por mail y pass, simplemente agregamos un valor más al campo key:

public Object GET(Map<String, String> M) {
  if(M.get("Target").equals("Mail")){
    return dbClient.view("vistas/Por_Mail"). //Nombre de la Vista
      includeDocs(true).
      key(M.get("Param1"), M.get("Param2")). //El parámetro key en la vista, ahora con 2 valores
      query(Usuario.class); //La clase a la que se va a Des.Serializar
  }
  return null;
} 

Genial :D !

Eso por ahora ;)

Publicado en CouchDB, Java, JavaScript | Etiquetado , | Deja un comentario