Categorias
Tecnologia

Como usar múltiplos databases no Django

Utilizando o Database Router de uma forma genérica

Existem diversos motivos para utilizar mais de um banco de dados no seu projeto de software. Um deles pode ser as réplicas, por exemplo. Neste caso o aplicativo escreve as informações em um banco de dados, mas lê de outro.

Um diagrama mostrando o banco primário e suas réplicas de leitura

A vantagem de usar uma réplica é que a escrita em banco é mais custosa do que a leitura. Então, se uma consulta for feita durante um processo de inserção de dados, dependendo da quantidade de dados que está sendo escrito no database, ela pode acabar ficando muito mais lenta, causando um problema de timeout ou até tirando seu sistema do ar.

Esse é apenas um dos exemplos que é abordado na própria documentação oficial do Django. Como mostra este link.

Obviamente, esse não é o único motivo para se ter mais de um database no sistema, podem existir inúmeros, incluindo o que me fez pesquisar sobre isso. Vou falar mais sobre o meu caso específico no final desta publicação.

Definindo os databases

O primeiro passo para utilizar mais de um database no Django é definir seus bancos de dados nas configurações do arquivo settings.py.

DATABASES = {
    'default': {},
    'primary': {
        'NAME': 'primary',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'spam',
    },
    'replica1': {
        'NAME': 'replica1',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'eggs',
    },
    'replica2': {
        'NAME': 'replica2',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'bacon',
    },
}

Com esse código, retirado da documentação do Django, estamos definindo um banco de dados principal MySQL e mais duas réplicas.

A partir de agora você pode alternar o banco manualmente chamando o using() do QuerySet. Ele recebe apenas um argumento, o nome do banco de dados.

>>> # Isso irá rodar a consulta no banco 'default'.
>>> Author.objects.all()
>>> # Isso também.
>>> Author.objects.using('default').all()
>>> # Já esse irá rodar a consulta no banco 'other'.
>>> Author.objects.using('other').all()

Acredito que esse approach não deve agradar muito o desenvolvedor. Ter que definir manualmente qual o banco a ser utilizado em cada query, não é lá muito prático, certo?

Pra evitar isso temos o Database Router, o roteador de banco de dados nativo do Django.

Criando um Database Router

O roteador de banco de dados simplesmente define qual banco é usado para cada tipo de operação.

No exemplo abaixo, também retirado da documentação do Django, mostro como rotear entre os bancos. Nesse caso, vamos ler das réplicas e escrever no primário.

import random
class PrimaryReplicaRouter(object):
    def db_for_read(self, model, **hints):
        """
        Consultas vão aleatoriamente para uma das réplicas.
        """
        return random.choice(['replica1', 'replica2'])
    def db_for_write(self, model, **hints):
        """
        Escritas sempre são feitas no banco primário.
        """
        return 'primary'
    def allow_relation(self, obj1, obj2, **hints):
        """
        Relações entre objetos são permitidas se ambos estiverem no 
        pool primary/replica.
        """
        db_list = ('primary', 'replica1', 'replica2')
        if obj1._state.db in db_list and obj2._state.db in db_list:
            return True
        return None
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        All non-auth models end up in this pool.
        """
        return True

Note que o roteador de banco de dados disponibiliza quatro métodos. São eles:

  • db_for_write()
  • db_for_read()
  • allow_relation()
  • allow_migrate()

Na leitura escolhemos aleatoriamente uma das duas réplicas e na escrita escolhemos o banco primário. Perceba que simplesmente retornamos o nome do banco, que definimos nas configurações de database no settings.py.

Para que o router seja aplicado, precisamos adicionar a constante DATABASE_ROUTERS no arquivo settings.py, como mostro abaixo.

DATABASE_ROUTERS = ['caminho.para.o.PrimaryReplicaRouter']

Este é um exemplo simplificado do que pode ser feito com os database routers. Apenas resumi a documentação do Django sobre o assunto até agora.

Partimos para algo diferente, então.

Uma solução mais genérica

Procurando sobre o assunto na internet, cheguei até esta postagem de 2011 que mostra uma solução mais genérica para o database router e ainda com possibilidade de utilizar para mais de um app do Django.

from django.conf import settings
class DatabaseAppsRouter(object):
    """
    A router to control all database operations on models for different databases.
    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router will fallback to the `default` database.
    Settings example:
    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
    """
    def db_for_read(self, model, **hints):
        """"Point all read operations to the specific database."""
        if settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
            return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
        return None
    def db_for_write(self, model, **hints):
        """Point all write operations to the specific database."""
        if settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
            return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
        return None
    def allow_relation(self, obj1, obj2, **hints):
        """Allow any relation between apps that use the same database."""
        db_obj1 = settings.DATABASE_APPS_MAPPING.get(obj1._meta.app_label)
        db_obj2 = settings.DATABASE_APPS_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
        return None
    def allow_syncdb(self, db, model):
        """Make sure that apps only appear in the related database."""
        if db in settings.DATABASE_APPS_MAPPING.values():
            return settings.DATABASE_APPS_MAPPING.get(model._meta.app_label) == db
        elif settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
            return False
        return None

Agora basta mapear os apps com os databases no seu settings.py:

# Fonte: http://stackoverflow.com/a/18548287
DATABASE_ROUTERS = ['caminho.para.o.DatabaseAppsRouter']
DATABASE_APPS_MAPPING = {'nome_do_app': 'nome_do_db',
                         'nome_do_outro_app': 'nome_do_outro_db'}

No Meta do meu model eu defino o app_label, como no exemplo abaixo.

class Author(models.Model):
    name = models.CharFiels(max_length=120)
    class Meta:
        app_label = 'nome_do_app'

No meu caso de uso dessa solução, eu precisava que apenas um model lesse de um banco da dados diferente dos outros. Então, somente nele, eu adicionei o app_label mapeado com o banco de dados diferente.

Todos os outros models, que não possuem essa configuração, usam o banco default automaticamente, apenas o model com o app_label definido passa a usar o outro database.

Essa solução simplificou muito minha vida, pois agora eu posso continuar fazendo as consultas normalmente, porque o database router vai rotear tudo de maneira automática. Não preciso mais me preocupar com qual banco ele está buscando a informação em cada model.

Concluindo

Bem, é isso que tenho para mostrar. Sei que a abordagem é simplificada, mas foi a primeira vez que trabalhei nesse problema e quis dividir o que aprendi por aqui.

Espero que tenha ficado tranquilo de entender sobre o roteamento de banco de dados no Django. Caso eu não tenho sido claro em algum detalhe, entre em contanto.

Publicado originalmente no Medium.

Categorias
Crônicas Tecnologia

Ser um Programador Ninja…

…ou Programador Rockstar ou Mago da Programação…


Este texto foi originalmente publicado no Medium. Leia a versão original clicando aqui e recomende se você gostar.


Alguns bons anos atrás, costumava ser cool ver uma vaga de emprego com a expressão “programador ninja” ou algo do gênero. Mostrava que a empresa era maneira, joinha e que tinha aquele ambiente de trabalho diferentão.

“Buscamos um programador ninja”

Hoje, a coisa é um pouco diferente. Quando vejo uma vaga que busca um programador ninja ou um mago da programação, eu já desconfio.

O problema não é a expressão em si, que hoje em dia é brega e não pinta mais a empresa como “diferentona”. O problema é que ela costuma vir com uma lista absurda de exigências…

O cidadão tem que comprovar anos de experiência em diversas linguagens de programação, tem que gostar de programar em seu horário livre (?!) e, às vezes, é um diferencial, se não obrigação, que o camarada tenha projetos paralelos.

Ou seja, o cara não tem que ser um programador… Ele tem que programar de tudo e o tempo todo. Se tiver vida pessoal, não serve para mago da programação.

O ninja-mago-rockstar da programação

Por algum motivo essas empresas acreditam que é preciso ser apaixonado pela programação para ser um bom profissional. E para demonstrar isso tem que ter aqueles projetos paralelos e, quem sabe, uns commits no seu Github nas madrugadas de sábado.

O que não percebem é que quem programa por prazer não necessariamente vai gostar de trabalhar no seu projeto de gestor administrativo ou de vendas. Hobbies que viram trabalho nem sempre são tão prazerosos. E isso não é tudo…


Nesta semana eu li um texto aqui no Medium chamado Programming Doesn’t Require Talent or Even Passion (Programar Não Exige Talento ou Mesmo Paixão). Eu recomendo a leitura para qualquer um que trabalhe na área.

Nele o autor fala que nunca antes uma habilidade foi tão mitificada como a programação.

“Você não precisa apenas ter talento, você também precisa ser apaixonado para se qualificar como um bom programador.”

É exatamente o que pedem nessas vagas de programador Rockstar dos dias de hoje.


Exemplo de uma vaga postada na lista Python BrasilNo texto citado, o autor ainda traz uma série de grandes nomes da área, como os criadores dos frameworks Rails e Django e da linguagem PHP, por exemplo, que demonstram claramente não serem grandes amantes da programação e muito menos gênios da magia do desenvolvimento de software.

“Na verdade eu odeio programar, mas eu amo resolver problemas.” Tradução da citação de Rasmus Lerdorf, criador do PHP.

Eu trabalho nessa área há cerca de treze anos e não tenho receio em dizer que os melhores profissionais que encontrei não eram grandes magos dos códigos. O que realmente importa nessa área é a entrega. É conseguir resolver os problemas apresentados de forma objetiva e simples e, quase sempre, em um curtíssimo espaço de tempo.

Um bom profissional da programação não precisa ser um gênio, um monstro, um ninja, um hacker… ele precisa saber resolver problemas e ter a capacidade e humildade de aprender o que precisar para atingir esse objetivo.

Além disso, esse mito atrapalha todo mundo que está envolvido ou quer se envolver na área.

“O mito do ‘programador gênio’ é extremamente perigoso. Por um lado, ele deixa o limiar de entrada muito alto, assustando um monte de aspirantes a programador. Por outro lado, também assombra os que já são programadores, porque isso significa que se você não é um mago na programação, você é ruim… …Programação é só um monte de habilidades que podem ser aprendidas e não exigem muito talento, e não é vergonha nenhuma ser um programador mediano.” Tradução livre de parte da citação de Jacob Kaplan-Moss, criador do Django.

Eu consegui aprender a programar, e nem sequer gostava disso…

“Depois de muito tempo percebi que eu tinha ficado melhor em programar e continuava sempre ocupando vagas em empresas nessa área, entretanto, sempre querendo sair para ‘trabalhar com o que gosto’.”

Esse é um trecho de um texto que escrevi em 2014 falando sobre a minha relação conturbada com a programação. Eu passei anos procurando trabalhar em outra área, porque realmente não gostava de programar.

O que mudou isso em mim foi conhecer o Python. Passei a curtir a simplicidade e objetividade de escrever códigos com essa linguagem. Hoje eu gosto do que faço. De vez em quando eu até programo por diversão no meu horário livre.

Eu não consigo me enxergar como um bom programador, mas sei que consigo atender as expectativas de quem me contrata. Resolvo os problemas e faço as minhas entregas. E isso me deixa orgulhoso do meu trabalho.

Não somos ninjas, gênios ou magos, somos profissionais da área de desenvolvimento de software. É isso que temos que ser das 9h as 18h, ou em qualquer que seja seu horário de trabalho, o que fazemos fora disso diz respeito somente a nós mesmos.

Quer ter projetos paralelos? Ama programar? Ótimo.

Mas se você gosta de nadar, andar de bicicleta, jogar futebol, passear com seu cachorro, ficar com sua família ou dormir por horas e horas no seu tempo livre, você continua sendo um programador.

Não é isso que vai definir sua qualidade como profissional.

Pode ser também…

Categorias
Tecnologia

Lendo RSS Feed somente com Javascript (jQuery)

Eu construi uma página estática em HTML, CSS e JS para ser uma espécie de portfólio e cartão pessoal para mim e queria colocar uma seção com os artigos deste blog nela. A melhor maneira de acessar esses artigos sem muito esforço é usando o RSS Feed do blog.

Mas a página é “estática”, então eu poderia usar no máximo o Javascript para ler o Feed do blog. Uma opção seria acessá-lo via Ajax e processar os dados em seguida, mas se o Feed for de outra origem (domínio diferente, que não era o meu caso, mas pode ser o seu) ia acabar esbarrando em um erro de segurança (CORS Error) e não funcionaria.

Então encontrei uma solução (no link de referência no final do artigo), usar uma API do Google que lê um Feed e retorna um JSON com as informações dele.

Segue abaixo uma versão do código que estou utilizando na página, totalmente comentado.

Os items que temos disponíveis para acesso, como mostra a imagem abaixo, são o autor, categorias e tags, o conteúdo inteiro em HTML, um pequeno snippet do conteúdo em texto puro, a URL do artigo, a data de publicação e o título. Eu senti falta do retorno das mídias, como fotos, por exemplo, mas se for necessário a opção seria pegá-las do próprio HTML que retorna em “content“.

É isso aí, qualquer dúvida ou sugestão deixe nos comentários abaixo. Para ver esse código em funcionamento acesse o site.

Referência: http://pt.stackoverflow.com/a/65758

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

Usando Grunt para compilar SCSS (Sass)

O Grunt é um gerenciador de tarefas JavaScript. O conheci pela primeira vez quando utilizei o Yeoman para desenvolver um projeto em AngularJS (leia mais).

O Sass é uma linguagem de extensão de CSS, que se auto-intitula “CSS com superpoderes”.

Precisei começar um novo projeto de frontend montando apenas os HTMLs, JavaScripts e CSSs das páginas. Então resolvi conhecer melhor o Grunt e o Sass.

Percebi que dominar essas ferramentas gera dezenas de possibilidades novas para o desenvolvimento frontend. Claro que ainda não dominei, mas vou escrever um pouco sobre o que já aprendi.

Obs: Para todos os comandos abaixo estou utilizando a linha de comando (console) do Linux.

Para instalar o Sass é necessário o package manager do Ruby, o RubyGems.

sudo su -c "gem install sass"

Para instalar o grunt-cli é necessário o package manager do Node.js, o npm. Aproveito para já instalar os pacotes que vou utilizar: grunt-contrib-sass e grunt-contrib-watch.

npm install grunt grunt-contrib-sass grunt-contrib-watch

Estou usando o Bootstrap nesse projeto, então é necessário instalar o bootstrap-sass pelo npm.

npm install boostrap-saas

Minha estrutura de diretórios será a seguinte:

  • project/
    • node_modules/
    • public/
      • css/
      • img/
      • js/
      • index.html
    • sass/
      • style.scss
    • Gruntfile.js
    • package.json

No arquivo Gruntfile.js configuramos todas as tarefas que o Grunt vai executar. No caso desta publicação faremos a preprocessamento do SCSS para CSS.

Todas as tarefas do Grunt são executadas na linha de comando (console) precedidas por “grunt”, como por exemplo “grunt watch” ou “grunt build”.

Primeiro configurei o Sass para compilar para o ambiente de desenvolvimento, nesse caso o CSS não ficará comprimido, para que eu possa vê-lo em ação. No caso da configuração para ambiente de produção ele será comprimido.

Então configuro a tarefa do grunt-watch, ela acionará a tarefa “sass:dev“, que configuramos no passo anterior, a cada mudança que eu fizer aos arquivos *.scss. Isso serve para que eu não precise rodar um comando grunt manualmente a cada alteração no SCSS, o que seria contraproducente.

Obs: Para que isso aconteça é preciso deixar o comando “grunt watch” rodando na linha de comando (console).

No final do arquivo registro as tarefas com o registerTask. No caso apenas “grunt build“, que será usado para gerar o CSS final comprimido para produção, pois a tarefa que gera os arquivos não-comprimidos para ambiente de desenvolvimento já será chamada pelo grunt-watch e não precisa ser registrada novamente.

Estas tarefas registradas ao final do arquivo servem para agregar várias tarefas a um único comando. Assim quando você precisar rodar tudo de uma vez para gerar a versão de produção, você rodorá apenas “grunt build” e todas as tarefas da lista serão executadas. No nosso caso temos apenas o Sass por enquanto, mas futuramente podemos ter diversas tarefas a serem executadas para gerar os arquivos de prod.

Vou falar mais sobre Grunt e Sass no futuro. Se tiver alguma sugestão ou dúvida sobre o conteúdo desta publicação, dexe um comentário abaixo.

Categorias
Crônicas Tecnologia

Lançamento do Diligeiro

Em novembro de 2015 recebi uma ligação de uma empresa na qual eu já havia feito entrevistas e testes meses antes. Nesta ligação marcamos uma reunião e na próxima semana lá estava eu ouvindo pela primeira vez sobre o Diligeiro.

O Diligeiro é um aplicativo para smartphone que serve para conectar advogados correspondentes com escritórios de advocacia ou advogados que precisam do serviço de diligência.

O meu desafio era criar o backend desse aplicativo, uma API. O seu grande diferencial é a geolocalização, mostrar aos correspondentes quais diligências estão mais próximas dele. Ou seja, seria minha primeira experiência desenvolvendo uma API e também a primeira experiência com Geolocalização. Estudei, experimentei e hoje posso dizer que aprendi muito.

Enquanto ainda desenvolvia todas essas coisas novas para mim, fui escalado para fazer também o frontend web do mesmo produto. Mais uma série de aprendizados, desenvolver um frontend web totalmente baseado em requisições de API. Mais estudos, experiências e aprendizados.

Nas últimas semanas o aplicativo para Android e a versão Web do Diligeiro foram lançados em formato Alpha e em seguida em formato Beta, para que os usuários pudessem começar a utilizá-lo e passarem seus feedbacks.

Nesta semana o aplicativo Android e o WebApp foram abertos ao público.

Foram seis meses de muito trabalho, muitos aprendizados e também muita diversão, graças à equipe incrível da TIA Tikal.

O trabalho continua, as melhorias e novas ideias para funcionalidades não param nunca, mas a fase inicial de desenvolvimento acabou. Agora é hora de deixar que os usuários decidam se todo o esforço valeu a pena. O aplicativo iOS continua em desenvolvimento.

Me sinto orgulhoso pelo resultado dessa primeira fase e agradecido a todos os envolvidos. A equipe do Diligeiro e a equipe do LegalNote, o produto irmão do Diligeiro que já está no mercado a mais tempo.

Categorias
Tecnologia

Escondendo partes de um endereço de email com Python

Nessa semana o projeto em que trabalho precisou de uma forma para exibir emails de usuários durante o processo de cadastro, apenas para relembrar qual endereço o mesmo havia cadastrado na sua conta.

Exibir o email completo seria uma falha de privacidade/segurança muito grande, pois o cadastro é um processo público. Para evitar isso, a equipe decidiu mostrar apenas uma parte do endereço, para que o usuário identificasse caso seja o dono da conta, mas caso não seja não teria como saber qual é o endereço completo.

Para atingir isso, criei dois pequenos métodos: um que substitui partes de uma string por asteriscos e outro que divide o email em username e domínio, para então executar o método anterior em cada parte. O resultado é que o email  testedeemail@teste.com se transforma em te********il@tes**.com.

Para deixar o método de substituição da string mais flexível, eu adicionei dois parâmetros (begin_letter_amount, end_letter_amount) que controlam quantas letras serão exibidas no começo e no final da string.

O código segue abaixo.

Categorias
Tecnologia

Usando grunt-ng-constant para mudar o endereço da API no build de um app AngularJS

Desenvolvendo pela primeira vez um webapp que consome uma API com AngularJS (e usando o Yeoman e o Grunt para fazer build e para autorefresh durante o desenvolvimento) me deparei com uma questão: Como faria o build dos arquivos para diferentes ambientes? Atualmente este projeto possui os ambientes local (dev), homologação e produção, cada um com seu respectivo endereço de API.

Para atingir esse objetivo, e como essa era minha primeira vez com Angular e com Grunt, pesquisei muito e achei uma solução que funcionou bem.

Usango o pacote grunt-ng-constant eu consigo manter uma constante com a URL de cada ambiente separadamente. Dependendo do comando de build que eu executar no Grunt, terei um resultado diferente nessa constante.

O primeiro passo é instalar o pacote citado. Lembrando que estou utilizando o NPM como gerenciador de pacotes.

npm install grunt-ng-constant --save-dev

Depois temos que configurá-lo no nosso arquivo Gruntfile.js. Aqui segue um exemplo comentado:

Com o Grunt configurado, podemos passar para nosso AngularJS agora.

Dentro do arquivo app.js injetamos o parâmetro ENV no método run e passamos a constante apiBaseURL para o $rootScope, fazendo com que ela fique disponível por todo o app.

Agora você pode usar um service que faça as chamadas de API concatenando seu apiBaseUrl em todos os requests. Como no exemplo abaixo.

Para finalizar, fazemos o build com o seguinte comando:

grunt build:homolog

Podendo subistituir o homolog por develop ou prod (ou deixar sem nenhum, que pegará o padrão prod que definimos no Grunfile.js) e testar seus arquivos no diretório dist. Lembrando que o arquivo config.js será gerado automaticamente durante o build.

Tive uma boa batalha com o Grunt no começo, mas depois que aprendemos como funciona ele se torna um aliado incrível para o desenvolvimento.

Espero que tenha gostado dessa postagem, se tiver alguma dúvida ou comentário deixe abaixo.

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:

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. =)

Categorias
Tecnologia

Calculando a distância entre dois pontos e ordenando os resultados com GeoDjango

Atualmente tenho trabalhado com Django em um projeto de API que aproveita a geolocalização do usuário para ordenar os dados e mostrar os resultados mais próximos.

Para fazer isso é preciso utilizar um conjunto de ferramentas específicas para geolocalização que o Django possui, que são conhecidos como GeoDjango.

Nesse texto não irei cobrir as instalações e configurações específicas do banco de dados. Mas elas podem ser vistas aqui.

Começamos adicionando django.contrib.gis no nosso arquivo settings.py e partimos para os nossos models.

Ao invés de importar os models comuns do Django, importamos os models do django.contrib.gis.db. Nesse caso eu criei um abstract model que vai ser utilizado em todos os outros que necessitarem de endereço e localização.

O campo location vem com um tipo especial, o PointField, que irá guardar os dados de geolocalização do endereço.

No model que extende nosso AddressBaseModel utilizamos o GeoManager para substituir o manager dos models comuns do Django.

Agora podemos ordenar o resultado pela distância de um ponto específico.

Na view vamos utilizar o método distance do queryset para “criar” um campo com o mesmo nome e utilizar o order para ordenar por esse novo campo distance.

Para o método distance, precisamos passar uma origem. Origem será o local que o usuário está nesse caso. Para transformar as strings com a latitude e longitude em um ponto de geolocalização, utilizamos a classe Point do django.contrib.gis.geos.

Para formatar o resultado eu criei um método get_distance no StuffSerializer. O objetivo dele é mostrar a distância em metros ou quilômetros.

O resultado será algo como o exemplo abaixo.

Este é um exemplo de como podemos utilizar o GeoDjango para calcular a distância e ordenar os resultados através dela, mas existem diversas outras formas de fazer isso também.

Deixe seu comentário ou dúvida abaixo.