Planeta PythonBrasil

PythonBrasil[10]

February 11, 2016

Álvaro Jüsten

Fatura do NuBank em CSV

O Que é NuBank?

Para quem não conhece, NuBank é um cartão de crédito que não te cobra anuidade e tem um ótimo atendimento, além de um aplicativo para celular bastante fácil de usar (é sua única interface com eles). Em resumo: eles estão fazendo o que em geral os bancos não fazem e estão lucrando com isso! Já faz alguns meses que estou usando e recomendo! Se quiser saber mais detalhes acesse a página deles.

Problemas

O NuBank, apesar de muito bom, para mim poderia melhorar em alguns detalhes:

  • Ainda não dá para transferir as milhas do cartão para compra de passagens aéreas;
  • Ainda não consigo colocar o pagamento da fatura como débito automático no Banco do Brasil (parece que só está disponível para Santander);
  • É chato ter que usar o PDF da fatura para conferir meus gastos.

Quando estou no Brasil tento usar o NuBank para todas as compras, pois isso facilita muito o controle dos meus gastos - caso não concorde que gastar no cartão é melhor para ter o controle dos gastos então leia o adendo (quando estou fora evito usar o cartão de crédito pois o IOF para compras internacionais é de 6,38%). Porém, como a fatura vem por email em PDF e não dá pra exportar para outros formatos pelo aplicativo, torna-se trabalhoso (e chato) demais o processo de jogar os gastos para minha planilha financeira pessoal e conferí-los. Como adoro programar e programar é, em resumo, automatizar coisas, resolvi criar um software para fazer a conversão da fatura do NuBank PDF para CSV (que antes eu fazia manualmente). :D

Convertendo a Fatura

O código do software está todo na minha conta no GitHub, chama-se nubank-to-csv. Ele foi escrito em Python usando a minha biblioteca rows (para facilitar a extração para CSV e qualquer outro formato tabular) e a biblioteca lxml para extrair os dados do HTML que é gerado pelo pdftohtml. Fique à vontade para contribuir com o script caso você manje dos paranauê (fiz uma lista de sugestões de contribuição).

O fluxo então é esse:

Fatura em PDF >[pdftohtml]> Fatura em HTML >[nubank-to-csv]> Fatura em CSV

Depois de instalar o nubank-to-csv e suas dependências basta rodar dois comandos:

pdftohtml fatura.pdf
python nubank.py faturas.html fatura-lindona.csv

O arquivo na segunda linha é faturas.html (com "s") mesmo -- esse arquivo é gerado pelo pdftohtml (junto com outros) depois da conversão. Você pode deletar os arquivos gerados pelo pdftohtml depois disso. Esse comando não segue bem a filosofia do UNIX e é bem inflexível (não dá pra especificar o nome do arquivo de saída, por exemplo). :-/

Uma coisa legal do script é que ele já junta as entradas relacionadas a IOF com o gasto que gerou o IOF, facilitando bastante meu controle! :D

Convertendo o CSV para Outro Formato

Se você tem a command-line interface da biblioteca rows instalada (pip install rows ou apt-get install rows) você também pode converter o CSV para diversos outros formatos, como XLS, XLSX, JSON, SQLite, HTML e TXT. Para converter, basta rodar:

rows convert fatura-lindona.csv fatura.xls

Caso queira outro formato em vez de XLS, basta trocar "xls" ali pela extensão desejada que o software é esperto o suficiente para identificar. :)

Adendo: Controle das Contas

Muita gente prefere evitar o uso do cartão de crédito para ter mais controle das contas (já vi muitos conselhos vindos de profissionais do ramo financeiro sobre evitar o uso do cartão). Eu prefiro usar o cartão sempre que possível (quando estou no Brasil) pois quando gasto em dinheiro em geral esqueço facilmente com o que gastei (e eu gosto de saber com o que gastei meu dinheiro - essa é a primeira ação para conseguir manter uma vida financeira saudável).

Muita gente se assusta quando eu digo isso porque a maior parte das pessoas que usa o cartão de crédito tem "uma surpresa" quando chega a fatura. Eu nunca tenho surpresas pois:

  • Na planilha onde controlo meus ganhos e gastos já tenho estimativas de entrada e saída para os próximos meses;
  • Duas vezes por semana vejo pelo aplicativo do NuBank o que gastei nos últimos dias e lanço na planilha do próximo mês (que é quando terei que pagar a fatura); e
  • Quando a fatura chega eu a converto para CSV, abro no LibreOffice e confiro se está de acordo com o que lancei na minha planilha financeira anteriormente.

Com esses simples passos eu sei exatamente o valor da fatura que virá e tenho como me conter caso esteja gastando demais!

Curtiu? Que tal compartilhar com seus amigos? ;)

Dúvidas e sugestões? Comente! :)

por noreply@blogger.com (Álvaro Justen "Turicas") em 11 de February de 2016 às 14:12

PythonClub

Salvando gráfico de contribuições do Github com Python e Selenium

Como alguns sabem, sou apaixonado por Python. Atualmente, minha linguagem favorita por conta de sua simplicade e poder (além de ficar LINDJA toda indentada, hahahaha).

Uma das coisas mais legais da linguagem é a enorme quantidade de bibliotecas disponíveis. Cada dia que abro um grupo de discussão acabo conhecendo alguma funcionalidade interessante. Se você faz parte de algum desses grupos, provavelmente já viu o post do Alex Recker "Using Selenium to Buy a Bus Pass", em que ele mostra como automatizou a compra de passagens de ônibus com Selenium e Python.

Eu já havia ouvido falar do Selenium, mas nunca tinha experimentado na prática e o post do Alex foi o empurrão que faltava.

Obviamente, meu projetinho é bem mais simples, mas foi algo divertido de se fazer como forma de aprendizado. Batizei-o de GHSS(Github Screenshot). Como o próprio nome sugere, ele entra no seu perfil do Github e tira um screenshot do gráfico de contribuições, salvando com a data atual.

Abaixo, irei mostrar como fazer. Visto que há muita gente que usa Python sem ser programador por profissão, tentarei explicar de forma mais simples possível. O código completo pode ser encontrado no meu Github.


Neste código, utilizaremos o Python2.

Primeiramente, temos que importar todas as bibliotecas necessárias.

Na linha 1, importamos o "OS", que será utilizado para "acharmos" o arquivo secrets.yml. Explicarei daqui a pouco.

Na linha 2, importamos do Selenium o Webdriver, responsável pela automatização (abertura das páginas e preenchimento dos campos).

Nas próximas duas linhas, importamos as bibliotecas restantes que são responsáveis pelo nosso arquivo secrets.yml, no qual o username e password serão guardados, e pela data que será salva no nome do arquivo final.

Na última linha, importamos o responsável por tirar o screenshot.

import os
from selenium import webdriver
import yaml
from datetime import date
import pyscreenshot as ImageGrab

Neste bloco de código, mostramos ao nosso programa onde está nosso arquivo secrets.yml e o carregamos.

cur_dir = os.path.dirname(os.path.realpath(__file__))
secret_path = os.path.join(cur_dir, 'secrets.yml')

with open(secret_path, 'r') as stream:
    data = yaml.load(stream)
    USERNAME = data.get('user','')
    PASSWORD = data.get('password')

O arquivo secrets.yml é composto por apenas dois campos, "password" e "user", que, PASMEM, são para inserir sua senha e seu usuário.

password: senha_do_zezinho_hacker
user: zezinhohacker123

Nestas três linhas abrimos o Firefox, passamos para ele qual o endereço desejamos acessar e maximizamos a janela, respectivamente.

driver = webdriver.Firefox()
driver.get("https://github.com/login")
driver.maximize_window()

Aqui é onde a "mágica" acontece.

Na primeira linha, a propriedade "find_element_by_id" busca o campo "login_field", onde devemos inserir o nome de usuário. Na linha posterior, enviamos aquele username informado lá no secrets, lembra?

Nas próximas duas linhas, é feito o mesmo procedimento, mas, desta vez, com a senha.

Na última, clicamos o botão para logarmos.

email = driver.find_element_by_id("login_field")
email.send_keys(USERNAME)
senha = driver.find_element_by_id("password")
senha.send_keys(PASSWORD)
driver.find_element_by_name('commit').click()

Nesta linha, nós entramos no nosso perfil do Github.

Quando utilizamos {0}, "guardamos" o espaço, para o que informarmos adiante. Ou seja, no espaço reservado, será inserido o username.

driver.get("https://github.com/{0}" .format(USERNAME))

Por exemplo, se fizermos o seguinte código:

print("Meus esportes preferidos são: {0}, {1} e {2}" .format("futebol", "basquete", "corrida"))

O resultado será:

 Meus esportes preferidos são: futebol, basquete e corrida.

Deu para entender?


Na última linha do programa, salvamos a imagem.

No campo bbox, informamos qual área da tela queremos dar o screenshot, na ordem: X1, Y1, X2, Y2. Você pode alterá-lo de acordo com seu navegador.

No save, utilizamos o que ensinei acima para gerar o arquivo da seguinte maneira: "dataatual_gitshot_nomedousuario".

img = ImageGrab.grab(bbox=(460,540,770,208)).save("{0}_gitshot_{1}.png" .format(date.today(), USERNAME))

Este será o resultado. O nome do arquivo, no meu caso, ficou "2016-01-24_gitshot_othonalberto.png".

Resultado


Código completo:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os

from selenium import webdriver
import yaml
from datetime import date
import pyscreenshot as ImageGrab

cur_dir = os.path.dirname(os.path.realpath(__file__))
secret_path = os.path.join(cur_dir, 'secrets.yml')

with open(secret_path, 'r') as stream:
    data = yaml.load(stream)
    USERNAME = data.get('user','')
    PASSWORD = data.get('password')

driver = webdriver.Firefox()
driver.get("https://github.com/login")
driver.maximize_window()

email = driver.find_element_by_id("login_field")
email.send_keys(USERNAME)
senha = driver.find_element_by_id("password")
senha.send_keys(PASSWORD)
driver.find_element_by_name('commit').click()

driver.get("https://github.com/{0}" .format(USERNAME))

img = ImageGrab.grab(bbox=(460,540,770,208)).save("{0}_gitshot_{1}.png" .format(date.today(), USERNAME))
# bbox=(X1, Y1, X2, Y2)

É isso! Espero ter contribuído com o conhecimento de vocês com este post e gerado curiosidade para que experimentem o Selenium.

Quem quiser contribuir, seja com código ou sugestões, sinta-se à vontade.

Abraços!

por Othon Alberto em 11 de February de 2016 às 13:47

February 10, 2016

Filipe Saraiva

Cantor migrando para o Phabricator: que ferramentas nossos contribuidores devem usar

Projetos e softwares desenvolvidos pela comunidade KDE estão migrando para uma nova ferramenta que irá gerenciar nossos códigos, commits, revisões, tarefas, e mais. Esta ferramenta é o Phabricator e você pode visitar a instância dela para os projetos do KDE neste endereço.

Desde novembro de 2015 estamos migrando o Cantor para o Phabricator. Após nossa primeira revisão de código finalizada com sucesso alguns dias atrás, decidi escrever um post sobre que ferramentas nossos contribuidores devem utilizar enquanto o processo de migração não é finalizado.

Projeto

Phabricator tem uma aplicação para gerenciamento de projetos onde podemos colocar algumas informações úteis e coordenar a execução de tarefas. A página de projeto do Cantor está online e configurada.

Outra interessante funcionalidade é a possibilidade de ingressar em um projeto ou apenas acompanhar suas atividades. Se você tiver uma KDE Identity, faça o login no KDE Phabricator e siga-nos!

Workboard

O KDE oferece uma aplicação para gerenciamento da execução de tarefas utilizando um quadro estilo kanban, o KDE TODO. Apesar dela ser uma excelente ferramenta, nós nunca a utilizamos para o Cantor.

A aplicação Projects no Phabricator tem uma funcionalidade com este mesmo objetivo, o Workboard. Atualmente a estamos utilizando para acompanhar as tarefas do estudante Fernando Telles durante o SoK. A partir dessa experiência, pretendo utilizar o Workboard para gerenciar o desenvolvimento do Cantor.

Tarefas, bugs, desejos

A aplicação do Phabricator chamada Maniphest é uma ferramenta para criação e acompanhamento de bugs, tarefas e desejos (sugestões de funcionalidades).

Entretanto no KDE temos um Bugzilla pesadamente customizado, fazendo com que para o momento não tenhamos uma decisão sobre como migrar nossa ferramenta de relatórios de bugs.

Portanto, KDE Bugzilla ainda é nossa ferramenta para relatórios de bugs. Apesar disso, convido os contribuidores para que usem o Maniphest para submissão de desejos de novas funcionalidades. Nós nunca utilizamos o Bugzilla para este último objetivo, então não haverá problemas se começarmos a utilizar essa nova ferramenta para isso.

Repositório

A exemplo da maioria dos projetos do KDE, Cantor tem seu código fonte gerenciado pelo git. Phabricator tem uma aplicação chamada Diffusion que permite navegar e ver uma variedade de dados a partir de um repositório de código fonte.

Esta aplicação está configurada para o Cantor e pode ser visitada neste link.

Revisão de código

A aplicação do Phabricator para revisão de código é chamada Differential e também está disponível para o Cantor.

Entretanto ainda não há uma decisão sobre a migração definitiva e desativação da ferramenta atual de revisão de código usada pelo KDE, o Reviewboard. Portanto, nossos colaboradores podem utilizar uma ou outra ferramenta (mas por favor nunca as duas ao mesmo tempo!). Ainda assim, fica a recomendação para que deem prioridade e comecem a utilizar o Differential.

Wiki

Sim, Phabricator também tem sua própria aplicação para páginas wiki, chamada Phriction. Atualmente o Cantor só tem uma página na wiki Userbase. Como nós não utilizamos wiki no momento, só decidiremos se iremos utilizar o Phriction em algum momento no futuro.

Comunicação

Ok, o Phabricator também tem uma ferramenta própria para comunicação, Conpherence. Entretanto, os colaboradores do Cantor devem continuar a usar nossas atuais ferramentas de comunicação providas pelo KDE Edu, o canal IRC #kde-edu na rede Freenode e a  lista de e-mail do KDE Edu.

Apesar de eu ter algumas críticas sobre o Phabricator (por exemplo, eu não gosto da arquitetura Aplicações -> Projeto; prefiro Projeto -> Aplicações), ela é uma ferramenta muito interessante para o gerenciamento de projetos e tem uma ampla gama de aplicações para trabalhos específicos. Neste texto eu listei várias delas, mas ainda há muitas outras para serem exploradas e avaliadas.

Espero que este post possa ajudar os colaboradores do Cantor sobre qual ferramenta deve ser utilizada para desempenhar algum trabalho do projeto. Talvez o texto também sirva para apresentar algumas novidades para futuros usuários do Phabricator e ajude desenvolvedores do KDE durante o processo de migração. Por último, principalmente para o público de não colaboradores, o texto apresenta algumas das ferramentas que usamos no KDE.

O impacto do Phabricator na comunidade KDE é algo que deverá ser analisado no futuro próximo. Essa ferramenta e suas diversas aplicações poderão mudar consideravelmente como os subprojetos no KDE são organizados. Vamos ver o que o futuro revelará para nós.

por Filipe Saraiva em 10 de February de 2016 às 16:16

February 08, 2016

Aprenda Python

Eu devo usar Python 2 ou Python 3?

Afinal, qual Python usar? Se você vai começar a estudar Python agora ou iniciar um projeto novo, escolha Python 3 e leia Por onde começar com Python? Nesse artigo vou citar alguns motivos para não usar Python 2, quais são as diferenças entre o Python 2 o Python 3 e como saber se as ferramentas que você pretende usar são compatíveis com Python 3. Python 2 já vem sendo chamado de "Python

por Vinicius Assef (noreply@blogger.com) em 08 de February de 2016 às 15:31

February 04, 2016

Gustavo Niemeyer

mgo r2016.02.04

This is one of the most packed releases of the mgo driver for Go in recent times. There are new features, important fixes, and relevant
internal reestructuring to support the on-going server improvements.

As usual for the driver, compatibility is being preserved both with old applications and with old servers, so updating should be a smooth experience.

Release r2016.02.04 of mgo includes the following changes which were requested, proposed, and performed by a great community.

Enjoy!

Exposed access to individual bulk error cases

Accessing the individual errors obtained while attempting a set of bulk operations is now possible via the new mgo.BulkError error type and its Cases method which returns a slice of mgo.BulkErrorCases which are properly indexed according to the operation order used. There are documented server limitations for MongoDB version 2.4 and older.

This change completes the bulk API. It can now perform optimized bulk queries when communicating with recent servers (MongoDB 2.6+), perform the same operations in older servers using compatible but less performant options, and in both cases provide more details about obtained errors.

Feature first requested by pjebs.

New fields in CollectionInfo

The CollectionInfo type has new fields for dealing with the recently introduced document validation MongoDB feature, and also the storage engine-specific options.

Features requested by nexcode and pjebs.

New Find and GetMore command support

MongoDB is moving towards replacing the old wire protocol with a command-based based implementation, and every recent release introduced changes around that. This release of mgo introduces support for the find and getMore commands which were added to MongoDB 3.2. These are exercised whenever querying the database or iterating over queried results.

Previous server releases will continue to use the classical mechanism, and the two approaches should be compatible. Please report any issues in that regard.

Do not fallback to Monotonic mode improperly

Recent driver changes adapted the Pipe.Iter, Collection.Indexes, and Database.CollectionNames methods to work with recent server releases. These changes also introduced a bug that could cause the driver to talk to a secondary server improperly, when that operation was the first operation performed on the session. This has been fixed.

Problem reported by Sundar.

Fix crash in new bulk update API

The new methods introduced in the bulk update API in the last release were crashing when a connection error occurred.

Fix contributed by Maciej Galkowski.

Enable TCP keep-alives for all connections

As requested by developers, TCP keep-alives are now enabled for all connections. No timing is specified, so the default operating system setting will be used.

Feature requested by Hunor Kovács, Berni Varga, and Martin Garton.

ChangeInfo.Updated now behaves as documented

The ChangeInfo.Updated field is documented to report the number of documents that were changed, but in fact that was not possible in old releases of the driver, since the server did not provide that information. Instead, the server only reported the number of documents matched by the selection document.

This has been fixed, so starting with MongoDB 2.6, the driver will behave as documented, and inform the number of documents that were indeed updated. This is related to the next driver change:

New ChangeInfo.Matched field

The new ChangeInfo.Matched field will report the number of documents that matched the selection document, whether the performed change was a removal, an update, or an upsert.

Feature requested by Žygimantas and other list members.

ObjectId now supports TextMarshaler/TextUnmarshaler

ObjectId now knows how to marshal/unmarshal itself as text in hex format when using its encoding.TextMarshaler and TextUnmarshaler interfaces.

Contributed by Jack Spirou.

Created GridFS index is now unique

The index on {“files_id”, “n”} automatically created for GridFS chunks when a file write completes now enforces the uniqueness of the key.

Contributed by Wisdom Omuya.

Use SIGINT in dbtest.DBServer

The dbtest.DBServer was stopping the server with SIGKILL, which would not give it enough time for a clean shutdown. It will now stop it with SIGINT.

Contributed by Haijun Wang.

Ancient field tag logic dropped

The very old field tag format parser, in use several years back even before Go 1 was released, was still around in the code base for no benefit.

This has been removed by Alexandre Cesaro.

Documentation improvements

Documentation improvements were contributed by David Glasser, Ryan Chipman, and Shawn Smith.

Fixed BSON skipping of incorrect slice types

The BSON parser was collapsing when an array value was unmarshaled into an existing field that was not of an appropriate type for such values. This has been fixed so that the the bogus field is ignored and the value skipped.

Fix contributed by Gabriel Russel.

por niemeyer em 04 de February de 2016 às 19:44

February 02, 2016

Álvaro Jüsten

Detecting File Type and Encoding In Python

I was looking for a simple and fast Python library to implement proper file type detection and encoding detection into my rows library and found that there are many libraries available on Python Package Index which claim to do it. None of them attracted me because one of the following reasons:

  • Do not have a pythonic implementation,
  • Is not available on Debian to install as a package (it's important so people can install rows and its dependencies using pip or apt-get),
  • Is not maintained anymore, or
  • Have some missing feature.

None seemed to be the de-facto way to do it in Python (I think pythonistas do it in many ways).

But there should be one -- and preferably only one -- obvious way to do it.

So, I thought: why not use the file software, which is well known by all UNIX hackers, faster and most accurate than all the other solutions I know? To my surprise there was not such a good Python binding for file on PyPI (and calling it as a child process was not and option since it would add one more system-dependant package, not detectable during a pip install if missing and would also turn this solution less portable). Then, searching on its repository I found a simple Python wrapper, which was not available on PyPI at that time and was not that pythonic as I expected.

The Solution

Since it's free/libre software, I've created an issue on file bug tracker to solve the problem and Christos Zoulas (the current maintainer) asked for a patch, which I implemented, sent and was accepted. I'm pretty happy I can contribute to a software I've been using since my earlier times on GNU/Linux (2003? 2004?). During this process I found that the first commit on the free/libre file (which every GNU/Linux distribution and BSD flavor uses) implementation was done when I was less than 4 months old (!) -- and it's still maintained today.

Now you can use the official file Python binding: the new library is called file-magic and can be installed by running:

pip install file-magic

It provides some methods and attributes but the most important are pretty simple and intuitive to use: they return a namedtuple with the data you want! Let's take a tour through an example:

>>> import magic

>>> # You can pass the filename and it'll open the file for you:
>>> filename_detected = magic.detect_from_filename('turicas.jpg')
>>> print filename_detected
FileMagic(mime_type='image/jpeg', encoding='binary',
          name='JPEG image data, JFIF standard 1.02, aspect ratio, density 1x1, segment length 16, progressive, precision 8, 842x842, frames 3')
>>> # It's a `namedtuple` so you can access the attributes directly:
>>> print filename_detected.mime_type
image/jpeg

# If you have the file contents already, just use `detect_from_content`:
>>> with open('data.html') as fobj:
...     data = fobj.read()
>>> content_detected = magic.detect_from_content(data)
>>> print content_detected
FileMagic(mime_type='text/html', encoding='utf-8',
          name='HTML document, UTF-8 Unicode text')
>>> print content_detected.encoding
utf-8

There are still some things to be improved (like running tests in other platforms -- including Python 3) but it's pip-installable and usable now, so we can benefit from it. Feel free to contribute. :)

Hope you enjoy it!

por noreply@blogger.com (Álvaro Justen "Turicas") em 02 de February de 2016 às 13:15

January 29, 2016

Aprenda Python

Como percorrer duas listas de tamanhos diferentes

Temos a seguinte situação: preciso percorrer duas listas de tamanhos diferentes, mas com equivalência de posições. Como resolver? Versão TL;DR: aumente a lista menor com extend(), antes de percorrê-las com zip(). Agora, a solução detalhada abaixo. Exemplo: >>> brinquedos = ["carrinho", "bola", "raquete"] >>> cores = ["azul", "branca"] O resultado precisa ser: carrinho azul bola branca

por Vinicius Assef (noreply@blogger.com) em 29 de January de 2016 às 11:00

January 28, 2016

Rodrigo Delduca

Turbinando o carregamento de imagens remotas no Android

image

Recentemente, precisei fazer um fine tuning de carregamento de imagens num app.

O cenário era o seguinte: O app consiste em uma série de cardviews com imagens de background oriundos de diversos locais da internet, resultando em alguns problemas:

  • Resolver DNS para cada domínio
  • Handshake SSL para cada domínio
  • Algumas imagens são infinitamente maiores do que o necessário
  • Formatos diferentes (JPEG, PNG, GIF, WEBP, etc)
  • Latência alta ou mesmo instabilidade
  • O fato das imagens serem bem grandes, que imposibilita o OkHttp de fazer cache
  • Alto uso de memória
  • Não é legal fazer Inline linking

Somando todos esses fatores, a experiência do usuário é prejudicada pela baixa performance.

Resolvi então usar o thumbor como proxy. O thumbor é um projeto open-source mantido pela Globo, que permite aplicar diversos efeitos, cortes, ajustes e filtros em imagens, tudo configurável pela URL.

Exemplo http://thumbor.thumborize.me/unsafe/300x100/filters:rotate(90)/placehold.it/250x150/ff0000

Existe um recurso que usa visão computacional para identificar rostos numa foto e no momento de recortar não cortar nenhum pescoço e/ou enquadrar todas as pessoas.

Com o thumbor eu posso redimensionar a imagem para o tamanho exato que cada dispositivo vai precisar, converter para outro formato menor, remover todas as informações ICC embutidas no arquivo, além de outros filtros disponíveis.

Deploy do thumbor

O deploy do thumbor é bem simples; existem imagens do Docker prontas para serem usadas, buildpacks do Heroku, Elastic Beanstalk ou mesmo manual. Optei por usar o OpsWorks com o Chef, na verdade já existia até um cookbook do thumbor pronto para ser usado.

Content Delivery Network (CDN)

Aproveitei e criei uma CDN usando o CloudFront, assim as respostas do thumbor seriam cacheadas e distribuídas por todo os “points of presence (PoPs)”.

Pollexor

No Android temos uma biblioteca chamada pollexor, que permite escrever a URI do thumbor de uma maneira elegante usando fluent interface. Como já fazia uso do picasso para o carregamento de imagens, usei o picasso-pollexor, que funciona como um RequestTransformer para o Picasso, adicionando alguns parâmetros extras na URL de modo que a imagem seja recortada e enquadrada no tamanho exato do ImageView.

Thumbor thumbor = Thumbor.create(BuildConfig.THUMBOR_URL, BuildConfig.THUMBOR_KEY);
final RequestTransformer transformer =
    new PollexorRequestTransformer(thumbor);

Picasso picasso = new Picasso.Builder(context)
    .requestTransformer(transformer)
    .downloader(new OkHttp3Downloader(httpClient))
    .build();

Desta forma, o uso do Picasso em conjunto com o thumbor fica totalmente transparente para o programador. Ao informar o tamanho da imagem e o tipo de crop, o PollexorRequestTransformer entra em ação alterando os parâmetros da URL, de forma totalmente transparente. Prático, não? :)

@Bind(R.id.background)
ImageView background;

...

picasso.load(url)
  .fit()
  .centerCrop()
  .into(background);

Com isso, resolvemos uma série de problemas:

  • Será executado apenas um handshake de SSL
  • Apenas um domínio a ser consultado no DNS
  • A imagem é servida no tamanho exato que será exibida
  • A latência é muito menor devido a CDN
  • Possibilidade cachear em disco ou memória devido ao tamanho reduzido da imagem

Resultado

Normal Otimizado
image image

Já acabou, Jéssica?

O próximo passo é descobrir o sentido em que o usuário está rolando a lista e ir fazendo pre-fetch de algumas imagens, passando a impressão de carregamento instantâneo.

28 de January de 2016 às 00:00

January 27, 2016

Julio Cesar Eiras Melanda

Programando em Python no ATOM

Olá!

Depois de um bom tempo estou aqui de novo escrevendo um tutorial!

Bom, tenho me afastado do PyCharm ultimamente, e descobri no Atom uma ótima solução para meus projetos, então resolvi compartilhar com vocês o que aprendi até agora para um bom setup do ATOM para Python!

Atom é um editor de código criádo pela equipe do GitHub, com intuito de ser um editor simples, poderoso e muito customizável. Ele é feito em node.js, e possui uma quantidade enorme de plugins.

Uma de suas características mais legais é que tem um gerenciador de pacotes visual, diferente de editores como o sublime oonde esse tipo de gerenciador visual tem que ser instalado posteriomente.

Então, antes de qualquer coisa, baixe a versão do Atom pro seu sistema operacional em atom.io.

Depois de instala-lo, vamos às suas configurações.

Logo após a instalação vc tem uma página de boas vindas com várias configurações. Vamos pulá-las por enquanto. Feche todas as abas e digite Ctrl+,. Isto abrirá a tela de configurações.

 Na seção de "Core Settings" vamos manter tudo como está.

Em "Editor Settings" vamos habilitar o checkbox "Back Up Before Saving", para evitar corromper arquivos caso haja alguma falha durante o salvamento.

Mais abaixo, em "Prefered Line Length" você pode ajustar o comprimento de linha que prefere. Não sou muito xiita de pep8 com isto, então gosto de uma valor entre 100 e 120. É mais agradável, especialmente em monitores mais largos ou com resolução muito alta.

Um pouco mais abaixo, eu gosto de habilitar "Show Ident Guide" e "Show Invisibles". O primeiro para eu não exceder o limite de tamanho de linha que coloquei, e o segundo para evitar que ecentualmente fique um espaço sobrando, ou ter certeza se estão sendo usados tabs ou espeços para identação.

Se você vai trabalhar com web, é interessante habilitar "Soft Wrap At Prefered Line Length" para não ter que ficar scrollando a tela para os lados para ver todo o texto, já que HTML e javascript tem sempre uma tendência a ter linhas mais longas, especialmente HTML.

A seguir, temos a opção TabLength. o valor 2 é o padrão do editor, mas eu gosto mais de 4, então sempre altero. De qualquer forma, o padrão do ATOM para Python já é 4.

Agora, no menu do lado esquerdo, vamos em Install.

Vamos instalar os pacotes:

  • linter-flake8
  • linter-pep8
  • autocomplete-python

Caso você vá programar com Django, outro pacote interessante é o django-templates.

Instalo flake8 e pep8 porque alguns pontos um cobre melhor que o outro, mas caso prefira pode ser somente um dos dois.

Após esta instalação, o Atom já estará configurado para seus projetos Python.

Porém, se você abrir uma pasta de projeto Python pelo menu de arquivos do ATOM ou o comando Ctrl+O, verá que nem tudo parece funcionar como esperado. especialmente o autocomplete.

Isto acontece porque o atom usa as ferramentas pep8, flake8 e jedi do python para seus plugins, assim, para o atom funcionar corretamente, devemos ter estes pacotes python instalados.

Assim, o que recomendo fazer é, criar um ambiente virtual para seu projeto. Aliás, isto não é importante somente para o funcionamento do ATOM, mas é importante para projetos python em geral.

Após seu virtualenv criado e configurado, instale nele o pep8, o flake8 e o jedi. Então, com o virtualenv ativado, abra o atom pela linha de comando, especificando o diretório do projeto.

Por exemplo

[(meu_venv) julio@localhost ~]$ atom meu_projeto/

E não se preocupe, o ATOM não ficará preso to terminal, então, depois de abri-lo pela linha de comando, pode fechar o terminal tranquilamente.

Fazendo desta forma, você garante não somente que os linters e o autocomplete vão funcionar corretamente, quanto que serão para a versão correta do Python, pois se seu virtualenv for de Python3, estas ferramentas serão as do Python3.

Bom, espero que gostem tanto de programar no ATOM quanto eu estou gostando!

Um grande abraço e até a próxima!

por admin em 27 de January de 2016 às 18:17

January 21, 2016

Alien Retro

Fleepy – Um Cliente para a API do Fleep

Para quem não sabe, Fleep é um chat que tem como foco ser simples, rápido e almeja substituir o seu email. Já tendo usado Fleep, Slack e Hipchat, todos eles possuem diferentes casos de uso. Mas se você ou seu time precisa lidar com muita gente de fora da sua organização, Fleep é certamente algo a ser considerado já que a sua integração com email é um recurso que permite você adicionar convidados usando seus emails sem que eles necessitem ter uma conta no Fleep ou a aplicação instalada.

Dito isso, estou desenvolvendo um projeto para a Universidade de Tartu e esse projeto necessita que eu use a API do Fleep. É uma API RESTful simples, porém eu não consegui achar um cliente atualizado para ela. Então resolvi criar essa biblioteca em um par de dias.

Ela é extremamente simples e tenta espelhar a API da maneira mais fiel possível.

Alguns exemplos:

Criar uma sala de chat com um tópico e mensagem inicial

O exemplo abaixo loga, cria uma sala de chat com 3 pessoas e envia uma mensagem. A mensagem usa recursos de estilo do Fleep, você pode vê-los aqui.

from fleepy import Fleepy
 
api = Fleepy()
api.account.login("your@email.com", "yourpassword")
 
 
api.conversation.create(
    topic='This is a Room Topic',
    emails=['your@email.com', 'guest1@email.com', 'guest2@email.com'],
    message="""*Hello*, everyone!
 
    Something has just been posted in our Issue tracker.
 
    http://issue.tracker.com<>
    """)
 
api.account.logout()

Fazendo o upload de um arquivo

from fleepy import Fleepy
 
api = Fleepy()
api.account.login("your@email.com", "yourpassword")
 
api.file.upload('/path/to/afile.jpg')
api.account.logout()

O código se encontra em https://github.com/nicholasamorim/fleepy

Use Facebook to Comment on this Post

por Nicholas Amorim em 21 de January de 2016 às 12:17

January 15, 2016

Eric Hideki

Como melhorar meu código em Python, tornando o mais pythonico, legível e fácil de dar manutenção?

Assisti a palestra do Raymond Hettinger sobre qualidade, legibilidade e discussões sobre a PEP 8.

A indico para aqueles que, assim como eu, tem conhecimentos intermediários em Python e deseja transformar seu código mais pythonico.

Para ser sincero, acho que todos devem assistir isso.

Mas o ponto mais legal e que quero compartilhar é essa lista de propostas de como criar códigos Python pythonicamente, de forma mais explícita e performática.


por Eric Hideki em 15 de January de 2016 às 00:31

January 10, 2016

Bruno Cezar Rocha

How to customize Quokka CMS home page template

How to customize quokka CMS templates

So you want to create a website using Quokka CMS but this website will not use the default 'blogging like' template that is the default CMS theme.

You can use Quokka to create any kind of website that handle dynamix content, the only thing you need is to create the desired channels and customize some jinja2 templates.

Take a look at this example of fundraising campaign website done with quokka: http://www.natalanimal.com.br this an e-commerce like app using quokka-fundraising and quokka-cart modules, this website customized the templates for 'home' channel as it looks like a web portal. (the source code for this template is available in: https://bitbucket.org/rochacbruno/natal-theme/)

Now lets create a sample website to our fake company that looks more like an institutional home-page than a blog.

Environment

If you do not have a quokka environment yet, lets start creating one, you will need a running instance of MongoDB as you can find more instructions here

And you will need git plus python 2.7 and virtualenvwrapper

git clone https://github.com/quokkaproject/quokka
cd quokka
mkvirtualenv quokka
pip install -r requirements.txt

NOTE: if your MongoDB is running externally you may need to change the MONGODB_HOST = xxx.xxx.x.x in a file called local_settings.py or you can export as an environment variable export QUOKKA_MONGODB_HOST=xxx.xxx.x.x

Populate sample data and run the local server

python manage.py populate
python manage.py runserver --reloader --debug

Now you should be able to see your running site in http://localhost:5000, as you can see your Quokka site looks like a blog because it uses the default theme, now we are going to customize this theme.

Hit CTRL+C to stop the running server and lets make a copy of current theme.

cp -r quokka/themes/pure quokka/themes/custom
export QUOKKA_DEFAULT_THEME="custom"

you can also put DEFAULT_THEME = 'custom' in your local_settings.py if you created one.

lets create a new channel

Now run the website again

python manage.py runserver --reloader --debug

Now access the website admin panel http://localhost:5000/admin using the default populated credentials 'admin@example.com' with passwd 'admin'

Go to the menu Content -> Channel


And create a new channel by clicking in create tab:

Put a name My Home Page and a description in html editor and then click in submit/save at the end of the form.

Your new channel is identified by the slug my-home-page this is the important detail to start customizing the theme.

Back to the channels page select your new channel:


And then click in actions selector with selected -> publish

Select it again and now use with selected -> set as homepage to set the new channel as the site homepage

lets create an about-us page

Access the admin and create a new channel called About Us (which will be identified by the slug about-us) add some text to description and check 'publish' and 'show in menu' and save it.


Now lets customize the home page template template

Home page:

Create a new template for my-home-page channel.

Quokka themes will try to find specific templates under a folder with the same name as the channel under content folder

mkdir quokka/themes/custom/templates/content/my-home-page

Now copy the default listing template to this folder and customize it

cp quokka/themes/custom/templates/content/default_list.html quokka/themes/custom/templates/content/my-home-page/

Edit the file quokka/themes/custom/templates/content/my-home-page/default_list.html with the following jinja2 code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="/favicon.ico">
    <title>{{channel.long_slug.split('/')|join(' | ')}} | {{Config.get('site', 'site_name', 'Quokka site')}}</title>
    <link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="http://getbootstrap.com/examples/justified-nav/justified-nav.css" rel="stylesheet">
    <script src="http://getbootstrap.com/assets/js/ie-emulation-modes-warning.js"></script>
  </head>
  <body>
    <div class="container">
      <div class="masthead">
        <h3 class="text-muted">{{Config.get('site', 'site_name', 'Quokka site')}}</h3>
        <nav>
          <ul class="nav nav-justified">
            <li class="active"><a href="/">Home</a></li>
            {% for channel in channels.filter(show_in_menu=True)%}
            <li><a href="{{channel.get_absolute_url()}}">{{channel.title}}</a></li>
            {% endfor %}
          </ul>
        </nav>
      </div>
      <div class="jumbotron">
        <h1>{{channel.title}}</h1>
        <p class="lead">{{channel.get_text()|safe}}</p>
        <p><a class="btn btn-lg btn-success" href="#" role="button">Welcome!</a></p>
      </div>
      <div class="row">
        {% for content in contents.items[0:3] %}
        <div class="col-lg-4">
          <h2>{{content.title}}</h2>
          <p class="text-danger"></p>
          <p>{{content.get_summary()|safe}}}} </p>
          <p><a class="btn btn-primary" href="{{content.get_absolute_url()}}" role="button">View details &raquo;</a></p>
        </div>
        {% endfor %}
      <footer class="footer">
        <p>&copy; Powered by Quokka CMS</p>
      </footer>
    </div> <!-- /container -->
    <script src="http://getbootstrap.com/assets/js/ie10-viewport-bug-workaround.js"></script>
  </body>
</html>

Now restart the server and go to the http://localhost:5000 to see yout new homepage template in action.


You can do the same as above for the about-us if needed, but note that empty channels will render its html description, so you don´t need to customize it if html description is enough


Now if you wish, you can customize the other templates located in content folder to follow the same pattern and you will have a completely modified Quokka CMS theme.

As you can see it is very easy to deal with templates in Quokka CMS because it uses Quokka-Themes extension and just pure Jinja2 templates.

por Bruno Rocha em 10 de January de 2016 às 16:03

January 09, 2016

PythonClub

Como encontrar soluções para seus problemas com Python

Como encontrar soluções para seus problemas com Python

Quando estamos aprendendo algo, o início geralmente é difícil. Conseguir absorver novos conceitos e entender como as coisas funcionam não é uma das tarefas mais simples, porém nessas horas precisamos lembrar do conceito de 'babysteps' (Um passo de cada vez), ter paciência e persistir.

A diferença entre uma pessoa experiente comparado a um iniciante é que a pessoa experiente errou muito mais vezes do que um iniciante, e com o tempo aprendeu com os erros.

E na área de programação temos inúmeros tutoriais e cursos espalhados pela internet, e geralmente as pessoas começam a aprender por conta própria. E quando acontece um problema (e isso irá acontecer inevitavelmente) e não sabemos como solucioná-lo, se torna um obstáculo chato.

Neste artigo irei mostrar algumas formas de poder encontrar soluções para seus problemas enquanto aprende a programar.

Os exemplos se aplicam a Python, mas podem ser exemplificados para qualquer outra linguagem ou tecnologia (se você trabalha com outras tecnologias como PHP ou Ruby, deixe nos comentários quais ferramentas utilizam para encontrar soluções dos seus problemas).

Dica: Veja essa palestra fantástica do Josh Kaufman sobre as primeiras 20 horas de aprendizado

Python console

Uma grande feature do Python é seu console interativo, por ele conseguimos testar nossos códigos e alguns scripts em tempo real. Vejamos um exemplo:

Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = {'Eric', 'Python', 'JavaScript'}
>>> b = ('Django', 'Flask')
>>> a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'set' and 'tuple'
>>> tuple(a) + b
('JavaScript', 'Eric', 'Python', 'Django', 'Flask')

O que aconteceu?

Na variável a criamos um set com os nomes Eric, Python e JavaScript, e depois criamos uma tupla com os nomes Django e Flask.

Ao tentarmos juntar a + b, o interpretador Python nos retorna um erro: TypeError: unsupported operand type(s) for +: 'set' and 'tuple'. Ou seja, o que ele diz é que não podemos somar um set a uma tupla.

O interpretador Python realiza as operações em tempo real, e se caso o que você deseja fazer não estiver correto, o interpretador irá informar o erro. Se o erro não for explícito para você, basta copiar e colar o erro no Google e encontrará os motivos do erro.

Para resolver esse problema, uma das soluções apresentada é transformar nosso set em uma tupla, onde fazemos a conversão em tempo de execução com o comando tuple(a) + b.

Ipython

Que tal termos um interpretador Python mais poderoso e com mais funcionalidades que o tradicional? O Ipython foi criado especificamente esse objetivo.

Vamos explorá-lo um pouco:

$ipython

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
Type "copyright", "credits" or "license" for more information.

IPython 1.2.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: nome = "Eric"

In [2]: nome.
nome.capitalize  nome.isalnum     nome.lstrip      nome.splitlines
nome.center      nome.isalpha     nome.partition   nome.startswith
nome.count       nome.isdigit     nome.replace     nome.strip
nome.decode      nome.islower     nome.rfind       nome.swapcase
nome.encode      nome.isspace     nome.rindex      nome.title
nome.endswith    nome.istitle     nome.rjust       nome.translate
nome.expandtabs  nome.isupper     nome.rpartition  nome.upper
nome.find        nome.join        nome.rsplit      nome.zfill
nome.format      nome.ljust       nome.rstrip      
nome.index       nome.lower       nome.split    

In [2]: nome.startswith("O")
Out[2]: False

In [3]: nome.startswith("E")
Out[3]: True

In [4]: len(nome)
Out[4]: 4

In [5]: nome.lower()
Out[5]: 'eric'

O que aconteceu?

Criamos uma variável nome onde é um simples String "Eric". E se digitarmos nome. e apertar TAB, o Ipython irá apresentar diversas operações que podemos fazer com essa variável.

Por exemplo, nome.startswith verifica se a primeira letra ou número começa com o parâmetro passado a ele.

Na primeira tentativa verificamos se a variável começa com a letra O, e ele me retornou False, ou seja, não é verdade.

Na segunda tentativa tentamos com a letra E, e ele me retornou True, o que é verdade já que o nome Eric começa com a letra "E".

O Ipython vai muito mais além do que isso, vá e dê uma olhada na página oficial e o que ele pode fazer.

E temos também o Ipython Notebook que é FANTÁSTICO, amplamente utilizado para computação científica.

Dreampie

Quando queremos testar algo mais elaborado, ter um console que permita criamos nossos códigos de forma mais organizada além do Interpretador padrão do Python e o IPython, podemos utilizar o Dreampie que é ideal para isso.

Vamos a outro exemplo:

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "copyright", "credits" or "license()" for more information.
DreamPie 1.1.1
>>> colunas = [
...     {'pnome': 'Eric', 'unome': 'Hideki', 'id': 4},
...     {'pnome': 'Luciano', 'unome': 'Ramalho', 'id': 2},
...     {'pnome': 'David', 'unome': 'Beazley', 'id': 8},
...     {'pnome': 'Tim', 'unome': 'Peters', 'id': 1},
... ]
>>> from operator import itemgetter
>>> colunas_por_nome = sorted(colunas, key=itemgetter('pnome'))
>>> colunas_por_id = sorted(colunas, key=itemgetter('id'))
>>> from pprint import pprint
>>> pprint(colunas_por_nome)
[{'id': 8, 'pnome': 'David', 'unome': 'Beazley'},
 {'id': 4, 'pnome': 'Eric', 'unome': 'Hideki'},
 {'id': 2, 'pnome': 'Luciano', 'unome': 'Ramalho'},
 {'id': 1, 'pnome': 'Tim', 'unome': 'Peters'}]
>>> pprint(colunas_por_id)
[{'id': 1, 'pnome': 'Tim', 'unome': 'Peters'},
 {'id': 2, 'pnome': 'Luciano', 'unome': 'Ramalho'},
 {'id': 4, 'pnome': 'Eric', 'unome': 'Hideki'},
 {'id': 8, 'pnome': 'David', 'unome': 'Beazley'}]

Preview da tela do Dreampie.

O que aconteceu?

Temos uma lista de dicionários desordenado e queremos ordenar de acordo com os parâmetros que queremos. Com isso, utilizamos duas bibliotecas que já estão por padrão com o Python: operator e pprint.

Dentro da biblioteca operator temos a funcionalidade itemgetter, onde através dos parâmetros que passamos, ele irá fazer a seleção. Já o pprint irá mostrar o resultado da nossa seleção de forma mais bonita. Vamos explicar detalhadamente o que cada coisa faz.

Criamos nossa lista de dicionários:

>>> colunas = [
...     {'pnome': 'Eric', 'unome': 'Hideki', 'id': 4},
...     {'pnome': 'Luciano', 'unome': 'Ramalho', 'id': 2},
...     {'pnome': 'David', 'unome': 'Beazley', 'id': 8},
...     {'pnome': 'Tim', 'unome': 'Peters', 'id': 1},
... ]

Importamos a biblioteca operator e sua funcionalidade itemgetter:

>>>from operator import itemgetter

E criamos nossa funcionalidade para ordenar a lista de dicionários de acordo com o parâmetro selecionado, que nesse caso será o primeiro nome, o pname:

>>> colunas_por_nome = sorted(colunas, key=itemgetter('pnome'))

Finalizando, utilizamos o pprint para exibir o resultado:

>>>pprint(colunas_por_nome)

Bacana, né? E também utilizamos a mesma lógica para ordenar de acordo com o id.

Se quiser saber mais a respeito do Ipython, indico o artigo do Python Help que fala a respeito.

Python Tutor

Ao observarmos uma funcionalidade queremos entender o que ele faz, e muitas vezes o que cada coisa no código faz não fica muito bem claro em nossas ideias. Por isso o Python Tutor existe! Ele exibe passo-a-passo o que está acontecendo no código.

Clique em Forward e veja o que acontece.

Outras opções

Também existem outras ferramentas que podem auxiliar e melhorar seu código:

Locais onde podemos postar nossas dúvidas

Vale sempre lembrar que é muito importante consultar a documentação oficial do Python, seja a versão 2 ou a versão 3.

Também existem outro lugar muito legal, o Stackoverflow. Se ainda o problema persistir, acesse as listas de discussões da comunidade Python no Brasil.

Deixe nos comentários seu feedback, e se tiver outra dica que não foi citado, não deixe de indicar.

por Eric Hideki em 09 de January de 2016 às 12:30

December 24, 2015

Grupy-DF

GitHub Pages com Pelican e Travis-CI

Olá pessoal!

Estou fazendo esta postagem para ajudar quem quer criar seu site no GitHub Pages usando Pelican para a criação das páginas e o Travis-CI para automatizar a tarefa de geração e publicação.

Este guia assume que o leitor possua conta no GitHub e no Travis-CI e tenha familiaridade com o ambiente python. A versão do pelican utilizada ao elaborar esta publicação foi a 3.6.

O GitHub Pages

O GitHub Pages é uma funcionalidade gratuita do GitHub para hospedar conteúdo estático (html, css, js e imagens) e publicar através de um sub-domínio de github.io ou até mesmo de um domínio customizado. É baseado em seu funcionamento que iremos guiar nossos próximos passos.

Resumidamente existem duas formas de se criar uma página pelo gh pages:

1 - Página de usuário/organização

Para este tipo de página crie um repositório com o nome usuario.github.io, onde usuario é o nome de usuário ou organização da conta em que o repositório será criado:

Repositório da página de usuário

O conteúdo a ser publicado deve ser colocado na branch master e os arquivos do pelican na branch pelican.

2 - Página de projeto

Para este tipo de página crie um repositório com o nome meuprojeto, onde meuprojeto é o nome desejado para o projeto que será publicado em usuario.github.io:

Repositório da página de projeto

O conteúdo a ser publicado deve ser colocado na branch gh-pages e os arquivos do pelican na branch pelican.

Para mais informações acesse o site oficial do GitHub Pages.

Pelican

O pelican é um gerador de site estático otimizado por padrão para criação de blogs. Utilizaremos aqui, para fins de demonstração, o modelo padrão do de blog seguindo o caminho de criação de página de usuário/organização, qualquer diferença do caminho de página de projeto será descrita quando necessário.

Para instalar o pelican basta rodar o comando:

$ pip install pelican==3.6

Para criar um projeto faça:

$ mkdir humrochagf.github.io
$ cd humrochagf.github.io
$ pelican-quickstart
Welcome to pelican-quickstart v3.6.3.

This script will help you create a new Pelican-based website.

Please answer the following questions so this script can generate the files
needed by Pelican.


> Where do you want to create your new web site? [.]
> What will be the title of this web site? Meu Blog
> Who will be the author of this web site? Humberto Rocha
> What will be the default language of this web site? [en] pt
> Do you want to specify a URL prefix? e.g., http://example.com   (Y/n) n
> Do you want to enable article pagination? (Y/n) y
> How many articles per page do you want? [10]
> What is your time zone? [Europe/Paris] America/Sao_Paulo
> Do you want to generate a Fabfile/Makefile to automate generation and publishing? (Y/n) y
> Do you want an auto-reload & simpleHTTP script to assist with theme and site development? (Y/n) y
> Do you want to upload your website using FTP? (y/N) n
> Do you want to upload your website using SSH? (y/N) n
> Do you want to upload your website using Dropbox? (y/N) n
> Do you want to upload your website using S3? (y/N) n
> Do you want to upload your website using Rackspace Cloud Files? (y/N) n
> Do you want to upload your website using GitHub Pages? (y/N) y
> Is this your personal page (username.github.io)? (y/N) y
Done. Your new project is available at /caminho/para/humrochagf.github.io

Inicialize um repositório neste diretório e suba os dados para a branch pelican:

$ git init
$ git remote add origin git@github.com:humrochagf/humrochagf.github.io.git
$ git checkout -b pelican
$ git add .
$ git commit -m 'iniciando branch pelican'
$ git push origin pelican

Para publicar o conteúdo na branch master é necessário o módulo ghp-import:

$ pip install ghp-import
$ echo 'pelican==3.6\nghp-import' > requirements.txt
$ git add requirements.txt
$ git commit -m 'adicionando requirements'
$ git push origin pelican

Publicando o blog:

$ make github
Primeira publicação do blog

Para publicar no caso da página de projeto altere o conteúdo da variável GITHUB_PAGES_BRANCH do makefile de master para gh-pages.

Agora que o nosso blog está rodando no gh pages vamos automatizar a tarefa de geração das páginas para poder alterar o conteúdo do blog e fazer novas postagens sem precisar estar um uma máquina com o ambiente do pelican configurado.

Travis-CI

O Travis-CI é uma plataforma de Integração Contínua que monta e testa projetos hospedados no github e será nossa ferramenta para automatizar a montagem das páginas do blog.

A Primeira coisa a ser feita é ir ao Travis-CI e habilitar seu repositório.

Habilitando repositório no travis

Em seguida vá nas configurações do repositório no travis e desabilite a opção Build pull requests para seu blog não ser atualizado quando alguém abrir um pull request e habilite o Build only if .travis.yml is present para que somente a branch que possuir o arquivo .travis.yml gerar atualização no blog.

Configurando remositório no travis

O próximo passo é criar uma Deploy Key para que o travis possa publicar conteúdo no github. Para isso gere uma chave ssh na raiz do repositório local:

$ ssh-keygen -f publish-key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in publish-key.
Your public key has been saved in publish-key.pub.

Criada a chave vamos cifrar usando a ferramenta Travis-CLI (certifique-se de que esteja instalada em sua máquina) para poder publicar em nosso repositório sem expor o conteúdo da chave privada:

$ travis encrypt-file publish-key
Detected repository as humrochagf/humrochagf.github.io, is this correct? |yes| yes
encrypting publish-key for humrochagf/humrochagf.github.io
storing result as publish-key.enc
storing secure env variables for decryption

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

    openssl aes-256-cbc -K $encrypted_591fe46d4973_key -iv $encrypted_591fe46d4973_iv -in publish-key.enc -out publish-key -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add publish-key.enc to the git repository.
Make sure not to add publish-key to the git repository.
Commit all changes to your .travis.yml.

Como dito no resultado do comando podemos adicionar a opção --add para já adicionar as informações no .travis.yml, porém, para evitar de sobrescrever algum comando que venha existir no seu arquivo é recomendado editar manualmente.

Em nosso caso iremos criar o arquivo:

$ touch .travis.yml

E adicionar o seguinte conteúdo:

sudo: false
branches:
  only:
  - pelican
language: python
before_install:
# troque a linha abaixo pelo resultado do comando:
# travis encrypt-file publish-key
# porém mantenha o final:
# -out ~/.ssh/publish-key -d
- openssl aes-256-cbc -K $encrypted_591fe46d4973_key -iv $encrypted_591fe46d4973_iv -in publish-key.enc -out ~/.ssh/publish-key -d
- chmod u=rw,og= ~/.ssh/publish-key
- echo "Host github.com" >> ~/.ssh/config
- echo "  IdentityFile ~/.ssh/publish-key" >> ~/.ssh/config
# substitua git@github.com:humrochagf/humrochagf.github.io.git
# pelo endereço de acesso ssh do seu repositório
- git remote set-url origin git@github.com:humrochagf/humrochagf.github.io.git
# Caso esteja montando a página de projeto troque master:master
# por gh-pages:gh-pages
- git fetch origin -f master:master
install:
- pip install --upgrade pip
- pip install -r requirements.txt
script:
- make github

Removemos em seguida a chave privada não cifrada para não correr o risco de publicar no repositório:

$ rm publish-key

ATENÇÃO: Em hipótese alguma adicione o arquivo publish-key em seu repositório, pois ele contém a chave privada não cifrada que tem poder de commit em seu repositório, e não deve ser publicada. Adicione somente o arquivo publish-key.enc. Se você adicionou por engano refaça os passos de geração da chave e cifração para gerar uma chave nova.

Agora adicionaremos os arquivos no repositório:

$ git add .travis.yml publish-key.enc
$ git commit -m 'adicionando arquivos do travis'
$ git push origin pelican

Para liberar o acesso do travis adicionaremos a deploy key no github com o conteúdo da chave pública publish-key.pub:

Adicionando a deploy key no github

Pronto, agora podemos publicar conteúdo em nosso blog sem a necessidade de ter o pelican instalado na máquina:

Fazendo a primeira postagem

Que o travis irá publicar para você:

Blog com a primeira postagem

Caso você tenha animado de criar seu blog pessoal e quer saber mais sobre pelican você pode acompanhar a série do Mind Bending sobre o assunto.

por Humberto Rocha em 24 de December de 2015 às 14:00

Magnun Leno

Hack ‘n’ Cast v0.17 - Introdução ao Bitcoin

Como confiar riquezas a algo intangível e da qual não temos certeza nem de quem é seu criador?

Baixe o episódio e leia o shownotes

por Magnun em 24 de December de 2015 às 05:40

December 22, 2015

Flavio Ribeiro

Working at the Video Team of The New York Times

So, after more than 3 years of the publication of this post and almost 9 months working here, I'll give my two cents and elaborate a bit about my experience on the day-to-day work at the video team of The New York Times.

the new york times

Environment

The technology of the video team is split into three teams: Video Players (VHS), Times Video and the brand new Video Publishing API team. We are growing really fast and things are changing in every direction; it's clear to me that everyone is excited and committed to our upcoming challenges.

We work in a fast-paced environment and always adapting our agile processes. However, I never feel rushed and everyone is committed to do deliveries with meaningful impact. The engineers have the freedom to give input on process, features and backlog. For example, after this quizz I was able to implement and send the thumbs on seekbar feature to production (http://nyti.ms/1jaFlVK).

We have frequent 1:1's and occasional brainstorm meetings. Our daily meeting is around 11 AM and takes no longer than 10 minutes, we usually have people working from home and the meeting always happens on Google Hangouts.

video team at nytimes

one of our daily meetings some time ago

Our team consists of people from all around the world such as U.S., China, Brazil, Canada among others. Our cultural differences are always the subject during lunch.

Speaking of lunches, while we don't have free food we do have a restaurant inside the building that serves typical food from a different country every day (somedays the food is awesome, sometimes not much).

Generally, a new hire rotates between all projects during the first month. This helps with understanding the whole video platform and familiarize with both team members and the codebase.

The Times also hosts a tech blog, 100% days (two days to do whatever you want) and hackweeks. There's also some events open to the public, you can find the schedule of events here.

Technology

We use JIRA to track our projects, milestones and sprints. I personally don't like it — it's too slow, hard to glue with the code and full of buttons. We use Github for code and Slack for communication throughout the entire company. Some of my favorite channels include #coffee for caffeine addicts, #conferences-events for events and #snacks for free goodies around the building.

The VHS team uses plain JavaScript for the players. We use Grunt for tasks and Karma + Chai + Mocha + Sinon for tests. One of our targets this year was to get rid of Flash so we needed to integrate the player with VPAID for ads and use hls.js for live streams. This whole journey deserves it's own post, we're planning to do that on our tech blog.

The Times Video team is responsible for Times Video product. We are using ES6 with Babel and moving from Backbone to React. The back-end is made with Node + Express + Memcached. For testing, we're using the same stack as VHS.

The Video Publishing API is still being assembled and will support our encoding pipeline, including the production & editing integration, security, encoding profiles, and syndication among others. The company is in an adoption movement of the Go language and we are willing to use it to build the API's.

Me

I was hired in April 2015 as a contractor and waited until October for the H1B visa, when I started working as a full-time employee. During this time I went to New York twice to get to know the team. I also used those trips to pick out apartments in different neighborhoods to get a better idea of where I would live.

It was my first experience working remotely and it was tough, I was living in Rio de Janeiro at the time and decided to spend some time with my parents and friends in Campina Grande, where I grew up. My life became a whirlwind during those months and my productivity was well below normal.

flavioribeiro github streak

my github streak proving I'm going back to a good shape

Since I arrived in New York, I feel much better. I have a routine, the commute works and the city surprises me everyday. As a natural Brazilian accustomed to a hot weather, I'm afraid of the upcoming winter. However, I'm lucky the cold is slow to arrive this year.

At the building, every engineer has it's own cube and it's huge. More than twice as size as my last table. It fits some books, figure toys, a small whiteboard and sometimes another coworker when pairing. You get a little isolated and is quite different from how are the open tables in startups, but I personally like the privacy that the cube give to me. The office is calm and quiet, I never turned on the noise cancellation of my headphone.

I'm very happy and hope to contribute even more on next year. If you want to be part of this amazing team, we are hiring.

video team

united colors of video team!

Related links:

por Flávio Ribeiro em 22 de December de 2015 às 15:27

December 16, 2015

Aprenda Python

The "call me back" test for open source software

This post isn't about Python. It's about choosing an open source software to be really used. I'm on the system development sea since 1988 and I've used a ton of tools in mainframe, desktop (MS-DOS, Windows and Linux), client/server and web environments. Many of them saved me. Others injured me a lot. I already saw great tools sink, even if they were being backed by wide enterprises. I also saw

por Vinicius Assef (noreply@blogger.com) em 16 de December de 2015 às 13:32

December 11, 2015

Aprenda Python

Rapidinha - comparações em Python

Mais um post da série Rapidinhas: Comparações em Python. Sobre comparações em Python, vale ressaltar alguns pontos: Elas retornam True ou False. Conteúdos vazios sempre retornam False. Você não precisa dos parênteses para comparações simples. A sintaxe para testar intervalos, nos operadores "menor que" e "maior que". Python tem tipagem forte. Ou seja, "1" == 1 sempre será falso, porque string

por Vinicius Assef (noreply@blogger.com) em 11 de December de 2015 às 11:07

December 10, 2015

Aprenda Python

Atalhos para and e or

A seguinte construção é bastante comum em Python: def dividir(a=None, b=None): if not a: a = 0 if not b: b = 1 return a / b Ela usa a avaliação booleana que eu explico no post Comparações em Python. Os testes retornam False se a variável não for informada porque, nesse caso ela assume o conteúdo default: None. Ou se for informado algum valor que seja avaliado como

por Vinicius Assef (noreply@blogger.com) em 10 de December de 2015 às 11:34

December 04, 2015

Aprenda Python

Rapidinha - Como contar em Python

Mais um post da série Rapidinhas: Como contar em Python. Como saber quantas vezes um texto aparece dentro da string (ou, como contar substrings): >>> "ela quase caiu pela janela".count("ela") >>> 3 Como contar quantas vezes um item aparece na lista: >>> [17, 43, 17, 105].count(17) >>> 2 >>> >>> ["carlos", "maria", "pedro", "fernanda"].count("maria") >>> 1 Como contar itens usando um

por Vinicius Assef (noreply@blogger.com) em 04 de December de 2015 às 16:49

PythonClub

Criando novos comandos no django-admin

Veja aqui como criar o seu próprio comando para ser usado com o django-admin ou manage.py do Django.

O django-admin ou manage.py já tem um bocado de comandos interessantes, os mais utilizados são:

  • startproject - cria novos projetos.
  • startapp - cria novas apps.
  • makemigrations - cria novas migrações baseadas nas mudanças detectadas nos modelos Django.
  • migrate - sincroniza o banco de dados com as novas migrações.
  • createsuperuser - cria novos usuários.
  • test - roda os testes da aplicação.
  • loaddata - carrega dados iniciais a partir de um json, por exemplo, python manage.py loaddata fixtures.json
  • shell - inicializa um interpretador Python interativo.
  • dbshell - acessa o banco de dados através da linha de comando, ou seja, você pode executar comandos sql do banco, por exemplo, diretamente no terminal.
  • inspectdb - retorna todos os modelos Django que geraram as tabelas do banco de dados.
  • runserver - roda o servidor local do projeto Django.

Mas de repente você precisa criar um comando personalizado conforme a sua necessidade. A palavra chave é BaseCommand ou Writing custom django-admin commands.

Começando do começo

Importante: estamos usando Django 1.8 e Python 3.

Criando o projeto

Eu usei este Makefile para criar o projeto.

wget --output-document=Makefile https://goo.gl/UMTpZ1
make setup

Ele vai criar um virtualenv e pedir pra você executar os seguintes comandos:

source venv/bin/activate
cd djangoproject
make install

Pronto! Agora nós já temos um projetinho Django funcionando. Note que o nome da app é core.

Criando as pastas

Para criarmos um novo comando precisamos das seguintes pastas:

core
├── management
│   ├── __init__.py
│   ├── commands
│   │   ├── __init__.py
│   │   ├── novocomando.py

No nosso caso, teremos 3 novos comandos, então digite, estando na pasta djangoproject

mkdir -p core/management/commands
touch core/management/__init__.py
touch core/management/commands/{__init__.py,hello.py,initdata.py,search.py}

Sintaxe do novo comando

Importante: estamos usando Django 1.8 e Python 3.

O Django 1.8 usa o argparse como parser de argumentos do command, mais informações em module-argparse.

from django.core.management.base import BaseCommand, CommandError
from optparse import make_option

class Command(BaseCommand):
    help = 'Texto de ajuda aqui.'
    option_list = BaseCommand.option_list + (
        make_option('--awards', '-a',
                    action="store_true",
                    help='Ajuda da opção aqui.'),
    )

    def handle(self, **options):
        self.stdout.write('Hello world.')
        if options['awards']:
            self.stdout.write('Awards')

Entendeu? Basicamente o handle é a função que executa o comando principal, no caso o self.stdout.write('Hello world.'), ou seja, se você digitar o comando a seguir ele imprime a mensagem na tela.

$ python manage.py hello
Hello World

--awards é um argumento opcional, você também pode digitar -a.

$ python manage.py hello -a
Hello World
Awards

action="store_true" significa que ele armazena um valor verdadeiro.

Obs: A partir do Django 1.8 os comandos de argumentos opcionais são baseados em **options.

Veja uma outra forma de escrever

from django.core.management.base import BaseCommand, CommandError

class Command(BaseCommand):

    def add_arguments(self, parser):
        # Argumento nomeado (opcional)
        parser.add_argument('--awards', '-a',
                            action='store_true',
                            help='Ajuda da opção aqui.')

    def handle(self, *args, **options):
        self.stdout.write('Hello world.')
        if options['awards']:
            self.stdout.write('Awards')

A diferença é que aqui usamos parser.add_argument ao invés de make_option.

hello.py

from django.core.management.base import BaseCommand, CommandError
# minimalista
class Command(BaseCommand):
    help = 'Print hello world'

    def handle(self, **options):
        self.stdout.write('Hello World')

Uso

$ python manage.py hello

initdata.py

Objetivo: Obter alguns filmes de uma api e salvar os dados no banco.

api: omdbapi.com

models.py

from django.db import models

class Movie(models.Model):
    title = models.CharField(u'título', max_length=100)
    year = models.PositiveIntegerField('ano', null=True, blank=True)
    released = models.CharField(u'lançamento', max_length=100, default='', blank=True)
    director = models.CharField('diretor', max_length=100, default='', blank=True)
    actors = models.CharField('atores', max_length=100, default='', blank=True)
    poster = models.URLField('poster', null=True, blank=True)
    imdbRating = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
    imdbID = models.CharField(max_length=50, default='', blank=True)

    class Meta:
        ordering = ['title']
        verbose_name = 'filme'
        verbose_name_plural = 'filmes'

    def __str__(self):
        return self.title

Não se esqueça de fazer

python manage.py makemigrations
python manage.py migrate

admin.py

Vamos visualizar pelo admin.

from django.contrib import admin
from core.models import Movie

admin.site.register(Movie)

Instale o requests

pip install requests

initdata.py

O código a seguir é longo, mas basicamente temos

  • print_red(name) função que imprime um texto em vermelho (opcional)
  • get_html(year) função que lê os dados da api usando requests, e depois escolhe um filme randomicamente a partir de 2 letras
  • get_movie(year) se o dicionário conter {'Response': 'True', ...} então retorna um dicionário do filme localizado
  • save() salva os dados no banco
  • handle(movies, year) este é o comando principal. Busca os filmes várias vezes, conforme definido pela variável movies, e salva os n filmes.
# -*- coding: utf-8 -*- #

import random
import string
import requests
from django.core.management.base import BaseCommand, CommandError
from django.core.exceptions import ValidationError
from optparse import make_option
from core.models import Movie


class Command(BaseCommand):
    help = """Faz o crawler numa api de filmes e retorna os dados.
    Uso: python manage.py initdata
    ou: python manage.py initdata -m 20
    ou: python manage.py initdata -m 20 -y 2015"""
    option_list = BaseCommand.option_list + (
        make_option('--movies', '-m',
                    dest='movies',
                    default=10,
                    help='Define a quantidade de filmes a ser inserido.'),
        make_option('--year', '-y',
                    dest='year',
                    action='store',
                    default=None,
                    help='Define o ano de lançamento do filme.'),
    )

    def print_red(self, name):
        """imprime em vermelho"""
        print("\033[91m {}\033[00m".format(name))

    def get_html(self, year):
        """
        Le os dados na api http://www.omdbapi.com/ de forma aleatoria
        e escolhe um filme buscando por 2 letras
        """

        # Escolhe duas letras aleatoriamente
        letters = ''.join(random.choice(string.ascii_lowercase) for _ in range(2))

        # Se não for definido o ano, então escolhe um randomicamente
        if year is None:
            year = str(random.randint(1950, 2015))
        url = 'http://www.omdbapi.com/?t={letters}*&y={year}&plot=short&r=json'.format(letters=letters, year=str(year))
        return requests.get(url).json()

    def get_movie(self, year, **kwargs):
        """ Retorna um dicionário do filme """

        movie = self.get_html(year)
        j = 1  # contador

        # Faz a validação de Response. Se a resposta for falsa, então busca outro filme.
        while movie['Response'] == 'False' and j < 100:
            movie = self.get_html(year)
            self.print_red('Tentanto %d vezes\n' % j)
            j += 1
        return movie

    def save(self, **kwargs):
        """SALVA os dados"""
        try:
            Movie.objects.create(**kwargs)
        except ValidationError as e:
            self.print_red(e.messages)
            self.print_red('O objeto não foi salvo.\n')

    def handle(self, movies, year, **options):
        """ se "movies" não for nulo, transforma em inteiro """

        self.verbosity = int(options.get('verbosity'))

        if movies is not None:
            movies = int(movies)

        # busca os filmes n vezes, a partir da variavel "movies"
        for i in range(movies):
            # verifica as validações
            m = self.get_movie(year)
            if m['imdbRating'] == "N/A":
                m['imdbRating'] = 0.0

            # Transforma "year" em inteiro
            if "–" in m['Year']:
                m['Year'] = year

            data = {
                "title": m['Title'],
                "year": m['Year'],
                "released": m['Released'],
                "director": m['Director'],
                "actors": m['Actors'],
                "poster": m['Poster'],
                "imdbRating": m['imdbRating'],
                "imdbID": m['imdbID'],
            }

            self.save(**data)

            if self.verbosity > 0:
                self.stdout.write('\n {0} {1} {2}'.format(i + 1, data['year'], data['title']))
        if self.verbosity > 0:
            self.stdout.write('\nForam salvos %d filmes' % movies)

Uso

Usage: python manage.py initdata [options] 

Faz o crawler numa api de filmes e retorna os dados.
     Uso: python manage.py initdata
     ou: python manage.py initdata -m 20
     ou: python manage.py initdata -m 20 -y 2015

search.py

Objetivo: Localizar o filme pelo título ou ano de lançamento.

from django.core.management.base import BaseCommand, CommandError
from optparse import make_option
from core.models import Movie


class Command(BaseCommand):
    help = """Localiza um filme pelo título ou ano de lançamento.
    Uso: python manage.py search -t 'Ted 2'
    ou: python manage.py search -y 2015
    ou: python manage.py search -t 'a' -y 2015"""

    option_list = BaseCommand.option_list + (
        make_option('--title', '-t',
                    dest='title',
                    default=None,
                    help='Localiza um filme pelo título.'),
        make_option('--year', '-y',
                    dest='year',
                    default=None,
                    help='Localiza um filme pelo ano de lançamento.'),
    )

    def handle(self, title=None, year=None, **options):
        """ dicionário de filtros """
        self.verbosity = int(options.get('verbosity'))

        filters = {
            'title__istartswith': title,
            'year': year
        }

        filter_by = {key: value for key, value in filters.items() if value is not None}
        queryset = Movie.objects.filter(**filter_by)

        if self.verbosity > 0:
            for movie in queryset:
                self.stdout.write("{0} {1}".format(movie.year, movie.title))
            self.stdout.write('\n{0} filmes localizados.'.format(queryset.count()))

Uso

Usage: python manage.py search [options] 

Localiza um filme pelo título ou ano de lançamento.
     Uso: python manage.py search -t 'Ted 2'
     ou: python manage.py search -y 2015
     ou: python manage.py search -t 'a' -y 2015

Aqui tem um exemplo legal que eu usei como ideia pra fazer este post.

Mais algumas referências:

Writing custom django-admin commands

Zachary Voase: Fixing Django Management Commands

Adding Custom Commands to manage.py and django-admin.py by dave

por Regis da Silva em 04 de December de 2015 às 00:00

November 30, 2015

Aprenda Python

Technical tenets

Some technical tenets, not in order of importance: Don't create technical debts. Forge ahead a "MUP" (Minimal Useful