Todos os posts de Claudio Santos

SIG com Python

Este é um tema vasto e pretendo falar sobre alguns conceitos como: Datum, Sistemas de Coordenadas, Projeção, Sistema de Referência e conceitos de SIG (Sistemas de Informações Geográficas).
Não conseguiremos cobrir os conceitos de operações espaciais, porém na demonstração isto será ilustrado em linhas gerais.

Este é um artigo derivado do screencast SIG com Python, disponível no YouTube.

O ambiente em que executamos esta aplicação está disponível no servidor de código fonte da sigma em: https://gitlab.sigmageosistemas.com.br/screencasts/gis-hands_on, neste repositório você tem o Dockerfile e no repositório https://gitlab.sigmageosistemas.com.br/dev/dockerize você encontra instruções de como buildar e montar esta imagem.

Localização e Medições

Localizar e Medir são uma necessidade inerente ao ser humano desde as sociedades mais antigas.

Com o passar dos séculos, foi preciso definir medidas mais precisas e padronizá-las permitindo às pessoas em qualquer local do planeta, entender e realizar medições, para estimar distancias, áreas e locais, definindo precisamente as fronteiras de um pais, estado, cidade, fazendas e terrenos.

A Geodésia é a área de conhecimento que estuda e aprimora os métodos de medição do planeta.

O planeta terra tem forma similar à uma batata, sendo sua forma matemática mais aproximada, uma elipse, sendo assim, o primeiro passo para calcular as medidas do planeta é a definição de uma elipsoide de referência.

Devido a deformidade do planeta, a forma eliptica se encaixa melhor em variadas regiões, conforme alteramos os parâmetros de tamanho e posição deste em relação ao eixo da Terra. A estes parâmetros que definimos para o elipse chamamos de Datum.

Com base no Datum, podemos determinar em radianos o posicionamento de algo no globo terrestre, ou até mesmo em métros.
Porém quando precisamos representar esta informação em um mapa, em outras palavras, precisamos transformar uma bola em uma folha de papel, é necessário projetar do dado geográfico, projeção é um modelo matemático com base no datum e sistemas de coordenadas, que tranforma as coordenadas geográficas em um plano cartesiano.

A projeção tem diversos tipos:

Cilindricas (Normal, Transversa, Obliqua), Planas (Polar, Equatorial, Obliqua), além de outros tipos como Gnomonica, Estereográfica e etc.

Cada uma delas é ideal para determinadas regiões ou cenários de uso.

O que é um Sistema de Informação Geográfica (SIG)?

É um sistema computacional que utiliza dados de localização geográfica como: coordenadas ou endereços, em conjunto com algoritmos que consumam estes dados para contruir informações.

Não necessariamente um sistema GIS precisa exibir um mapa, ele pode por exemplo informar o tempo estimado para uma entrega ser concluída ou enviar um SMS indicando que seu bairro passará por uma instabilidade elétrica.

Checklist

Nesta seção apresentamos um checklist de como iniciar a definição de seu SIG. São perguntas importantes, que devem ser respondidas antes de sua construção. É uma etapa crucial, para que o SIG, atenda as necessidades da sua organização.

Qual é o sistema de referência de seu dado geográfico?

Por mais que para os desenvolvedores de sistema, este seja uma assunto muito complexo, é extremamente importante ter o conhecimento sobre alguns detalhes do dado geográfico utilizado.

A parte mais importante à se pensar ao criar um sistema de informação geográfica é qual sistema de referência vamos utilizar. Mas como escolher?

Respondendo as questões:

  • Qual abrangência da área de trabalho, cidade, estado, países?
  • Qual principal requisito: precisão de área e comprimento ou precisão de posicionamento?

A resposta destas duas questões é geralmente respondida pela equipe cartográfica de seu cliente ou por geográfos, porém é importante compreender os motivadores destas escolhas.

Qual a forma de obtenção das coordenadas?

Para sistemas que requerem precisão é necessário coletar as informações geográficas com equipamentos avançados, que possuam precisão métrica (ou submetrica) e permitam seu pós processamento, aumentando ainda mais sua precisão.

Para casos em que isso não é necessário, somente as coordenadas gps de um smartphone são suficientes.

Mas tenha em mente que toda iteração (ou interação) com o mapa para criar dados geográficos à mão livre é imprecisa, pois o nível de zoom e a referência pixels para coordenada geográfica prejudicam a precisão.

Qual é a informação que será produzida com este dado geográfico?

É muito importante pensar nisso quando vai contruir um sistema, pois ter uma mapa em sua aplicação a enriquece bastante, mas se não há uma utilidade funcional isso pode acabar tornando-se um grande problema.

Então é crucial pensar no resultado final, ou na informação que queremos produzir e mostrar aos nossos usuários.

Demo

A demonstração vai exibir código que permite importar dados de um arquivo shp, em seguida como apresentá-los no mapa e por fim sua consulta.

Shapefile: Formato binário de arquivos espaciais da ESRI, que se tornou bastante popular.
PostGIS: Banco de dados geográfico.
GEOS: Biblioteca de Operações Espaciais e Conversões;
TileStache: Servidor GIS de Vector Tiles;
Django TileStache: APP Django que cria um endpoint tilestache para suas entidades geográficas.
Django Rest Framework: Permite a criação de endpoints rest para seus django models.
Django Rest Framework GIS: Permite a serialização de Atributos geométricos e operações espaciais através do DRF endpoint;

Referências:

UFABC Flavia Feitosa: https://flaviafeitosa.files.wordpress.com/2015/06/02a_sistreferencia-compressed.pdf
Geodésia: https://pt.wikipedia.org/wiki/Geod%C3%A9sia
Curso de PostGIS Sigma: https://github.com/sigma-geosistemas/sigma.universidade.postgis/wiki/aula02
imagem qgis: https://flic.kr/p/UGGiGu

Integrando o Django com o AD Microsoft

Em um projeto novo da SIGMA, nos foi solicitado que utilizássemos
os usuários registrados no Active Directory do cliente, significando
que teríamos de integrar toda a funcionalidade de login do nosso projeto
Django com este camarada.

Existem diversas razões para esta integração, principalmente no nosso
caso, em que o cliente final é uma grande empresa, com toda uma infraestrutura
de Tecnologia da Informação bastante sólida e madura.

Entre estas razões, se encontram, em ordem de prioridade:

  1. Cliente não quer mais um usuário/senha para usar um sistema especialista;
  2. Equipe de TI não quer gerenciar os cadastros dos usuários em mais um sistema;
  3. Economia no desenvolvimento, de tempo e dinheiro;

Como funciona?

O Django, como um framework, já possui toda um maquinário importante para
criação e manutenção de permissões e grupos. Mas existe uma “pegadinha”:
o usuário em questão precisa existir no banco de dados que estamos usando.

Isto porque as permissões e grupos estão armazenadas em um banco de dados
relacional e existem relações com chaves estrangeiras entre usuários, permissões
e grupos.

O fluxo mais simples, portanto é:

  1. Usuário se autentica com a senha do Active Directory;
  2. Usuário está inativo no AD? Caso esteja, impeça o login e inative o usuário local;
  3. Usuário não está inativo, confira a senha;
  4. Sistema confere se o usuário já existe na base local;
  5. Caso ele já exista, ok, não faça mais nada;
  6. Caso ele não exista, copie os dados do usuário e crie um novo usuário na base local;
  7. Associe permissões/grupos;
  8. Vitória;

Seguindo este fluxo, todos os componentes do Django continuam a funcionar e não
precisamos criar permissões remotas no AD (ou LDAP) o que tornaria tudo
mais complicado de administrar (pense nas migrações do Django).

Outro ponto importante, delimitador deste fluxo, é a necessidade das sessões HTTP
no Django. Sem existir um usuário na base, não conseguiremos manter as sessões. Teríamos
de adaptar também o componente que registra e valida as sessões ativas.

Portanto esta arquitetura de cópia de usuários é a mais comum para integração
em bases de usuários “remotas” (este fluxo também é usado em pacotes que realizam
autenticação e integração com outros provedores de login, como OAuth2, por exemplo).

Como realizar a integração?

Felizmente, o Django é um projeto bastante modular. Neste caso em específico,
o Django aceita a configuração de settings de autenticação, permitindo
inclusive a autenticação em múltiplos backends, em cascata. Caso a autenticação
do usuário não funcione em um backend, ele tenta no próximo.

A arquitetura do Django facilitou bastante nossa vida neste sentido.

Quais são os passos:

Configurar o AUTHENTICATION_BACKENDS em seus settings.py

Desenvolver ou reutilizar um pacote open-source que já faz a conexão
e validação do usuário no Active Directory. No nosso caso, utilizamos o
django-python3-ldap.

Configurar as variáveis do django-python3-ldap:

# settings.py
LDAP_AUTH_USER_FIELDS = {
  "username": "uid",
  "first_name": "givenName",
  "last_name": "sn",
  "email": "mail",
}
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_SYNC_USER_RELATIONS = "jango_python3_ldap.utils.sync_user_relation"
"  LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
# Atenção neste ponto, pois ele é necessário para funcionar com o AD
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"```

Para a url indique conforme abaixo substituindo <ip_ou_dominio>
por seu ip ou dominio:

# settings.py
LDAP_AUTH_URL='ldap://<ip_ou_dominio>:389'

Utilize o comando dsquery user no ms-dos para obter a seguinte informação informação:

C:\Users\csantos>dsquery user
"CN=Claudio Santos,CN=Users,DC=server,DC=local"
"CN=Guest,CN=Users,DC=server,DC=local"

Utilize os três critérios de pesquisa, resultando em: LDAP_AUTH_SEARCH_BASE = 'CN=Users,DC=server,DC=local'

Comumente temos dois tipos de usuários: InetOrgPerson ou Users. Nós utilizamos o Users,
configurando LDAP_AUTH_OBJECT_CLASS = 'Users'

Configure este settings com uma variável que podemos pegar na
tela de criação de usuário no campo em destaque na imagem,
ela representa o Pre-Windows 2000 Domain Name do Active Directory:

attribute-editor-uid

# settings.py
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = 'SERVER'

Por fim vá em Active Directory Users and Computer, vá na pasta Users,
em seguida, clique com o botão direito, propriedades e por fim na aba Atributte Editor
informe o login do usuário no atributo uid, como mostrado na imagem abaixo;

attribute-editor-uid

Conclusões

Não vou discorrer aqui sobre os benefícios técnicos de usar open-source ou
coisa do tipo, mas sim dos benefícios gerados por uma integração deste nível.

As vantagens auferidas tem um profundo impacto psicológico sobre o usuário final
e sobre o retorno sobre investimento.

Em primeiro lugar, o sistema parece ter sido construído e fazer parte de fato
de toda a organização. O usuário não tem a impressão de sair de seu ambiente tecnológico,
confortável e seguro, para trabalhar em um sistema externo.

Como segundo ponto, o usuário tem uma barreira menor para utilizar o sistema.
A adoção de novos sistemas em organizações grandes sempre foi e sempre será
uma dificuldade, pelo próprio fator humano. Como a barreria de início é menor,
maior será a adoção dos usuários.

Em terceiro lugar, mas consequência do segundo ponto, quanto maior a adoção
dos usuários, maior o impacto que o novo sistema terá na organização, trazendo
um retorno maior para os stakeholders do projeto.

TDD (Desenvolvimento Guiado por Testes)

A primeira pessoa a apresentar este método de desenvolvimento foi Kent Beck, autor do livro Extreme Programming, e relata em um capitulo de seu livro a necessidade de testar prematuramente, frequentemente e automaticamente, destacando que tal abordagem é necessária, pois quanto mais cedo encontrarmos erros mais barato será consertá-lo. Três anos mais tarde ele lança o livro Test Driven Development by example definindo uma estratégia de desenvolvimento que além de permitir detectar erros prematuramente, cria um ambiente de trabalho que favorece o desenho de uma boa arquitetura.

A principal característica do TDD é sua simplicidade, onde a principal regra a seguir é: criar um teste que falhe, escrever o código para o teste passar e em seguida refatorar seu código.

Perceba que não foi dito que uma linguagem é preferível que outras, nem que uma IDE é necessária para trabalhar desta forma. É claro que será necessário um framework de testes e o Kent Beck criou um que foi base para todos os outros que surgiram, o JUnit.

Certo, neste ponto começam a surgir as dúvidas, sobre como definir os métodos de testes, qual a granularidade do meu teste e por onde começar.

Um sistema sempre vai começar a partir dos requisitos ou estórias, uma boa documentação que defina quais serão os comportamentos do software.

Nós, na Sigma, descrevemos o comportamento de nosso sistema utilizando estórias. Então vamos escrever uma estória hipotética que define o comportamento de um sistema.

Eu como comprador

quero adicionar produtos comprados ao estoque

para manter o saldo em estoque atualizado;

Analisando a estória acima podemos extrair algumas entidades que o sistema de almoxarifado deve ter: Produto e Estoque. Além disso podemos visualizar os comportamentos esperados: dar entrada no estoque de um produto, consultar saldo de um produto em estoque;

Então podemos criar um teste que falhe para um dos comportamentos:

import unittest

class TestEstoque(unittest.TestCase):

    def test_estoque_deve_dar_entrada_em_produto(self):
        produto = Produto()

Este teste vai falhar, pois não existe uma classe Produto definida em nosso sistema.

import unittest


class Produto(object):
    def __init__(self):
        pass


class TestEstoque(unittest.TestCase):

    def test_estoque_deve_dar_entrada_em_produto(self):
        produto = Produto()

O código vai passar então podemos refatorar e criar corretamente um módulo e retirar o código da classe produto do module de teste;

Damos o segundo passo na implementação:

import unittest

class TestEstoque(unittest.TestCase):

    def test_estoque_deve_dar_entrada_em_produto(self):
        produto = Produto()
        estoque = Estoque()
        quantidade = 10
        estoque.entrada(produto, quantidade)

Perceba que avançamos alguns passos extras na implementação, pois temos experiência na criação de classes e podemos ser flexíveis e escrever mais trechos de código que temos fluência. Esta flexibilidade não pode ser usada em excesso, pois o principal objetivo ao escrever código que falhe é que esta falha seja fácil de ser encontrada, portanto se eu escrever muitas linhas de código eu posso gastar muito tempo consertando ele. Portanto aprecie com moderação.
O código acima está falhando, pois o método entrada não está implementado.

Portando vamos implementá-lo:

class Estoque(object):

    def __init__(self):
        self._estoque = []

    def entrada(self, produto, quantidade):
        self.estoque.append({
            'produto': produto,
            'quantidade': quantidade
        })

Novamente o teste vai passar, mas como definir que este teste é válido?

Asserções!

Asserção é um método para validar se uma condição de nosso teste é válida e caso ela não seja, minha asserção não será válida e vai lançar uma exceção.

import unittest

class TestEstoque(unittest.TestCase):

    def test_estoque_deve_dar_entrada_em_produto(self):

        # Definições
        produto = Produto()
        estoque = Estoque()
        quantidade = 10

        # Ações
        estoque.baixar(produto, quantidade)

        # Asserção
        saldo = estoque.saldo(produto)
        self.assertTrue(saldo == 10)

Perceba a estrutura de um testes, eu defino o estado inicial para meu teste, tomo uma ação e verifico se meu teste chegou ao estado esperado.

Este artigo serviu de base para o screencast sobre TDD, que foi gravado e está disponível neste link, com a apresentação também disponível neste link.

Automação de Infraestrutura com Puppet

Let the computer do the repetitious, the mundane – it will do a better job of it than we would. We’ve got more important and more dificult things to do.
Trecho retirado do livro Programador Pragmatico.

O profissional de desenvolvimento de software conta com um grande trunfo em sua manga que é frequentemente negligenciado, um trunfo que vem sendo utilizado vastamente por outras áreas em que sua complexidade é maior do que a encontrada na área de software. Com a automação pode ser alcançado o que economia chama de lucro, pois um profissional aumenta sua produtividade e a qualidade do que é produzido. Estes dois parametros trarão uma enorme mudança em sua carreira, então a partir deste momento se torne um profissional que se vale deste trunfo e se destaca dos demais, simplesmente aplicando em seu trabalho o que de melhor fazemos: automatizar tarefas.

Durante os ultimos anos vemos as práticas DevOps se tornando cada vez mais a cultura dos desenvolvedores e administradores. Neste artigo faremos uma comparação da utilização de uma das tecnologias de escrita de infraestrutura como código criada pela empresa Puppet, o produto escolhido se chama puppet-agent e vamos escrever em uma dsl de mesmo nome, que tenta tornar este processo tão simples como listar quais software serão utilizados.

#####Aplicamos esta prática em dois cenários:

#####Windows:

Neste cenário o cliente utiliza Windows, embora esta arquitetura seja compatível com ambientes linux o cliente tinha mais experiência com Windows e o utilizava em todos ambientes do desenvolvimento à produção. Para prover serviços de mapa e hospedar a aplicação web map é utilizado IIS 7.5, Geoserver, Postgresql e Postgis. Nós escolhemos o puppet-agent e o script será aplicado em uma maquina existente preparada para receber as instalações oriundas do script puppet.

O primeiro passo é instalar o puppet para poder executar o script, que como dito é uma dsl simples

A script que criamos é composta por resources, cada resource tem funções como: instalação de pacote, execução de comandos shell, agendamento de tarefas, administração de serviços, criação de usuários, operações no sistema de arquivos e etc.

Os resources podem ser combinados de forma a determinar qual é o resource requerido para que um outro seja executado, ou assim que determinado resource for executado ele pode notificar outro para que este seja executado.

Uma dificuldade ao utilizar o puppet para instalação de sistemas é que o windows não possui um gestor de pacotes, os instaladores são individuais e distribuídos individualmente por cada fornecedor. Desta forma o processo de instalação exige que em alguns casos seja utilizado o processo de instalação headless que é disponibilizado pela ferramenta de empacotamento utilizada pela empresa que provê o instalador do software. Infelizmente por não haver uma padrão isto é o que mais dá trabalho durante a redação do script.

Após as instalações é necessário configurar as aplicações instaladas, iniciar serviços ou agendar tarefas.

Basicamente utilizamos o resources exec, que permite executar comandos shell, o file, que permite copiar arquivos e o scheduled_task para agendar tarefas.

Com o exec nós configuramos o postgres, executamos os scripts sql de criação das feições espaciais, com o file copiamos arquivos, como por exemplo o diretório data_dir do geoserver e com o scheduled_task configuramos a tarefa de execução do geoserver.

Utilizamos também o Hiera para criar um arquivo de configuração externo e permitir que o script fosse executado independente dos diretórios em que os arquivos existiriam ou do local em que desejamos instalar.

A grande desvantagem foi o tempo que levamos para montar este script. É bastante trabalhoso e em por utilizarmos o windows neste cenário, tivemos pouca documentação disponível principalmente para as instalações headless de cada instalador. Porém quando este desafio foi ultrapassado a grande vantagem foi conseguir instalar com pouquissima dificuldade quatro maquinas diferentes, uma maquina na cloud (Windows Server 2012 R2 ) e três estações de trabalho com Windows 7. O primeiro grande beneficio é que temos a certeza que todas as maquinas possuem a mesma versão e configuração e uma vantagem é que nossa equipe é distribuída e podemos contar com o apoio de outros desenvolvedores que não estejam geográficamente perto e eles conseguirão replicar o ambiente com rapidez e poderão efetivamente apoiar o desenvolvimento do software.

#####Linux:

Montar a maquina servidora de um web map escrito em python e javascript, que consulta um banco de dados geográfico.

De cara podemos dizer que só há vantagens ao utilizar vagrant e puppet para montar ambientes em Linux (dist Ubuntu) não tivemos um décimo das dificuldades apresentadas no Windows e o tempo transcorrido para redigir os scripts foi muito menor.

O maior desafio que encontramos é que algumas versões disponíveis por padrão nos repositórios do apt-get podem estar com uma grande defasagem, o que pode induzir ao erro ao usar uma versão antiga que contém bugs que já foram resolvidos, porém uma vez que você configura o repositório correto do pacote e instala as versões mais recentes o restante é muito prático.

Nós utilizamos postgres e postgis e é excelente ter um repositório de pacotes, fizemos todas as configurações, criação do database e configuração da extensão postgis.

Uma novidade foi utilizar um provider diferente ao utilizar o resource package, pois utilizamos o pip3 para instalação dos pacotes python, porém não tivemos problema algum.

Não identifiquei desvantagens ao escrever o script puppet para uma maquina linux, basta ter atenção às versões que são instaladas pelos repositórios default do apt-get e na dúvida utilize o repositório da fornecedora do software.

Para não dizer que a experiência foi livre de problemas, perdemos um tempo para instalar corretamente o puppet, pois às maquinas ubuntu disponíveis não trazem mais esta instalação por padrão.

A grande vantagem neste caso é que a replicação para outros provider do vagrant se torna fácil, permitindo que eu utilize o virtualbox, vmware, amazon aws ou digital ocean.