Pra agitar um pouco mais as coisas na empresa, e trazer um pouco de inovação no modo de pensar e trabalhar, preparei um coding dojo.
Os desafios, peguei de dojopuzzles.com, que é um .com mas o conteúdo é totalmente em português, mas nada que um google translator não resolvesse pra usar em inglês.
Faltava um contador e um semáforo pra ver o estado do código. Procurei pelos relógios/cronômetros de pomodoro, mas não achei um que realmente me agradasse. Existem várias soluções, mas muitas são pequenas demais pra apresentar numa tela projetada.
O semáforo, encontrei depois a solução do Danilo Bellini, o dose, que é escrito em python com wxwindows. É uma boa solução, mas ainda faltava o cronômetro.
Foi então que resolvi botar a mão na massa e criar meu próprio sistema. Usei PyQT pra desenhar a janela principal. Claro que não fiz tudo na mão: eu usei o qt4-designer pra agilizar tudo e deixar quase pronto, deixando o python pra somente pegar os valores e interagir o mínimo possível.
O sistema ainda precisa de umas melhorias, com certeza. Ele é burro ao ponto de ficar em loop rodando com python a cada 5s todo arquivo que estiver lá. Então eu preciso melhorar pra poder usar outras linguagens além de python. Nisso eu vi que a solução do Danilo é mais inteligente, pois usa um "watchdog()" pra verificar se houve mudança no arquivo antes de rodar. Então já inclui no "roadmap" tentar implementar isso. Também achei que faz falta um som ou alarme pra avisar do tempo. Vou ver se consigo incluir um do tipo do NBA, que vai tocando quando o tempo está acabando (a partir de 10s). Isso vai dar mais "visibilidade" durante os dojos.
Quem quiser participar, ou só dar uma olhada, o código está no github: codingdojocontrol
Desde que o twitter liberou o histórico dos tweets antigos em formato CSV, procurei uma ferramenta para poder um tagcloud das minha palavras. Como o arquivo era muito grande (mais de 3 MB), as ferramentas online simplesmente travavam.
Procurando por alternativas, encontrei o pouco citado pytagcloud.
Então fiz o seguinte programa pra gerar o tagcloud dos meus tweets:
#! /usr/bin/pyhon
# -*- coding: utf-8 -*-
import re, csv
from pytagcloud import create_tag_image, make_tags
from pytagcloud.lang.counter import get_tag_counts
TWEETS = "tweets.csv"
buffer = ""
tws = csv.reader(open(TWEETS), delimiter=',', quotechar='"')
for rows in tws:
buffer += rows[5] + "\n"
output = "clound_large.png"
tags = make_tags(get_tag_counts(buffer), maxsize=120)
create_tag_image(tags,
output,
size=(800, 600),
fontname='Lobster')
Foi exatamente a cópia do programa documentado pelo próprio pytagcloud. Pra minha supresa, não funciona e sai o seguinte erro:
Traceback (most recent call last): File "generate_cloud.py", line 53, in File "/usr/local/lib/python2.7/dist-packages/pytagcloud/__init__.py", line 344, in create_tag_image File "/usr/local/lib/python2.7/dist-packages/pytagcloud/__init__.py", line 275, in _draw_cloud File "/usr/local/lib/python2.7/dist-packages/pytagcloud/__init__.py", line 62, in __init__ IOError: unable to read font filename Error in sys.excepthook: Traceback (most recent call last): File "/usr/lib/python2.7/dist-packages/apport_python_hook.py", line 66, in apport_excepthook ImportError: No module named fileutils Original exception was: Traceback (most recent call last): File "generate_cloud.py", line 53, in File "/usr/local/lib/python2.7/dist-packages/pytagcloud/__init__.py", line 344, in create_tag_image File "/usr/local/lib/python2.7/dist-packages/pytagcloud/__init__.py", line 275, in _draw_cloud File "/usr/local/lib/python2.7/dist-packages/pytagcloud/__init__.py", line 62, in __init__ IOError: unable to read font filename
Após muito mexer, pois o código exemplo, com texto menor, funciona perfeitamente, descobri que esse erro é gerado pela quantidade de palavras no arquivo de tweets. Consegui corrigir isso colocando um limite no tamanho dos tags, que é um array.
E foi-se a Python Brasil 9, em Brasília. Evento muito legal e que reuniu a comunidade de desenvolvedores python tupiniquins.
Eu participei falando um pouco do meu trabalho em telecomunicações. Minha apresentação foi essa abaixo, "python in telecommunications", que contou um pouco da história do nono dígito. Claro que não dá pra entender muito, uma vez que tem mais imagens que informação, mas mostra bem como python ajudou a ter uma solução mais robusta e limpa em relação a anterior, que rodava em shell script.
Dia desses eu redescobri as imagens da minha webcam. Tirei vários screenshots usando o aplicativo cheese, desde que minha mais nova nasceu. E nem lembrava disso.
Consegui criar uma videozinho com elas, o que foi bem legal, mostrando o crescimento dela (e minha barba ficando cada vez mais branca).
A idéia inicial era gerar um gif animado, mas o mesmo ficou em 85 MB de tamanho. E sem som.
Então resolvi fazer 2 coisas:
A captura do screenshot, eu consegui fazer utilizando pygame. O módulo já inclui vários binding pra realizar ações como capturar da webcam e salvar a imagem. O script ficou assim:
#! /usr/bin/python -u
"""
Not only Obamas _is_ watching you...
Based in: http://stackoverflow.com/questions/15870619/python-webcam-http-streaming-and-image-capture
"""
SAVEDIR = "/home/helio/Pictures/Webcam"
import pygame, sys
import pygame.camera
import time, random
pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0", (640,480))
while True:
print "Taking a shot:",
cam.start()
image = cam.get_image()
cam.stop()
timestamp = time.strftime("%Y-%m-%d_%H%M%S", time.localtime())
filename = "%s/%s.jpg" % (SAVEDIR, timestamp)
print "saving into %s" % filename
pygame.image.save(image, filename)
time.sleep(random.randrange(10) * 60)
Chamei de obamawatch.py em homenagem à espionagem da NSA nas nossas vidas, e que o presidente Obama não fez esforço nenhum pra diminuir ou mesmo evitar. É um script super intrusivo, pois tira fotos de tempos em tempos, podendo pegar situações que... humm... não o faça se sentir muito orgulhoso. Então é bom rodar de vez em quando.
Pra juntar as imagens JPEG geradas em um GIF animado, usei o imagemagick com o mogrify. Com o mogrify, na verdade, eu diminui as imagens pra 320x240 pixels, pra diminuir o tamanho. Então usei o imagemagick pra gera o GIF.
mogrify -resize 320x240 *jpg
gm convert -delay 20 2013-09-07_1* animated-2013-09-07.gif
Com isso consegui o resultado abaixo. Bem divertido.
Às vezes eu escrevo programas. Entre esse programas, alguns são daemons.
Além da confusão com demônios, o que são daemons?
Daemons são os programas que rodam em background no sistema, não precisando de um terminal (console) anexado. E qualquer tipo de programa pode ser um daemon, pra qualquer finalidade.
Em geral daemons seguem as seguintes regras pra se tornarem daemons:
O segundo fork() é feito para garantir que o programa, através do segundo processo filho, seja "herdado" pelo processo init do sistema.
Em Python, sempre incluo uma função como essa:
def Daemonize(self):
"""
Fork to became a daemon.
"""
if not self.isDaemon:
try:
self.run()
except KeyboardInterrupt:
sys.exit(0)
return
os.chdir("/")
pid = os.fork()
if (pid > 0):
sys.exit(os.EX_OK)
else:
pid = os.fork()
if (pid > 0):
sys.exit(os.EX_OK)
else:
self.run()
Esse é parte de um método, mas poderia ser uma função. A idéia é usar o getopt() para verificar as opções passadas e entrar no modo de daemon ou não, dependendo da opção passada, que modifica a variável booleana self.isDaemon.
Mas um dos meus programs começou a apresentar o seguinte erro:
helio@goosfraba:~$ connect_TSP.py ccn
IP already setup... skipping root access
Running as daemon
daemonized...
close failed in file object destructor:
IOError: [Errno 10] No child processes
Error in sys.excepthook:
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/apport_python_hook.py", line 66, in apport_excepthook
from apport.fileutils import likely_packaged, get_recent_crashes
RuntimeError: sys.meta_path must be a list of import hooks
Original exception was:
IOError: [Errno 10] No child processes
Inicialmente achei que era problema no "apport" com meu programa, que usa python-expect. Mesmo com tal erro, o programa funcionava perfeitamente em background, como daemon. Várias fontes na Internet, principalmente no Launchpad, o sistema de bug report do Ubuntu, várias pessoas reclamavam de tal erro como sendo problema do apport.
Após muito buscar a origem do problema, não no Ubuntu, mas no python, descobri que alguns file descriptors estavam causando esse erro, por continuarem abertos quando ocorria o fork(). Corrigi da seguinte forma:
def Daemonize(self):
"""
Fork to became a daemon.
"""
if not self.isDaemon:
try:
self.run()
except KeyboardInterrupt:
sys.exit(0)
return
os.chdir("/")
pid = os.fork()
if (pid > 0):
os.close(sys.stdin.fileno())
os.close(sys.stout.fileno())
os.close(sys.stderr.fileno())
sys.exit(os.EX_OK)
else:
pid = os.fork()
if (pid > 0):
os.close(sys.stdin.fileno())
os.close(sys.stdout.fileno())
os.close(sys.stderr.fileno())
sys.exit(os.EX_OK)
else:
self.run()
então bastou fechar os descritores de arquivo do STDIN, STDOU e STDERR pra ter certeza que o daemon não sairia com o erro acima.
Happy hacking :-)