29 ago. 2015

Extracción de información de PDFs con python (parte 1).

Existen varias herramientas en python para obtener información de PDF.
En este caso visite la página de Cencoex, en la sección de Cencoex en cifras hay un enlace a un pdf  a Liquidaciones a Empresas Agremiadas del Sector Salud (Ordinarias + ALADI + SUCRE)

Para este primer artículo se usará pycurl para bajar el pdf y peepdf para analizarlo. Para descargar peepdf se puede hacer desde el repositorio del proyecto en github.

En el caso de pycurl se instala por pip o por apt-get en Debian.

Se baja peepdf:
git clone https://github.com/jesparza/peepdf.git

Para bajar el archivo se ejecuta el código a continuación, el cual baja el archivo pdf y lo salva como salud.pdf:
import pycurl

with open('salud.pdf', 'wb') as f:
    c = pycurl.Curl()
    c.setopt(c.URL, 'http://www.cencoex.gob.ve/images/stories/pdfs/estadisticas/Salud.pdf')
    c.setopt(c.WRITEDATA, f)
    c.perform()
    c.close()


En el directorio de la aplicación se ejecuta (opción -f para el archivo y -i en modo interactivo:
./peepdf.py  -f ../salud.pdf -i 

Y devuelve:
File: salud.pdf
MD5: e639fca504182dc5cb383cc455d30c21
SHA1: 2c29f4a20e4e0b6a3c10decbfacced991e59ad79
SHA256: 9a4d3d48f060d1fa29fe9cb5e7a435643586e9871e62254839daf3fd87e45506
Size: 57611 bytes
Version: 1.3
Binary: True
Linearized: False
Encrypted: False
Updates: 0
Objects: 34
Streams: 7
Comments: 0
Errors: 0

Version 0:
Catalog: 1
Info: 2
Objects (34): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
Streams (7): [10, 33, 32, 5, 15, 21, 27]
Encoded (7): [10, 33, 32, 5, 15, 21, 27]

El comando tree devuelve la estructura lógica del archivo:

PPDF> tree

/Catalog (1)
/Pages (3)
/Page (4)
/Pages (3)
stream (5)
integer (6)
Unknown (0)
/Catalog (1)
/R10 (12)
stream (10)
/R7 (11)
/ExtGState (7)
/R8 (13)
/Font (8)
/Encoding (34)
/FontDescriptor (9)
stream (32)
stream (33)
/Page (14)
/Pages (3)
stream (15)
integer (16)
Unknown (0)
/Catalog (1)
/R10 (18)
stream (10)
/R7 (17)
/ExtGState (7)
/R8 (19)
/Font (8)
/Page (20)
/Pages (3)
stream (21)
integer (22)
Unknown (0)
/Catalog (1)
/R10 (24)
stream (10)
/R7 (23)
/ExtGState (7)
/R8 (25)
/Font (8)
/Page (26)
/Pages (3)
stream (27)
integer (28)
Unknown (0)
/Catalog (1)
/R10 (30)
stream (10)
/R7 (29)
/ExtGState (7)
/R8 (31)
/Font (8)
/Info (2)


Para ver la estructura física se usa el comando offsets:
PPDF> offsets 

       0 Header
      15
        Object  5 (8865)
    8879
    8881
        Object  6 (19)
    8899
    8901
        Object  15 (9005)
   17905
   17907
        Object  16 (20)
   17926
   17928
        Object  21 (8618)
   26545
   26547
        Object  22 (20)
   26566
   26568
        Object  27 (6554)
   33121
   33123
        Object  28 (20)
   33142
   33144
        Object  4 (189)
   33332
   33334
        Object  14 (191)
   33524
   33526
        Object  20 (191)
   33716
   33718
        Object  26 (191)
   33908
   33910
        Object  3 (88)
   33997
   33999
        Object  1 (47)
   34045
   34047
        Object  7 (40)
   34086
   34088
        Object  11 (29)
   34116
   34118
        Object  12 (31)
   34148
   34150
        Object  10 (4140)
   38289
   38291
        Object  13 (29)
   38319
   38321
        Object  17 (29)
   38349
   38351
        Object  18 (31)
   38381
   38383
        Object  19 (29)
   38411
   38413
        Object  23 (29)
   38441
   38443
        Object  24 (31)
   38473
   38475
        Object  25 (29)
   38503
   38505
        Object  29 (29)
   38533
   38535
        Object  30 (31)
   38565
   38567
        Object  31 (29)
   38595
   38597
        Object  33 (614)
   39210
   39212
        Object  8 (436)
   39647
   39649
        Object  34 (408)
   40056
   40058
        Object  9 (204)
   40261
   40263
        Object  32 (16249)
   56511
   56513
        Object  2 (243)
   56755
   56757
        Xref Section (709)
   57465
   57467
        Trailer (138)
   57604
   57605 EOF

Para ver el metadato del pdf se ejecuta metadata:
PPDF> metadata

Info Object in version 0:

<< /ModDate D:20140908210848+04'-30'
/CreationDate D:20140908210848+04'-30'
/Producer Nitro PDF PrimoPDF
/Title LIQUIDACIONES DEL 1 ENERO AL 5 DE SEPTIEMBRE 2014.xls
/Creator PrimoPDF http://www.primopdf.com
/Author lrojas >>

La fecha de creación del archivo es 09-08-2014 y la hora es 21:08:48  hora de Venezuela. El título del archivo Liquidaciones del 1 enero al 5 de septiembre de 2014, ah, es un archivo xls de excel. Y usaron primopdf para generar el pdf, por último el autor del pdf es lrojas.


Si se quiere revisar el objeto 2:
PPDF> object 2

<< /ModDate D:20140908210848+04'-30'
/CreationDate D:20140908210848+04'-30'
/Producer Nitro PDF PrimoPDF
/Title LIQUIDACIONES DEL 1 ENERO AL 5 DE SEPTIEMBRE 2014.xls
/Creator PrimoPDF http://www.primopdf.com
/Author lrojas >>

Es el que contiene la información de los metadatos.

Revisando el objeto 15 se obtiene:
432 1739.33 3991 6 re
f
432 1642.33 3991 6 re
f
432 1545.33 3991 6 re
f
432 1448.33 3991 6 re
f
432 1351.33 3991 6 re
f
432 1254.33 3991 6 re
f
432 1157.33 3991 6 re
f
432 1060.33 3991 6 re
f
432 963.332 3991 6 re
f
432 866.332 3991 6 re
f
432 769.332 3991 6 re
f
432 672.332 3991 6 re

O usando rawstream:
rawstream 15
50 9f fa 71 0f 29 76 8a bc 44 3e dd c6 15 84 9e   öP..q.)v..D>.....ö
4c 89 76 f6 75 8c 3d 23 65 11 fb 65 48 d1 0e a1   öL.v.u.=#e..eH...ö
37 f6 08 d3 b6 f8 d2 17 15 4a 76 29 b0 04 86 e1   ö7........Jv)....ö
b4 9e 9b 3a 34 e7 d1 ce 91 9a c9 c0 91 9e cc 6c   ö...:4..........lö
4a 5d 24 67 c7 aa 21 fe 2c ea e6 ea 9e 8d 95 33   öJÅ$g..!.,......3ö
61 e5 55 2c 13 3c cd 61 21 d1 26 03 47 ba 32 b3   öa.U,.<.a!.&.G.2.ö
09 da cc ce 8e da e4 61 a1 85 34 d6 ba 00 63 5c   ö.......a..4...cÖö
08 f1 63 34 7a 90 44 cb 79 4d 95 50 58 6c e7 55   ö..c4z.D.yM.PXl.Uö
74 d4 da fb 44 4c 8a 48 a8 ae 41 42 b1 e5 44 a8   öt...DL.H..AB..D.ö
e9 bb 42 bb 47 28 3b cd 5e 22 0f 5d 3b 42 e8 d9   ö..B.G(;.Ü".Å;B..ö
e4 68 93 af 5f ec 99 50 8b d8 7f 03 08 05 8b 04   ö.h.._..P......ö
f5 a3 48 d5 bc 7b 7d 97 33 80 d8 c6 f6 85 57 90   ö..H..äå.3.....W.ö
4b 6a 8c 50 15 17 e4 e2 3a ca ce 7e 55 46 e4 5a   öKj.P....:..üUF.Zö
d4 d9 d5 eb 7d 70 a0 fb d6 a0 98 7d 3e 8f d9 72   ö....åp.....å>..rö
12 6d 08 55 71 41 2e 79 12 93 b3 9f 36 d4 72 5a   ö.m.UqA.y....6.rZö
68 a3 40 78 6b 9a 72 79 8c e9 6e 67 6c b9 cb ce   öh.@xk.ry..ngl...ö
9a 4b 4d ec f8 02 e7 f3 3e fa f8 f8 cd 2f 6e 7e   ö.KM.....>..../nüö
79 d3 c5 a0 96 4f 37 9d f1 46 3b 8e ea 1b 4d 13   öy....O7..F;...M.ö
7a fd 8d c9 ae fb 3e 4d 16 4c 7d e1 e9 63 a1 bc   öz.....>M.Lå..c..ö
eb 47 b5 bc 1d a2 5a de 0c ad 5a 5e 0f 83 5a 1e   ö.G....Z...ZÜ..Z.ö
c7 a0 96 d3 21 d8 ab 72 3a 16 77 5d 9e 4f a7 3c   ö....!..r:.wÅ.O.<ö
2f 2f 07 c1 ad ca e9 64 a7 55 39 9d eb b2 2a a7   ö//.....d.U9...*.ö
d3 1c 56 e5 b4 01 fb aa 9c 36 37 5e 95 d3 fe 9c   ö..V......67Ü....ö
ab 72 da b9 6e 55 4e fb 48 ad ca 69 57 96 f3 f2   ö.r..nUN.H..iW...ö
b2 af c0 aa 9c d6 e9 ae ca 69 85 dd aa 9c 96 b5   ö.........i......ö
ac ca 29 b9 7c 55 4e f9 9b ab 72 ca c2 5a 95 53   ö..).öUN...r..Z.Sö
8e c2 aa 9c a6 b4 56 e5 34 5a 7b 5e 5e 06 75 ce   ö......V.4ZäÜÜ.u.ö
cb 4b 1f ec bc bc b4 8d ce cb 0b 79 b0 fc 47 37   ö.K.........y..G7ö
ff 07 29 f1 43 c1                                 ö..).C.ö


Uno de los problemas de analizar pdf generados en la APN en Venezuela es que no lo generan a partir de la información que manejan directamente de las bases de datos, si no que usan una hoja de cálculo en este caso en windows y la de Microsoft Office, y generan el pdf con herramientas para windows. 

¿Será autentica la información que tiene el pdf luego de manipulación con varias herramientas?

En siguientes post seguiré probando distintas herramientas en Python para ver si logro hacer un pdfscraping decente del pdf de Cencoex.

27 ago. 2015

Obtener la resolución de la pantalla desde Python.

Este artículo se basa en un artículo en inglés sobre el tema.

Desde la línea de comandos en Linux se puede ejecutar el comando xrandr como se muestra a continuación:

ernesto@grievous:~$ xrandr | grep '*'
   1366x768      60.00*+

La resolución es de 1366x768.

El script se muestra a continuación:

#!/usr/bin/env python

#Se importa el modulo subprocess
import subprocess

#Se define un par de variables con los comandos a pasar:
cmd = ['xrandr']
cmd2 = ['grep', '*']

#Se ejecuta el comando xrandr y luego se abre una tuberia.
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)

#Se ejecuta el segundo comando
p2 = subprocess.Popen(cmd2, stdin=p.stdout, stdout=subprocess.PIPE)

#Se cierra la salida estandar.
p.stdout.close()


#Obteccion de la resolucion
resolution_string, junk = p2.communicate()
resolution = resolution_string.split()[0]
width, height = resolution.split('x')
print width,height


Al ejecutar el script se obtiene:
./screencatch.py 
1366 768

Obtener resolución con gtk con el siguiente script:

#!/usr/bin/env python

#Se importa gtk
import gtk

#Se captura el ancho y alto 
width = gtk.gdk.screen_width()
height = gtk.gdk.screen_height()

#Se muestra en la consola el resultado
print width,height

Al ejecutar el script se obtiene:
 ./screencatch2.py 
1366 768

Obtener la resolución con PySide/PyQT con el siguiente script:

#!/usr/bin/env python
#Importar QtGui de PySide
#para solo PyQt se cambia a
#from PyQt4 import QtGui

from PySide import QtGui

#Se crea la instancia de la aplicacion
app = QtGui.QApplication([])
#Se captura la resolucion y se muestra  en la consola
screen_resolution = app.desktop().screenGeometry()
width, height = screen_resolution.width(), screen_resolution.height()

print width,height

El resultado de ejecutar el script  es:

./screencatch3.py 
1366 768


En el artículo mencionado se muestra como obtener la información con wxPython y en un enlace en el artículo como obtener la información desde windows y desde MacOS.


14 may. 2015

Usar mongodb en Django

En artículos anteriores se ha tocado el tema de  mongodb y el tema de Django.

En este caso se tocará el tema de crear un blog con Django usando mongodb, este artículo se basa en un artículo en Inglés del sistio Developers Work de IBM.

En este caso se tendrá un formulario para crear post, actualizarlos y borrarlos. Se usará la plantilla de material design para Django explicado en el artículo anterior.

Para poder desarrollar la aplicación en Django y con mongodb se tiene que instalar Django, mongodb y mongoengine a continuación se muestra la instalación a lo Debian:

apt-get install python-mongoengine mongodb-server mongodb-clients mongodb python3-pymongo python3-pymongo-ext python-django python3-django

Lo primero que se tiene que hacer es crear el proyecto blog.

django-admin startproject blog

Esto crea directorios y archivos como se muestra a continuación:

blog
├── blog
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py


Ahora se crea la aplicación posts:
django-admin startapp posts 

Ahora se tiene la siguiente estructura de directorios y archivos: 
blog
├── blog
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── settings.py
│   ├── settings.pyc
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── posts
    ├── admin.py
    ├── __init__.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py


Modificar el archivo blog/blog/settings.py para que refleje el uso de mongodb y la aplicación.

Es necesario agregar en las primeras líneas del settings.py el nombre de la base de datos para mongodb:
DBNAME = 'blongo'

Agregar en las aplicaciones posts y el soporte a material-design que se explico en artículo anterior.

INSTALLED_APPS = (
    'material',
    'material.admin',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts',
)

Se modifica también el settings.py para que procese el directorio donde estarán las plantillas:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates' )],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Se crea los directorios templates y media: 
mkdir -p {templates,static}

Y en media se crea los directorios css, images y js:
mkdir -p static/{css,images,js}

Ahora la estructura de directorios queda de la siguiente manera:
blog
├── blog
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── settings.py
│   ├── settings.pyc
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── posts
│   ├── admin.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── static
│   ├── css
│   ├── images
│   └── js
└── templates




En el directorio posts se modifica el archivo models.py con el siguiente contenido:
#Se importa models
from django.db import models

#Se importa mongoengine quien es el que facilita la conexión a mongodb
from mongoengine import *

#Se trae el nombre de la base de datos de mongodb de settings.py
from blog.settings import DBNAME

#Se conecta a la base de datos
connect(DBNAME)


#Se crea la tabla Posts (documento), con title, content y la fecha de publicación y/o actualización
class Post(Document):
    title = StringField(max_length=120, required=True)
    content = StringField(max_length=500, required=True)
    last_update = DateTimeField(required=True)


Se modifica el archivo blog/posts/views.py con el siguiente contenido (ahí se publica el formulario, la lista de posts, se actualiza cada post y se puede borrar cada post):

from django.shortcuts import render, render_to_response
from django.template import RequestContext

from models import Post

import datetime

#Se define index donde recibe como argumento request
def index(request):
    #SI el metodo es un post, se toma los datos del formulario y se guarda en 
    #mongodb
    if request.method == 'POST':
       # nuevo post
       title = request.POST['title']
       content = request.POST['content']

       post = Post(title=title)
       post.last_update = datetime.datetime.now()
       post.content = content
       post.save()
   #Si el metodo es get entonces se está cargando la página inicialmente, así que se publican 
   #los posts
    # se obtiene todos los posts de la base de datos
    posts = Post.objects
    return render_to_response('index.html', {'Posts': posts},context_instance=RequestContext(request))


#Actualizar un post
def update(request):
    #Captura el id del post
    id = eval("request." + request.method + "['id']")
    post = Post.objects(id=id)[0]
    #Si el metodo es post, se está actualizando la información del formulario
    if request.method == 'POST':
       
        # se actualiza los valores y se salva en mongodb
        post.title = request.POST['title']
        post.last_update = datetime.datetime.now()
        post.content = request.POST['content']
        post.save()
        template = 'index.html'
        params = {'Posts': Post.objects}
    #Si el metodo es GET se muestra la página incial update.html
    elif request.method == 'GET':
        template = 'update.html'
        params = {'post':post}

    return render_to_response(template, params, context_instance=RequestContext(request))

#Para borrar un posts
def delete(request):
    #Se toma el id del post
    id = eval("request." + request.method + "['id']")
    #Se pregunta si es POST, se le pasa el id del posts y se borra, se va a la página index.html
    if request.method == 'POST':
        post = Post.objects(id=id)[0]
        post.delete()
        template = 'index.html'
        params = {'Posts': Post.objects}
    #Si es un metodo get entonces se muestra la página delete.html
    elif request.method == 'GET':
        template = 'delete.html'
        params = { 'id': id }

    return render_to_response(template, params, context_instance=RequestContext(request))


 Ahora se mostrará las imágenes de los archivos base.html, index.html, update.html y delete.html.

En el archivo base.html se agregará las siguientes dos líneas las cuales permiten que se use el estilo y javascript a lo material design:
{% include 'material/includes/material_css.html' %}
{% include 'material/includes/material_js.html' %}



Los archivos index.html, update.html y delete.html tendrán 2 líneas, en una heredan el contenido  del archivo base.html y en la otra línea se carga material_form (material design):
{% extends 'base.html' %}
{% load material_form %}  

En el caso que se tenga un formulario se debe colocar después del token csrf la siguiente línea:
{% form form=form %}{% endform %}

Ya con eso se maneja los formularios con el estilo material design. 

A continuación se muestra la imagen del archivo index.html:


A continuación se muestra la imagen del archivo update.html:


A continuación se muestra la imagen del archivo delete.html:


Para que esto funcione falta modificar el archivo blog/blog/urls.py con los urls del index, update y delete:
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^$', 'posts.views.index'),
    url(r'^update/', 'posts.views.update'),
    url(r'^delete/', 'posts.views.delete'),
]


Se crea la base de datos administrativa con el comando migrate:
./manage.py migrate 
Operations to perform:
  Synchronize unmigrated apps: staticfiles, posts, material, messages, material_admin
  Apply all migrations: admin, contenttypes, auth, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying sessions.0001_initial... OK


Se crea la cuenta administrativa:
./manage.py createsuperuser
Username (leave blank to use 'ecrespo'): ernesto
Email address: ecrespo@xxxx.xxx
Password: 
Password (again): 
Superuser created successfully.

Se ejecuta el servidor de django:
./manage.py runserver 
Performing system checks...

System check identified no issues (0 silenced).
May 14, 2015 - 20:52:27
Django version 1.8, using settings 'blog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

A continuación se muestra una imagen de la aplicación mientras se llena el formulario:


Por último se muestra el post que se acaba de publicar:




Para más información pueden revisar los siguientes enlaces:

8 may. 2015

Habilitar autenticación en un servidor mongodb

En el artículo sobre la instalación de un servidor mongodb le falto definir la autentcación de usuarios que puedan conectarse a dicho servidor.

En este caso se habilitará dicha autenticación para el acceso al servidor.

Lo primero es deshabilitar la autenticación en el archivo /etc/mongodb.conf:
# Turn on/off security.  Off is currently the default
#noauth = true
#auth = true

Se inicia el servicio de mongodb:
 systemctl start  mongodb.service

Se revisa si levanto:
systemctl status  mongodb.service
● mongodb.service - An object/document-oriented database
   Loaded: loaded (/lib/systemd/system/mongodb.service; enabled)
   Active: active (running) since vie 2015-05-08 11:44:26 VET; 5s ago
     Docs: man:mongod(1)
 Main PID: 3405 (mongod)
   CGroup: /system.slice/mongodb.service
           └─3405 /usr/bin/mongod --config /etc/mongodb.conf

may 08 11:44:26 grievous mongod[3405]: all output going to: /var/log/mongodb/mongodb.log

Se accede al shel de mongodb:
mongo
MongoDB shell version: 2.4.10
connecting to: test
>


Se cambia a la base de datos admin: 
> use admin
switched to db admin

Se crea el usuario admin con clave 123456 (sólo de manera didactica) y rol lectura y escritura dbAdmin.


> db.addUser("admin","123")
{
"user" : "admin",
"readOnly" : false,
"pwd" : "9f3121efccbe3fef09a799d5e63077c2",
"_id" : ObjectId("554cf1405cf1b965e6f5c10f")
}


Se sale de la base de datos: 
> exit 
bye


Ahora se edita el archivo /etc/mongodb.conf  para habilitar la autenticación: 
# Turn on/off security.  Off is currently the default
#noauth = true
auth = true

Se reinicia mongodb:
systemctl stop  mongodb.service
systemctl stop  mongodb.service
systemctl status   mongodb.service
● mongodb.service - An object/document-oriented database
   Loaded: loaded (/lib/systemd/system/mongodb.service; enabled)
   Active: active (running) since vie 2015-05-08 11:58:44 VET; 4s ago
     Docs: man:mongod(1)
 Main PID: 4188 (mongod)
   CGroup: /system.slice/mongodb.service
           └─4188 /usr/bin/mongod --config /etc/mongodb.conf

may 08 11:58:44 grievous mongod[4188]: all output going to: /var/log/mongodb/mongodb.log

Se ejecuta el shell de mongodb y se intenta visualizar las bases de datos: 

> show dbs 
Fri May  8 12:06:19.122 listDatabases failed:{ "ok" : 0, "errmsg" : "unauthorized" } at src/mongo/shell/mongo.js:46

Se nota que no se tiene autorización para ver la lista de bases de datos.

Para ingresar y autenticar al usuario se hace lo siguiente:

> use admin
switched to db admin
> db.auth("admin", "123")
1

Listar bases de datos:

> show dbs 
admin 0.203125GB
local 0.078125GB
personasdb 0.203125GB

Listar usuarios:
 
> db.system.users.find()
{ "_id" : ObjectId("554ce337c597c2dbd0089d74"), "user" : "admin", "pwd" : "95ec4261124ba5951720b199908d892b", "roles" : [  "readWrite",  "dbAdmin" ] }



Para más información se tiene los siguientes enlace:

26 abr. 2015

Darle estilo material design a Django (parte 1, el admin)

Continuando con los artículos sobr Django.

En este caso se tocará el tema de usar en el admin y formularios un estilo llamado Material Design (desarrollado por Google para Android), la idea es que nuestro backend tenga ese estilo. Para ello se tiene una aplicación de Django llamada Django-material .

La documentación de django-material la pueden ver en el siguiente enlace.

Instalación:
Para instalar django-material se ejecuta el comando pip:
pip install django-material

Se crea el proyecto prueba:
django-admin startproject prueba

Se tiene los siguientes directorios y archivos:


prueba
├── manage.py
└── prueba
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py




En settings.py se agrega las aplicaciones :
  • material
  • material.admin

Al final se tiene lo siguiente en la sección de aplicaciones:


# Application definition

INSTALLED_APPS = (
    'material',
    'material.admin',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
)

Nota: material.admin debe estar antes que admin.

Se ejecuta manage.py migrate:
ecrespo@grievous:~/django/prueba$ ./manage.py migrate Operations to perform: Synchronize unmigrated apps: staticfiles, material, messages, material_admin Apply all migrations: admin, contenttypes, auth, sessions Synchronizing apps without migrations: Creating tables... Running deferred SQL... Installing custom SQL... Running migrations: Rendering model states... DONE Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying sessions.0001_initial... OK

Se crea el usuario administrador del proyecto:
./manage.py createsuperuser Username (leave blank to use 'ecrespo'): ernesto Email address: ecrespo@ Password: Password (again): Superuser created successfully.

Se ejecuta el servidor web de django:

ecrespo@grievous:~/django/prueba$ ./manage.py runserver 
Performing system checks...

System check identified no issues (0 silenced).
April 26, 2015 - 16:22:53
Django version 1.8, using settings 'prueba.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Se muestra la imagen del inicio de sesión del admin de Django:



Se muestra la imagen del admin luego de iniciar sesión:

La siguiente imagen muestra la sección de usuarios del admin de Django:



En siguiente artículo se explicará como darle el estilo material design a los formularios de una aplicación. 


7 abr. 2015

API rest Full para Python con Eve (parte 1).

Eve es un framework API rest diseñado "para humanos", en enlace para ver el proyecto es el siguiente.

Soporta mongodb y backends de SQL. Sus características las pueden revisar acá.

Este artículo se basa en el quickstart de la aplicación.

Para instalar a Eve se usa el comando pip o easy_install:

#pip install Eve

ó
#easy_install Eve

Luego se creará un archivo con nombre run.py. Su código es el siguiente:
#Se importa eve de Eve
from eve import Eve

#Se crea la instancia de Eve
app = Eve()

#Se ejecuta run.
if __name__ == '__main__':
        app.run()


Ahora se crea un archivo settings.py con el siguiente contenido:
DOMAIN = {'persona': {}}

Los dos archivos deben estar guardados en el mismo directorio.

Ahora se ejecuta run.py:
ernesto@grievous:~/bin/apirest$ python run.py
 * Running on http://127.0.0.1:5000/

Ahora se consulta el API con curl:
ernesto@grievous:~$ curl -i http://127.0.0.1:5000
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 64
Server: Eve/0.5.3 Werkzeug/0.9.6 Python/2.7.8
Date: Wed, 08 Apr 2015 00:09:14 GMT

{"_links": {"child": [{"href": "persona", "title": "persona"}]}}


Ahora se consulta a persona:
ernesto@grievous:~$ curl http://127.0.0.1:5000/persona
{"_items": [], "_links": {"self": {"href": "persona", "title": "persona"}, "parent": {"href": "/", "title": "home"}}, "_meta": {"max_results": 25, "total": 0, "page": 1}}

Lo que muestra la ejecución de run.py es lo siguiente:
ernesto@grievous:~/bin/apirest$ python run.py 
 * Running on http://127.0.0.1:5000/
127.0.0.1 - - [07/Apr/2015 19:39:14] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [07/Apr/2015 19:41:35] "GET /persona HTTP/1.1" 200 -

Se muestra las 2 peticiones en el log.

En próximo artículo se explicará el uso de Eve con mongodb.



22 mar. 2015

Geometría Analítica con Sympy (triángulos).

A continuación se tiene los artículos sobre sympy tratados anteriorente:


En este artículo se explicará las instrucciones de geometría analítica para el caso del triángulo.

A continuación el código del script:

#!/usr/bin/env python

from sympy.geometry import *


#Creacion de 4 puntos
P1 = Point(0, 0)
P2 = Point(3, 4)
P3 = Point(2, -1)
P4 = Point(-1, 5)

T = Triangle(P1, P2, P3) ; #Se crea el triangulo con los puntos p1,p2 y p3

print (T.area); #Se calcula el area del triangulo

print (T.angles); #Angulos del triangulo

print (T.sides);#Lados del triangulo

print (T.perimeter);#Perimetro

print (T.is_right())  ; #triangulo recto?

print (T.is_equilateral());#triangulo equilatero?

print (T.is_isosceles());#triangulo isosceles?

print (T.is_scalene());#triangulo escaleno?

print (T.altitudes);#Altitudes

print (T.orthocenter) ;#Intercepcion de las altitudes

print (T.bisectors()) ;#bisector

print (T.incenter) ;#en centro

print (T.incircle); #en circulo

print (T.inradius); #en radio

print (T.medians); #medianas

print (T.centroid);#intercepcion de las medianas

print (T.circumcenter); #intercepcion de bisectores perpendiculares

print (T.circumcircle)

print (T.circumradius)

print (T.medial)

C1 = Circle(P1, 3);#se crea un circulo con centro en P1 y radio 3.

print (T.intersection(C1));#intercepcion del triangulo con el circulo c1

print (T.distance(T.circumcenter)); #minima distancia desde un punto a otro de un segmento

print (T.is_similar(Triangle(P1, P2, P4)));# si dos triangulos son similares?




A continuación se muestra la salida del script:

-11/2
{Point(2, -1): acos(3*sqrt(130)/130), Point(0, 0): acos(2*sqrt(5)/25), Point(3, 4): acos(23*sqrt(26)/130)}
[Segment(Point(0, 0), Point(3, 4)), Segment(Point(2, -1), Point(3, 4)), Segment(Point(0, 0), Point(2, -1))]
sqrt(5) + 5 + sqrt(26)
False
False
False
True
{Point(2, -1): Segment(Point(6/25, 8/25), Point(2, -1)), Point(0, 0): Segment(Point(0, 0), Point(55/26, -11/26)), Point(3, 4): Segment(Point(4/5, -2/5), Point(3, 4))}
Point(10/11, -2/11)
{Point(2, -1): Segment(Point(3*sqrt(5)/(sqrt(5) + sqrt(26)), 4*sqrt(5)/(sqrt(5) + sqrt(26))), Point(2, -1)), Point(0, 0): Segment(Point(0, 0), Point(sqrt(5)/4 + 7/4, -9/4 + 5*sqrt(5)/4)), Point(3, 4): Segment(Point(-50 + 10*sqrt(26), -5*sqrt(26) + 25), Point(3, 4))}
Point((3*sqrt(5) + 10)/(sqrt(5) + 5 + sqrt(26)), (-5 + 4*sqrt(5))/(sqrt(5) + 5 + sqrt(26)))
Circle(Point((3*sqrt(5) + 10)/(sqrt(5) + 5 + sqrt(26)), (-5 + 4*sqrt(5))/(sqrt(5) + 5 + sqrt(26))), -11/(sqrt(5) + 5 + sqrt(26)))
-11/(sqrt(5) + 5 + sqrt(26))
{Point(2, -1): Segment(Point(3/2, 2), Point(2, -1)), Point(0, 0): Segment(Point(0, 0), Point(5/2, 3/2)), Point(3, 4): Segment(Point(1, -1/2), Point(3, 4))}
Point(5/3, 1)
Point(45/22, 35/22)
Circle(Point(45/22, 35/22), 5*sqrt(130)/22)
5*sqrt(130)/22
Triangle(Point(3/2, 2), Point(5/2, 3/2), Point(1, -1/2))
[Point(9/5, 12/5), Point(sqrt(113)/26 + 55/26, -11/26 + 5*sqrt(113)/26)]
sqrt(26)/11
False

El código del script anterior lo pueden ver en el siguiente enlace en bitbucket.org.

También se puede usar notebook (ipython notebook), el archivo que se utilizó se puede descargar en el enlace.

A continuación se muestra una figura de la ejecución del notebook:




AddThis