Cansada de ser feliz

Bienvenidos a mi flujo de conciencia

How to upload a file to Amazon S3 and generate a pre-signed URL

| Comments

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import datetime
import StringIO
import xlwt

from boto3.session import Session
from boto3 import client

# Create a file, for example, an Excel document.
xls = xlwt.Workbook()
ws = xls.add_sheet('A Test Sheet')
ws.write(0, 0, 1234.56)

file_name = 'example.xls'
output = StringIO.StringIO()
xls.save(output)

# Save the file to S3
session = Session(
    aws_access_key_id=config['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=config['AWS_SECRET_ACCESS_KEY'],
)
s3 = session.resource('s3')
s3_bucket_name = 'YourBucket'
s3_file_key = 'dir1/dir2/{}'.format(file_name)

result = s3.Bucket(s3_bucket_name).put_object(
    Key=s3_file_key,
    Body=output.getvalue()
)

# Generate a pre-signed URL
s3_client = client(
    's3',
    region_name='us-east-1',
    aws_access_key_id=config['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=config['AWS_SECRET_ACCESS_KEY'],
)

url = s3_client.generate_presigned_url(
    ClientMethod='get_object',
    Params={
        'Bucket': s3_bucket_name,
        'Key': s3_file_key,
    },
    ExpiresIn=60 * 60 * 24,  # Default: 3600
)

print(url)
# https://s3.amazonaws.com/YourBucket/dir1/dir2/example.xls?AWSAccessKeyId=ATPAJNUDN3ENT2I4S6EF&Expires=1477582080&Signature=POvreXScdYNES98SPFeAN3y12DL%3D

How to send an email with attachment via Amazon SES in Python

| Comments

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import xlwt
from boto3 import client

email_from = 'from@example.com'
email_to = 'to@example.com'

# Create a workbook
xls = xlwt.Workbook()
# Create a sheet
sheet = xls.add_sheet('Sheet name')
# Set styles for headers and dates
header_style = xlwt.easyxf('font: bold True;')
date_format = xlwt.XFStyle()
date_format.num_format_str = 'dd/mm/yyyy hh:mm:ss'

# Header
labels = [
    u'Title 1',
    u'Title 2',
]
content = [labels]

# Get data for other rows
for row in get_rows_with_content():
    content.append(list(row))

# Write it down
for row in range(len(content)):
    for col in range(len(content[row])):
        data = content[row][col]
        # If it's a header
        if row == 0:
            sheet.write(row, col, data, header_style)
            continue
        # If it's a date
        if type(data) == datetime.datetime:
            sheet.write(row, col, data, date_format)
        else:
            sheet.write(row, col, data)

# Set filename
today = datetime.date.today()
file_name = 'my_file_{}.xls'.format(today.strftime("%Y-%m-%d"))

# Save the file
output = StringIO.StringIO()
xls.save(output)

# Build an email
msg = MIMEMultipart()
msg['Subject'] = 'Hi, here is your file'
msg['From'] = email_from
msg['To'] = email_to
# What a recipient sees if they don't use an email reader
msg.preamble = 'Multipart message.\n'

# The attachment
part = MIMEApplication(output.getvalue())
part.add_header('Content-Disposition', 'attachment', filename=file_name)
part.add_header('Content-Type', 'application/vnd.ms-excel; charset=UTF-8')
msg.attach(part)

# Connect to Amazon SES
ses = client(
    'ses',
    region_name='us-east-1',
    aws_access_key_id=config['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=config['AWS_SECRET_ACCESS_KEY'],
)
# And finally, send the email
ses.send_raw_email(
    Source=email_from,
    Destinations=[email_to],
    RawMessage={
        'Data': msg.as_string(),
    }
)

If we need to create a ZIP archive first:

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
import zipfile
import tempfile
from email import encoders
from email.mime.multipart import MIMEMultipart, MIMEBase

# ....
output = StringIO.StringIO()
xls.save(output)

# Create zip file
zf = tempfile.TemporaryFile(prefix='fileprefix', suffix='.zip')
zip = zipfile.ZipFile(zf, 'w')
zip.writestr(file_name, output.getvalue())
zip.close()
zf.seek(0)

msg = MIMEMultipart()
msg['Subject'] = 'Hi, here is your file'
msg['From'] = email_from
msg['To'] = email_to

# What a recipient sees if they don't use an email reader
msg.preamble = 'Multipart message.\n'

# the attachment
part = MIMEBase('application', 'zip')
part.set_payload(zf.read())
encoders.encode_base64(part)
part.add_header(
    'Content-Disposition', 'attachment',
    filename=u'{}.zip'.format(file_name),
)
msg.attach(part)
# ....

Cómo pasar variable desde una vista a formulario en Django

| Comments

views.py
1
2
3
4
5
6
7
8
9
10
11
12
class MyCreateView(CreateView):
    model = MyModel
    template_name = 'myapp/my_form.html'
    form_class = MyForm

    def get_form_kwargs(self):
        kwargs = super(MyCreateView, self).get_form_kwargs()

        # La variable que queremos pasar al formulario
        kwargs.update({'current_user': self.request.user})

        return kwargs
forms.py
1
2
3
4
5
6
7
class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        # Recibir la variable y borrarla del listado de argumentos.
        current_user = kwargs.pop('current_user')

        super(MyForm, self).__init__(*args, **kwargs)

Herramientas para el desarrollo que nos hacen la vida más fácil

| Comments

Editores

Plugins para Sublime Text

Para llamar Package Control desde Sublime Text: Control+Shift+P, escribe “install” y selecciona Package Control: Install Package en Command Palette.

Editar y compartir código en línea

  • CodePen: editor de código HTML/JS/CSS en línea.
  • repl.it: editor de Python 3 que permite compartir el código.
  • Dillinger: editor de Markdown.

Bases de datos

Git

Screenshots

  • Shutter: aplicación para tomar y editar screetshots en Linux.

REST

  • PostMan: es una extensión del Google Chrome, que permite el envío de peticiones HTTP.

Diff y validación

CSS/Iconos

Múltiples bases de datos en Django

| Comments

Tenemos dos tablas con esquemas iguales dos en bases de datos diferentes: production y history.

settings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DATABASE_ROUTERS = ['routers.HistoricRouter']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'localhost',
        'NAME': 'production',
        'USER': 'secret',
        'PASSWORD': 'secret',
    },
    'historical': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'localhost',
        'NAME': 'history',
        'USER': 'secret',
        'PASSWORD': 'secret',
    }
}

Escribimos un router para definir a qué base de datos hacer la petición:

routers.py
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
35
36
class HistoricRouter(object):
    """
    A router to control all database operations on models in the historic application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read historic models go to historical.
        """
        if model._meta.app_label == 'historic':
            return 'historical'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write historic models go to historical.
        """
        if model._meta.app_label == 'historic':
            return 'historical'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the historic app is involved.
        """
        if obj1._meta.app_label == 'historic' or \
            obj2._meta.app_label == 'historic':
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the historic app only appears in the 'historical' database.
        """
        if app_label == 'historic':
            return db == 'historical'
        return None

En nuestros modelos especificamos app_label para referirnos a la base de datos distinta a default:

models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Author(models.Model):

    class Meta:
        db_table = 'authors'

class AbstractBook(models.Model):
    author = models.ForeignKey('books.Author', related_name='books')
    created_at = models.DateTimeField()

    class Meta:
        unmanaged = True
        abstract = True

class Book(Book):

    class Meta:
        db_table = 'books'

class HistoricBook(Book):

    class Meta:
        unmanaged = True
        db_table = 'books'
        app_label = 'historic'

La petición Book.objects.all() va a traer libros de la base de datos production y HistoricBook.objects.all() - de la base de datos history.

Modificando allow_relation en nuestro router, podemos permitir llaves foráneas entre modelos de diferentes bases de datos:

1
2
def allow_relation(self, obj1, obj2, **hints):
    return True

Enlaces:

Cómo hacer integración con Tpaga API usando Python

| Comments

Tpaga es una plataforma que permite recibir pagos electrónicos. Tiene una estructura sencilla para entender y fácil para usar.

Para obtener nuestros claves de acceso y conectarnos con el API de Tpaga, creamos una cuenta en el “sandbox” de la plataforma: sandbox.tpaga.co.

Al registrarnos podemos ver que ahora tenemos dos claves que podemos usar para la autenticación: Private Api Key y Public Api Key:

No llevas leña

| Comments

El bus lleno de Buratinos* va a toda velocidad. En algún momento el conductor frena duro, y escucha las voces enojadas del los pasajeros: “¡Ey, señor conductor, no lleva la leña!”

*Buratino es Pinocchio en ruso.