sexta-feira, 4 de março de 2011

Upload e Download de dados no Google App Engine - GAE

Olá para todos,

 Dando continuidade aos meus estudos de Python (Webapp-CE), visando um melhor uso do GAE, estudei uma funcionalidade muito bacana nessa semana: o Bulk Loader, que pelo tradutor do Google significa Carregador de Alto Volume.

 Ele serve para fazer upload de dados para o Banco de Dados (BD) no GAE e para fazer download de dados do mesmo. Então algumas tarefas interessantes podem ser feitas com ele:

  •  Backup de dados
  • Carregamento de cargas estáticas no servidor
  • Update de dados no servidor
  • Migrar dados de uma app para outra
Os dados podem ser baixados e carregados em dois formatos básicas: CSV e XML.

 Depois dessa introdução vou mostrar um exemplo na prática.

 Digamos que queremos fazer uma aplicação estados brasileiros. Vamos aos passos:

 1) Criar o modelo para a entidade de Estado (State) :


class State(db.Model):
    name = db.StringProperty(required = True)
    acronym = db.StringProperty(required = True)
    country = db.ReferenceProperty(reference_class = Country, required = True)


2) Configurar o arquvo bulkloader.yaml:

Esse arquivo configura as maneira como será feito o upload/dowload. Na documentação existe uma maneira de criar o formato básico do arquivo, mas já que vc está lendo esse post, pode copiar daqui mesmo = ). A parte inicial do arquivo contém os imports necessários para as transferências. Na documentação é ensinado como organizar imports de módulos de terceiros, caso vc deseja fazer algo mais rebuscado. Mas na maioria dos casos, o início não será alterado.

 Observe a parte final do arquivo, a partir do item "-transformers". Nessa seção que a configuração dos dados referentes ao State é feita. Primeiramente se indica o tipo do dado (kind=State). Em seguida, é configurado o tipo de arquivo (connector=csv). Portanto escolhi o formato de valores separados por vírgula, mas poderia ser XML, bastando colocar o valor simplexml.

 Depois são configuradas as propriedades do modelo. No caso, configurei 3 propriedades: a chave (key), o nome (name) e a referência para um país (country). Cada propriedade possui algumas opções de configurações.

 O item property contém o nome da propriedade em seu modelo.

 O item external_name indica o nome da propriedade no CSV. Esses nomes constarão como cabeçalhos na primeira linha do CSV

 O item external_transform indica uma transformaçao que deve ser feita no dado quando for feito o download. No caso, utilizei apenas o transform.key_id_or_name_as_string que transforma a chave (key) do modelo em String.

 O item internal_transform indica uma transformação que deve ser feita no dado quando for feito o upload. No exemplo, utilizei apenas o transform.create_foreign_key('Country',True) que serve para que seja criada uma referência para o tipo País (Country) e que se use o valor número para a chave (parâmetro com valor True.


python_preamble:
- import: base64
- import: re
- import: google.appengine.ext.bulkload.transform
- import: google.appengine.ext.bulkload.bulkloader_wizard
- import: google.appengine.ext.db
- import: google.appengine.api.datastore
- import: google.appengine.api.users

transformers:
- kind: State
  connector: csv
  connector_options:
    # TODO: Add connector options here--these are specific to each connector.
  property_map:
    - property: __key__
      external_name: key
      export_transform: transform.key_id_or_name_as_string
   
    - property: name
      external_name: name
     
    - property: country
      external_name: country
      export_transform: transform.key_id_or_name_as_string
      import_transform: transform.create_foreign_key('Country',True)



3) Montando dados a serem carregados:

 Para nosso exemplo, digamos que vc já tenha salvado o País Brasil em seu BD no GAE e que ele possua um id=1. Então construi um CSV (state.csv) com alguns estados brasileiros:


country,name
1,Acre
1,Piauí
1,Santa Catarina


4) Carregando os dados:

 Importante: o Big Table, BD do GAE, não possui esquema. Por essa razão, ele só conhece o "esquema" de seus modelos após vc ter salvado ao menos um item nele. Portanto, antes de carregar dados, certifique-se de salvar um dado do tipo desejado antes de tentar fazer o upload de dados.

Acresente em seu app.yaml a seguinte configuração:


builtins:
- remote_api: on

 Faça o deploy da app. Feito isso, execute o comando, onde "sua-app": é o id da sua aplicação:

appcfg.py upload_data --config_file=bulkloader.yaml --filename=state.csv --kind=State --url=http://sua-app.appspot.com/_ah/remote_api

Vc verá mensagens dos dados sendo carregados. Após o termino do carregamento, entre no console de administração de sua app para conferir os novos dados em sua aplicação.

5) Dowload dos dados:

 Experiemente salvar novos estados em sua aplicação repetindo os passos anteriores. Depois disso, vamos fazer o dowload de todos esses dados. Execute o comando:


appcfg.py dowload_data --config_file=bulkloader.yaml --filename=state_dump.csv --kind=State --url=http://sua-app.appspot.com/_ah/remote_api

 Esse comando irá baixar seus dados para o arquivo state_dump.csv. No meu caso, eu salvei todos os estados brasileiros em minha app, e o conteúdo baixado foi:

country,name,key
1,Acre,1001
1,Piauí,1002
1,Santa Catarina,1003
1,Alagoas,2001
1,Bahia,2002
1,Amapá,3001
1,Ceará,3002
1,Maranhão,3003
1,Rio de Janeiro,3004
1,São Paulo,3005
1,Amazonas,4001
1,Espírito Santo,4002
1,Rio Grande do Norte,4003
1,Sergipe,4004
1,Distrito Federal,5001
1,Paraíba,5002
1,Pernambuco,5003
1,Roraima,5004
1,Goiás,6001
1,Minas Gerais,6002
1,Rondônia,6003
1,Mato Grosso,7001
1,Rio Grande do Sul,7002
1,Tocantins,7003
1,Mato Grosso do Sul,8001
1,Pará,8002
1,Paraná,8003


Dessa maneira, espero ter conseguido exemplificar como fazer transferências de dados de seu computador para o GAE e vice-versa. Para conferir a documentação completa da funcionalidade, confira na Documentação.

 Deixe um comentário ou crítica se achou esse post util.

 Abs
 Renzo Nuccitelli

quinta-feira, 24 de fevereiro de 2011

Desrespeito do Ponto Frio com o consumidor

 Apesar de o blog ser voltado para a área de tecnologia, empreendedorismo também é uma característica marcante. Para que um negócio de certo, respeito ao consumidor/cliente é fundamental.

 Então darei um contra exemplo, citando o mau atendimento do Ponto Frio:

Infelizmente cai na besteira de efetuar uma compra no Ponto Frio e só depois vi esse site: http://www.guiadosac.com.br/guia-do-sac/ponto-frio/base_view#1298558355

 Irei ao Procon para fazer valer meus direitos, mas segue a história.

Atendimento online realizado no Ponto Frio em 24/02/2011 às 11:25

Atendente Ponto Frio: Olá Renzo ! Bem-vindo ao atendimento online Pontofrio. Em que posso ajudar?

Renzo: Estou ligando no telefone do SAC de vcs, e apesar de estarmos no horário de atendimento, ninguém atende
Renzo: Esse atendimento possui protocolo?

Atendente Ponto Frio: Somente se for consultado algum pedido.

Renzo: Entendi
Renzo: Bom
Renzo: Antes de ontem compareci na loja do Ponto Frio no Shopping Center Vale, de São José dos Campos/SP
Renzo: e efetuei a compra de uma geladeira no valor de R$ 2.000,00
Renzo: alias, R$ 2.700,00
Renzo: mandei buscar a geladeira ontem. A mesma se encontrava totalmente embalada. Ao abrir a embalagem, justamente por baixo do isopor, ela se encontrava danificada, com um aranhão profundo

Atendente Ponto Frio: Sim .

Renzo: Compareci a loja ontem mesmo, dado o prazo de 72 horas para efetuar a troca do produto, mas o gerente se recusou a fazer a troca

Atendente Ponto Frio: Sim porque se trata de uma avaria e a empresa não efetua a troca de produtos avariados.

Renzo: então estou entrando em contato com o SAC, para pedir uma solução. Caso não haja, vou procurar meus direitos junto ao Procon e Tribunal de Pequenas causas, além da propaganda ruim que farei da loja nos meios de imprensa e internet

Atendente Ponto Frio: Só trocamos por defeito técnico.

Renzo: Então a empresa vende produtos avariados e não efetua troca?

Atendente Ponto Frio: O produto e para ser verificado no ato da entrega.

Renzo:
Isso não foi avisado, além dela ter vindo embalada
Renzo: Engraçado que em qualquer outra loja, eles realizam a troca, principalmente se mandaram a peça avariada.

Atendente Ponto Frio: E na nota fiscal consta termo de conformidade informando que o produto foi entregue em perfeito estado.

Renzo: pois é, o gerente não entregou a nota do produto

Atendente Ponto Frio: Se for assinado não temos mais como troca.

Renzo: e se recusou a entregar a mesma depois que compareci a loja
Renzo: esta bem, poderia me fornecer um protocolo de atendimento, de forma que possa comparecer ao procon?
Renzo: Em minha nota fiscal consta um termo de vistoria, com um carimbo, com as opções sim e não

Atendente Ponto Frio: Só e gerado um protocolo se for aberto alguma reclamação mais como se trata de uma avaria não geramos reclamação.

Renzo: e o mesmo não está com nenhuma opção assinalada, e muito menos assinado
Renzo: mas eu estou reclamando de um produto que nem a vistoria foi assinada, quer dizer que nem um protocolo eu posso receber? Em qualquer empresa que ligo ao sac, a primeira coisa que me informam é o protocolo
Renzo: Se trata de uma avaria em cuja nota não consta a assinatura do produto ter sido vistoriado pelo cliente

Atendente Ponto Frio: .

Renzo: Existe uma Ouvidoria no Ponto Frio?

Atendente Ponto Frio: Sim mais só trata casos de imprensa.

Renzo: Poderia me passar o telefone?

Atendente Ponto Frio: A ouvidoria que efetua contato com o cliente mais e como eu informei antes eles só tratam casos de imprensa.

Renzo: Não existe um telefone para contato com o Cliente? Então vai ser a primeira empresa que vejo com esse procedimento, totalmente em desacordo com o Código do Consumidor. Não fornecem protocolo de atendimento no SAC e não possui telefone de contato com uma Ouvidoria. Desse jeito não vai ser difícil conseguir colocar isso na imprensa e eles entrarem em contato comigo

Atendente Ponto Frio: E como informado até após não trocamos por avaria se o cliente retira o produto da loja a responsabilidade passa a ser do
mesmo.

Renzo: Estou com a nota que consta o campo de vistoria do produto que não está assinado. Basicamente vc não tem protocolo para me dar desse atendimento, não tem o telefone da Ouvidoria para me informar e está dizendo que o problema é meu, correto?

Houve um erro de conexão.
Por favor, reconecte-se para efetuar um novo atendimento. 

 Enfim, fica a dica para evitaram entrar numa fria comprando no Ponto Frio.

quarta-feira, 2 de fevereiro de 2011

Webapp-CE: Mais um framework saindo do forno

 Bom dia!

 Estava meio sumido do Blog, mas a Nuccitec tem consumido praticamente todo meu tempo :).
 O fato é que agora estou me aprofundando para aprender Python, influenciado pelo Google App Engine(GAE), infra da qual gosto muito. E para aprender, nada melhor que logo fazer framework...rs.
 Quando você lê os primeiro passos no GAE voltado para o Python, acaba se decepcionando com o webapp, pequeno framework web que o Google fez. A necessidade de ficar mapeando URL´s para handlers é um saco. Inclusive na própria documentação se comenta pra usar outros frameworks.
 Conversando sobre isso com meu amigo Reginaldo, ele me deu a idéia de fazer um REST seguindo uma convenção para executar os handlers, eliminando a tarefa braçal, chata, e suscetível a erros de ficar mapeando URL´s para Handlers. Assim nasceu o Webapp-CE, ou Webapp Convetion Engine.
 A idéia básica da convenção é parecida com existente no VRaptor, framework Java para WEB, que também é brasileiro. Suponha que você tem um handler br.com.SeuHandler que possua o método executar. A convenção consiste em mapear automaticamente esse método para a URL /br/com/SeuHandler/executar. E o que vir depois desse padrão é passado como parâmetro para o método executar.
 O framework também faz o inverso, ou seja, você passa o método e ele te devolve a URL. Isso torna o processo de mapeamento automático e se você mudar o nome do Handler ou até mesmo movê-lo para outro diretório, a URL muda de forma automática.
 O legal de tudo isso foi ter feito o framework em duas madrugadas. Ele possui apenas dois scripts. Mas no fonte do projeto existem também os testes unitários.
 A experiência serviu para me indicar que efetivamente o Python te da uma liberdade imensa e é uma tremenda ferramenta. A introspecção dele é excelente, bem como a parte de testes unitários. Cada vez mais tenho menos saudades do Java...rs. Em breve devo lançar o curso Python Básico e Python para GAE na Nuccitec.

 Espero que curtam o projeto.
 Renzo Nuccitelli

terça-feira, 21 de dezembro de 2010

Lançada primeira versão do framework JFERA!

 Olá visitante,

 Nos últimos 8 meses estive desenvolvendo uma aplicação real utilizando Adobe Flex e o Google App Engine. Já postei aqui a aplicação, que se chama Revelação Virtual. Durante esse período foi intenso o aprendizado para integrar a tecnologia Flex, que permite fazer interfaces muito bonitas e responsivas, no Google App Engine, servidor fornecido pelo Google. Pensei que seria interessante repassar o conhecimento para outras pessoas.

 Dessa maneira, fui desenvolvendo em paralelo um framework para integrar as duas tecnologias, aproveitando para documentar muitas dores de cabeça experiências. O resultado foi o JFERA (Java/Flex Easy Remote Architecture). O objetivo foi criar uma arquitetura simples para efetuar chamadas remotas Flex no Google App Engine. A documentação pode ser conferida em http://jfera.nuccitec.com.br. A documentação se encontra em inglês (pelo qual já peço desculpas adiantadas, já que nunca estudei a língua...rs), mas já enviei um artigo para ser postado na revista MundoJ, e assim que eu souber em qual edição ele será incluído, se for incluído em alguma, venho aqui atualizar o blog.

 Diferente do JColtrane, onde contei com o Prof. Doutor Eduardo Guerra como mentor, esse framework eu desenvolvi sozinho. Não ter com quem tirar dúvidas realmente foi um grande desafio.

 A documentação não está 100% completa, mas já é possível fazer várias aplicações bacanas utilizando o projeto.

 Espero que gostem e me enviem sugestões.

 Até a próxima,
 Renzo Nuccitelli

terça-feira, 14 de dezembro de 2010

Revelação de Fotos pela internet

Recentemente o sistema de revelação de fotos da Nuccitec, o Revelação Virtual, ganhou sua versão web. Agora não é mais necessário instalar a aplicação, basta acessar o endereço fornecido pelo cliente. Em São José dos Campos, por exemplo, o laboratório Digital do Vale oferece o serviço através do endereço www.digitaldovale.com.br.

 A idéia do projeto é se espalhar pelo Brasil, viabilizando aos laboratórios de revelação fornecerem seus serviços pela internet, sem ter que se preocupar com tecnologia. Dessa forma, eles podem recuperar também os clientes que perderam para grandes players, uma vez que podem oferecer preços mais acessíveis e, principalmente, podem entregar as fotos mais rapidamente a seus cliente.

 Além disso, o caráter mais pessoal dos laboratórios locais trazem mais confiança a seus cliente, que possui um canal mais direto para resolução de problemas que possam ocorrer com a revelação, ou até mesmo para pedir que o laboratório retoque suas fotos usando Photoshop. Que grande player consegue fornecer um serviço mais humano dessa forma?

Esperamos que gostem do novo produto.

quinta-feira, 4 de novembro de 2010

CSS no Adobe Flex

 A documentação da Adobe aborda muito conteúdo sobre o CSS no Flex. Inclusive o Flash Builder contém muito boas ferramentas, como autocomplete no CSS e a possibilidade de vc editar o visual de um componente no modo design e depois exportar o CSS. Contudo, um amigo do trabalho me mandou dois links muito interessantes para trabalhar com CSS:

 No primeiro vc acessa a documentação completa das propriedades de CSS dos diferentes componentes. No segundo vc altera o visual dos componentes e confere as alterações "on the fly".

 Apesar de curto esse post, espero que ajude a muita gente.

 Até o próximo post.

quarta-feira, 20 de outubro de 2010

Adobe Flex - Setando um item em ComboBox

 Olá a todos. Estou um tempo sem postar pq estou acabando a documentação preliminar do JFERA, framework de integração Java-Flex no Google App Engine.

 Apesar disso, sempre acompanho e respondo questões de Flex no GUJ, que apesar de ser um Fórum Java, é muito bom para qualquer linguagem de programação. Por algumas vezes respondi algumas questões sobre problemas para setar um item em um ComboBox.

 Já passei por esse problema uma vez, quando eu precisava mostrar um formulário de edição de endereço. Nele existiam dois combos, um para estado e outro para cidade. O combo da cidade era dependente do de estado, de forma que seu dataProvider era setado de acordo com o estado escolhido. Na hora de editar o endereço, eu pegava os dados de minha classe de domínio e setava meus campos visuais. Ao setar o estado, tudo funcionava perfeitamente, mas a cidade não.

 Depois de um tempo estudando percebi que da forma que eu fazia as coisas, ocorria o seguinte:

  1.  Eu setava o selecteIndex do combo
  2. Depois de escolhido o estado, eu setava o dataProvider com as respectivas cidades
Me ocorreu então que era provável que ao setar o dataProvider não devia haver uma lógica para conferir se o mesmo continha o objeto já indicado no selectedItem, e por isso que não era mostrado o item corretamente. Pensando nisso, criei um combo personalizado para essas horas:


package br.com.nuccitelli.component {
     import flash.events.Event;
     import mx.binding.utils.BindingUtils;
     import mx.utils.ObjectUtil;
     import spark.components.DropDownList;

     public class DropDowListWithMemory extends DropDownList {
         private var _selectedItemInMemory:Object=null;

         public function DropDowListWithMemory() {
              super();
              BindingUtils.bindSetter(dataProviderHandler,this,[ "dataProvider","length" ]);
         }

         private function dataProviderHandler(obj:Object=null):void {
              if(_selectedItemInMemory) {
                   for each(var item:Object in this.dataProvider) {
                        if(this.equals(item,_selectedItemInMemory)) {
                            this.selectedItem=item;
                            break;
                        }
                   }
              }
         }

         /**
          * The selectedItemInMemory sets selectedItem in 2 ocasions:
          * 1) When you set selectedItemInMemory and data provider has an equals object
          * 2) When dataProvider change and has an object equals to selectedItemInMemory
          **/
         public function get selectedItemInMemory():Object {
              return _selectedItemInMemory;
         }

         /**
          * The selectedItemInMemory sets selectedItem in 2 ocasions:
          * 1) When you set selectedItemInMemory and data provider has an equals object
          * 2) When dataProvider change and has an object equals to selectedItemInMemory
          **/
         public function set selectedItemInMemory(value:Object):void {
              _selectedItemInMemory=value;
              dataProviderHandler();
         }

         private function equals(item:Object,item2:Object):Boolean {
              return ObjectUtil.compare(item,item2)==0;
         }
     }
}


 Também fiz um código unitário, para exemplificar o uso:




package br.com.nuccitelli.component {
     import flexunit.framework.Assert;
     import mx.collections.ArrayCollection;

     public class DropDowListWithMemoryTest {

         [Test]
         public function testSelectedItemWithExistingDataProvider():void {
              var combo:DropDowListWithMemory=new DropDowListWithMemory();
              combo.dataProvider=new ArrayCollection([ "A" , "B" , "C" ]);
              Assert.assertUndefined(combo.selectedItem);
              combo.selectedItemInMemory="B";
              Assert.assertEquals("B" , combo.selectedItem);
         }

         [Test]
         public function testSelectedItemWithOutExistingDataProvider():void {
              var combo:DropDowListWithMemory=new DropDowListWithMemory();
              combo.dataProvider=new ArrayCollection([ "A" , "B" , "C" ]);
              Assert.assertUndefined(combo.selectedItem);
              combo.selectedItemInMemory="D";
              Assert.assertUndefined(combo.selectedItem);
              combo.dataProvider=new ArrayCollection([ "A" , "B" , "C" , "D" ]);
              Assert.assertEquals("D" , combo.selectedItem);
         }
     }
}



Usei o DropDownList, que o combo no Flex 4. Se vc estiver usando o Flex 3, basta trocar o DropDownList por Combo. Para utilizar, basta usar o selectedItemInMemory no lugar do selectedItem.

 Se vc gostou desse post, peço  a gentileza de divulgar o blog bem como os cursos da Nuccitec. Também deixe uma mensagem no blog :)

 Até o próximo post.