Categorias
Tecnologia

Primeiros passos com o GIT

GIT é um software de controle de versão utilizado por muitos e muitos desenvolvedores e empresas no mercado de tecnologia.

Um controle de versão serve para manter o histórico do desenvolvimento do código e também para ajudar uma equipe de programadores trabalhando no mesmo código a não fazer besteira no meio do caminho.

Vamos à um exemplo bem simples.

João está fazendo alterações no arquivo index.html ao mesmo tempo em que Carlos. João altera as linhas 30 até 50 do arquivo e Carlos da 80 a 100. Em algum momento os dois precisam juntar esse código para ter a versão final do documento que vai para o site joaoecarlos.com.

Isso é bem fácil, basta que um dos dois pegue a parte do código que o outro alterou e cole na posição correta no arquivo index.html. Está pronto para subir para o servidor, certo? Certo.

Então para que eu preciso do Git para ajudar o João e o Carlos?

Bem, basta dizer que se o João estivesse alterando entre as linhas 80 e 100 do mesmo arquivo teríamos um conflito entre o que o João fez e o que o Carlos fez. Não bastaria colar as alterações do João sobre as do Carlos, seria preciso que se analisasse linha por linha do que cada um fez para ver qual deve entrar e qual deve sair. Basicamente os dois precisariam sentar juntos e reescrever o código. Trabalho dobrado.

Agora imagine que essa reescrita de código pudesse ser feita de forma automática? Não seria ótimo?! Sim. Seria.

Aí entra nosso querido software de controle de versão! No caso dessa publicação estamos falando de Git, mas ele não é o único.

Vamos rever o exemplo do site joaoecarlos.com agora utilizando o Git em todo o processo.

João modifica o arquivo em seu computador. Ele salva localmente suas alterações e faz o commit com o Git que salva no repositório local quais alterações foram feitas. No final do processo ele executa um push e essas alterações ficam salvas em no repositório remoto.

Carlos faz alterações no mesmo arquivo e executa seu commit localmente. Quando ele for executar o push para o repositório o Git vai avisá-lo que seu código local não está atualizado com o repositório. Então ele executa um pull e baixa as alterações que João fez e o Git se preocupa em reescrever seu código para escolher quais linhas devem ficar em quais posições. Depois disso, Carlos pode verificar se está tudo certo com seu novo código e mandá-lo para o repositório com mais um push.

Pronto. É assim que funciona o Git… da maneira mais básica o possível, é claro.

O Git é muito mais do que isso, mas primeiro é preciso entender o seu conceito básico para começar a trabalhar com ele.

Vamos ver o passo a passo do que o João e o Carlos fizeram.

Aviso: todos os exemplos vistos nesta publicação são comandos executados em um console/terminal de linha de comando de um sistema operacional com base em Unix (MacOS ou Linux, por exemplo). Eles consideram que o Git já está instalado no seu ambiente de trabalho. Para ver como instalar clique aqui.

Primeiro eles precisaram iniciar o repositório Git localmente no diretório em que seus códigos estavam. Acessando via linha de comando eles entraram na raiz do código.

cd /Project/joaoecarlos/

Foi lá que ele iniciaram seu novo repositório com o comando:

git init

Para verificar os arquivos que o Git vai rastrear eles usaram o comando git status e viram algo como o exemplo abaixo.

On branch master
Initial commit
Untracked files:
  (use "git add <file>..." to include in what will be committed)
index.html
nothing added to commit but untracked files present (use "git add" to track)

Eles notaram que está escrito Untracked files logo acima do nome do arquivo. Isso quer dizer que o Git não está “rastreando” esse arquivo.

Com o comando git add . eles adicionaram todos os arquivos do seu projeto na lista de arquivos a serem rastreados. O . representa todos os arquivos e diretórios presentes nesse diretório. Eles viram algo assim:

On branch master
Initial commit
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
new file:   index.html

Agora eles tem um ambiente preparado para fazer o seu primeiro commit. Eles adicionaram o arquivo index.html à lista de arquivos que serão gravados nesse commit.

Com a execução do comando git commit -m "Primeiro commit" eles finalmente gravaram sua primeira “versão” do site joaoecarlos.com no seu repositório local.

[master (root-commit) d8cbc79] Primeiro commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 index.html

O atributo -m "Primeiro commit" é um encurtamento de uma parte do processo de commit que é muito importante: descrever o que foi alterado.

Todo commit precisa ter uma boa descrição do que foi feito. Isso facilita muito para o futuro, onde centenas ou milhares de commits foram executados em um repositório. Imagina se você precisa encontrar onde foi feita alguma alteração no passado?

Pode-se executar esse comando sem o atributo -m, mas em seguida o console irá abrir um editor de texto para que se escreva uma mensagem descrevendo o que foi alterado.

Bom, agora que nossos colegas do exemplo já tem um repositório local, precisam enviar isso para um repositório centralizado. Um exemplo de serviço muito bom (e bem conhecido) para criar um repositório é o GitHub.

Depois de criarem sua conta e seu repositório no Github, nossos desenvolvedores adicionaram esse repositório como remote nas suas configurações do Git.

git remote add origin https://github.com/user/repo.git

Esse origin que você vê no comando é um apelido que se dá para o repositório remoto em sua máquina. Por padrão estamos chamando de origin que é o mais comum que você vai encontrar por aí.

Adicionar o remote no seu repositório local serve para indicar de onde ele deve puxar o código atualizado e para onde ele deve enviar seus commits. É o repositório com o qual todos os desenvolvedores do projeto vão interagir e onde ficará registrado todas as informações sobre alterações e versões do código do seu projeto.

Agora nossos desenvolvedores de exemplo estão prontos para subir seus primeiros commits para o repositório no GitHub com o comando git push origin master.

git push origin master

Vamos analisar esse comando…

git push é a parte que diz que queremos enviar o que temos de commitsna nossa máquina para o repositório remoto, GitHub por exemplo.

origin master diz para onde enviar esses commits, como o apelido do repositório é origin isso significa que vamos enviar para o GitHub que adicionamos a pouco.

master significa o branch que estamos usando. Mais para frente vou falar sobre branch, mas por enquanto vamos assumir que estamos trabalhando apenas com o branch padrão, que é chamado de master. Ele já foi criado quando o João ou o Carlos executou o git init lá no começo.

voilá. O código dos nossos amigos está no repositório do GitHub.

Agora vamos dizer que o Carlos precise trazer as atualizações de lá. Ele executou os primeiros passos (git initgit remotegit add e git commit) na sua máquina local e agora quer atualizar o código com o que tem no repositório remoto.

git pull origin master

Assim como o git push nós estamos dizendo para o Git de onde queremos buscar as atualizações com o origin master.

Agora o Git já atualizou o código na máquina do Carlos.

Em outro momento vou falar sobre os conflitos, algo que pode acontecer nesse momento de sincronização (ou merge) quando se usa controle de versão.

Por enquanto é isso. Esses são os primeiríssimos passos para utilizar o Git como controle de versão em um projeto.

Esse texto foi produzido para quem nunca teve contato algum com controle de versão, para dar uma ideia geral de como é e para que serve.

Se você quer saber mais sobre Git, deixe um comentário que eu planejo mais publicações como essa, dando continuidade ao processo e explicando mais sobre outros fatores.

Postado originalmente no Medium.

Categorias
Tecnologia

Criando um bot super simples para o Slack

Em uma das reuniões dessa última semana na empresa onde trabalho surgiu uma necessidade simples de comunicação: Precisamos que toda a equipe saiba quando um novo release de algum dos nossos repositórios for criado no Github.

Como usamos o Slack como ferramenta de comunicação interna, na hora já pensei:

“Esta é a minha chance de criar um bot para o Slack!”

Sempre tive vontade de fazer algo para o Slack, mas não tinha conseguido achar um tempo e nem um motivo específico para isso. Eu já imaginava que para esse objetivo, avisar sobre um novo release, não seria algo complicado.

Realmente não foi. Em apenas algumas linhas deu pra criar um bot que conecta os webhooks do Github e do Slack utilizando o Flask como meio-campo.

Abaixo o código final da primeira versão.

# coding: utf-8
import os
import requests
from flask import Flask
from flask import request

app = Flask(__name__)

slack_webhook_url = os.getenv('SLACK_WEBHOOK_URL')

@app.route("/", methods=['POST', ])
def webhook():
 action = request.json['action']
 release = request.json['release']
 repository = request.json['repository']

slack_data = {
 "text": "A new release from *{repo_name}* was {action}!\n"
         "Click <{release_url}|Release {tag_name}> for more details".format(action=action,
           repo_name=repository['name'],
           release_url=release['url'],
           tag_name=release['tag_name'])
}

response = requests.post(slack_webhook_url, json=slack_data)

if response.status_code != 200:
 raise ValueError(
   'Request to slack returned an error {}, the response is:\n{}'.format(response.status_code, 
      response.text)
 )

return ""

Pelo pouco tempo que passei lendo sobre como fazer um bot para o Slack eu entendi que você cadastra um Incoming Webhook e eles geram uma URL a qual você irá executar um request do tipo POST enviando a mensagem que você quer que um canal específico receba.

É realmente muito simples, porque a própria URL já carrega os tokens que são gerados para você.

Existem diversas outras funcionalidades que podem ser criadas em um bot para o Slack e a documentação deles cobre tudo. Mas vou falar apenas desse pequeno release-alert que criei.

Explicando linha por linha

Antes de começar a escrever o código vou precisar do Flask e de alguns outros pacotes. Então começo ainda na linha de comando do shell.

$ pip install Flask
$ pip install requests

Depois eu coloco a URL que gerei no Incoming Webhooks do Slack em uma variável de ambiente que chamo de SLACK_WEBHOOK_URL. Depois basta importá-la para dentro do código.

$ export SLACK_WEBHOOK_URL=<aqui vai a url do webhook do slack>

Agora crio meu arquivo Python chamado bot.py. Como estou usando o Flask, só vou precisar desse arquivo e praticamente nada mais.

Nas primeiras linhas começo importando o que vou utilizar e criando o app do Flask.

import os
import requests
from flask import Flask
from flask import request

app = Flask(__name__)

Trago para dentro do código a variável de ambiente que criei antes.

slack_webhook_url = os.getenv('SLACK_WEBHOOK_URL')

Agora vou começar a montar o meu endpoint. Ele receberá um evento POST enviado pelo webhook do Github. Para que isso aconteça você precisa cadastrar uma URL de webhook no settings do seu projeto no Github.

No meu caso, eu cadastrei a webhook no settings da organização, já que queria que todos os projetos da empresa disparassem o aviso. Mas funciona do mesmo jeito em qualquer um dos casos.

Quando cadastrei o meu webhook no Github especifiquei que gostaria de receber apenas os eventos relacionados ao release dos projetos.

Para testar o código localmente eu utilizei o ngrok. Explico como usar o ngrok em outra publicação.

@app.route("/", methods=['POST', ])
def webhook():

As primeiras linhas do meu endpoint capturam as informações que eu preciso do corpo enviado pelo POST do webhook do Github.

 action = request.json['action']
 release = request.json['release']
 repository = request.json['repository']

O Flask facilita recuperar informações de um corpo de um request JSON com o seu objeto request nativo. Basta chamar request.json e ele retorna um dicionário.

Agora eu vou montar o corpo da mensagem que enviarei para o Slack. O formato pedido é muito simples: um JSON com um campo ‘text’.

slack_data = {
 "text": "A new release from *{repo_name}* was {action}!\n"
         "Click <{release_url}|Release {tag_name}> for more details".format(action=action,
            repo_name=repository['name'],
            release_url=release['url'],
            tag_name=release['tag_name'])
}

Esse dicionário será transformado em um JSON pela próxima linha. O pacote requests já resolve isso para mim através do argumento json.

response = requests.post(slack_webhook_url, json=slack_data)

if response.status_code != 200:
 raise ValueError(
   'Request to slack returned an error {}, the response is:\n{}'.format(response.status_code, 
       response.text)
 )

return ""

No final do arquivo estou apenas tratando algum possível erro. Caso o requests retorne um status_code diferente de 200 eu levanto uma exceção.

No final retorno uma string vazia, porque cada endpoint do Flask precisa retornar alguma coisa para não levantar erros.

Para que meu bot fique disponível o tempo todo, fiz um deploy na minha conta gratuita do Heroku. E voilá!

Categorias
Tecnologia

Usando BeautifulSoup para pegar jogos, resultados e previsões do Brasileirão 2016

O pessoal no escritório está se divertindo muito com o Cartola FC. Graças a Liga que criamos por lá, todos estamos envolvidos em escalar nossos times e para isso eu gosto de ver as probabilidades de resultado dos jogos. Descobri a pouco que o Bing faz uma previsão quando você busca por dois times que irão se enfrentar. Então resolvi automatizar essa busca para me divertir e experimentar o BeautifulSoup4  (BS) para fazer crawler.

A ideia de um crawler é pegar um HTML de um site e procurar informações dentro dele. O BeautfulSoup4 é quem faz o parse deste HTML e nos permite executar métodos que fazem essa busca por informações no mesmo.

Comecei fazendo um app de um arquivo só com o Flask apenas para executar os crawlers facilmente e exibir as informações e dentro dele criei dois métodos que executam crawlers. O primeiro atualiza a tabela de jogos com os resultados (e na primeira execução popula o banco com os jogos) e o segundo executa a busca no Bing e me retorna a previsão de resultado de cada jogo.

Então o primeiro passo é buscar esse HTML no site onde você tem a informação. No caso das informações dos jogos eu fui buscar no site tabeladobrasileirao.net.

Começo utilizando o requests do Python para retornar o HTML dos jogos e seus resultados (no caso dos que já aconteceram).

import requests
r = requests.get('http://www.tabeladobrasileirao.net/')

Depois é a vez do BeautifulSoup4 entrar em ação. Passo o resultado para o BS (com o encode) e ele “parseia” o HTML.

from bs4 import BeautifulSoup
soup = BeautifulSoup(r.text.encode('utf-8'), 'html.parser')

E aí começo a procurar. Depois de analisar o HTML do site, descubro onde as informações estão e uso o método find do BS para pegar as informações.

table = soup.find('table', id="jogos")
for row in table.findAll("tr")[1:]:
    cells = row.findAll("td")
    if len(cells) == 12:
        game = {}
        game['round'] = int(cells[0].find(text=True))

        date_string = cells[1].find(text=True)
        date_string = '{}/{}'.format(date_string, '2016')
        game['date'] = datetime.datetime.strptime(date_string, "%d/%m/%Y").date()

        game['home_team'] = cells[4].find(text=True)

        home_team_result = cells[5].find(text=True)
        if home_team_result:
            game['home_team_result'] = int(cells[5].find(text=True))
        else:
            game['home_team_result'] = None

        away_team_result = cells[7].find(text=True)
        if away_team_result:
            game['away_team_result'] = int(cells[7].find(text=True))
        else:
            game['away_team_result'] = None
            
        game['away_team'] = cells[8].find(text=True)

Neste código estou buscando a tabela com id igual a “jogos” e iterando sobre suas células, a partir das linhas da tabela. Como já identifiquei onde estão os dados, comecei a guardá-los em um dicionário (tratando datas, tipos e resultados nulos) que usarei para salvar no banco de dados posteriormente. Por exemplo, cada jogo está em uma linha e a data dele está na segunda coluna, então uso cells[1].find(text=true) para pegar somente o texto da célula.

Omiti a parte de salvar no banco de dados, mas o código todo está disponível no meu Github.

Com todos os jogos salvos é a vez de procurar pela previsão de resultados no Bing.

Mais uma vez começo com o requests e dou um parse no resultado com o BS. Leve em consideração que a variável “game” no código abaixo retorna a string com o jogo em questão, por exemplo, “Internacional x Grêmio” ou “Corinthians x Flamengo”.

r = requests.get(u'http://www.bing.com/search?q={}'.format(game))

soup = BeautifulSoup(r.text.encode('utf-8'), 'html.parser')
div = soup.find('div',{'id':'tab_4'}).findNext('span',{'class':'b_demoteText'}).find_next_sibling(text=True)

predicts_string = div

Nesse caso foi muito mais difícil achar o local exato no código fonte do resultado da busca do Bing do que foi no caso do tabeladobrasileirao.net. Mas com um pouco de pesquisa e muita tentativa e erro eu acabei chegando no texto de previsão do Bing usando soup.find(‘div’,{‘id’:’tab_4′}).findNext(‘span’,{‘class’:’b_demoteText’}).find_next_sibling(text=True), mais uma vez com text=true para retornar somente o texto do objeto que vem no formato “Chapecoense 14% – Empate 29% – Corinthians 57%”, por exemplo.

Com os templates do Flask eu criei um pequeno site que executa e exibe o resultado desses crawlers. Ficou assim:

Essa foi minha primeira experiência com BeautifulSoup4 e crawlers. Qualquer dúvida ou sugestão, deixe um comentário abaixo. E para ver o código fonte desse pequeno app acesse meu Github.

Categorias
Tecnologia

Achando coordenadas de um endereço em Python

Precisei criar um script que encontra as coordenadas de acordo com um dado endereço. Eles vieram de um arquivo Excel com diversos locais registrados.

Para atingir esse objetivo me baseei em um projeto antigo do meu colega Fernando Freitas Alves. Ele já vinha pronto para ler planilhas Excel e buscar coordenadas para cada endereço, o que facilitou muito meu trabalho.

Precisei alterar diversos comportamentos do script e também atualizei a maneira como ele fazia requests, dado que na época em que ele criou o script não se utilizava o requests do python, ele utilizava urllib e simplejson para parsear os resultados dos requests.

Mandei um pull request apenas com a parte de atualização de urllib + simplejson para o requests no projeto dele. 😉

Abaixo segue um Gist com o código parecido com o que tenho utilizado, comentado em cada parte, para melhor compreensão. Utilizando Python 2.7:

[gist id=”d815e7cc6119bbe11f045b4ea26a90e3″ file=”coords.py”]

Meu script trabalha para tentar encontrar as coordenadas de milhares endereços. Como a API do Google Maps tem um limite diário, estou utilizando mais de uma API Key.

Com esses dados eu faço um request no minha API e adiciono no banco de dados. Mas você pode fazer o que quiser com eles agora. =)