segunda-feira, 15 de março de 2010

O "jeitinho" brasileiro!

As vezes eu gosto de ser brasileiro, as vezes tenho vergonha, acho que muitos se sentem assim de vez em quando.

O "jeitinho" brasileiro é bem mais do que achar uma solução criativa para os problemas, é muitas vezes, talvez na maioria delas, de pensarmos apenas no nosso próprio umbigo, nos entregarmos aos nosso desejos. Deixar de lado as nossas obrigações, deixar de lado nossos pensamentos e nos jogarmos na certeza de que a injustiça nos deixará livres e o que passou, passou! O futuro me aguarda e cheio de presentes!!!

O "jeitinho" brasileiro não nos permite planejar, pensar, realmente elaborar trabalhos que são elaborados, no final das contas dá-se um "jeitinho" e tudo se encaixa em seu lugar, mesmo que tenha que ser com esparadrapo, fita-crepe e super bonde.

O pior de tudo, as pessoas parecem gostar disso, principalmente os minutos de alívio após o "jeitinho" que talvez não estoure mais nas mãos delas.

Ainda bem que alguns se cansam de viver de "jeitinhos", mas infelizmente o seu poder é muito maior, não temos controle sobre ele, é parte da nossa cultura, infelizmente, e muitas vezes falamos dele com orgulho, mas desse do qual podemos ter orgulho não é o mesmo que todos os dias saem da lata do lixo... e assim a cultura continua, sem parar, sem calar...

quinta-feira, 10 de dezembro de 2009

Seam, Richfaces e Ajax: Conversação para ligar tudo isso.

Desde que comecei a utilizar o Seam sempre fiquei um tanto quanto decepcionado por não conseguir utilizar Ajax para fazer interações básicas com o usuário, tal como o usuário clica em um botão para adicionar algo (uma pessoa) a uma lista, abre-se então uma janela com uma listagem de pessoas que ele pode selecionar e através de um filtro ele pode digitar o nome desta pessoa para limitar o número de resultados. Ao encontrar a pessoa desejada o usuário a seleciona e esta é adicionada a uma lista do sistema que representa algo.

A figura acima mostra um caso típico deste tipo de interação. Este tipo de interação torna muito mais prático a utilização do sistema por parte do usuário, já que o tempo de resposta de cada requisição do usuário é menor.

A pouco tempo atrás comecei a escrever uma nova aplicação onde trabalho, criando um projeto war com o Seam 2.2.0.G.A, o que já me deixou bastante motivado, pois dá primeira vez que tentei criar um projeto desta forma não obtive sucesso, pois tentei utilizar os plugins do Eclipse para criar o projeto, sendo que este simplesmente não fazia deploy das alterações que eu realizava. Criei utilizando o seam-gen e depois importei o projeto no Eclipse e tudo fucionou como esperado, fazendo hot-deploy das classes, o que é um grande passo no desenvolvimento de aplicações para web utilizando Java.

Estava decidido que neste projeto iria utilizar Ajax de qualquer maneira. Após dois dias (infelizmente não trabalho só com desenvolvimento) testando diferentes maneiras de chamar meus componentes descobri o santo graal :D.

Mas vamos começar do começo, pois acredito que o Richfaces tenha problemas mesmo e que a forma como o utilizei apenas contornoram tal problema, mas até hoje não sei por quê. Entrei em contato com os desenvolvedores, mas até hoje não obtive resposta porque tecnicamente a aplicação não se comportava como eu esperava, sendo assim, gostaria de deixar o caminho que percorri, quem sabe alguma alma me explique por que não obtive sucesso inicialmente.

Bem, chega de enrolação e vamos para a prática. Criei um componente de listagem que estende a classe EntityQuery do framework Seam. Na minha janela de busca de pessoas liguei o "Nome" no "Filtro de Busca" ao nome de um campo que é utilizado no meu componente para restringir os resultados da pesquisa. Cada vez que uma letra era digitada na caixa de busca a listagem era re-renderizada. Isto funcionou muito bem, filtro feito!

O problema apareceu quando selecionei uma pessoa pela primeira vez. Quando renderizava a lista de pessoas, o link Selecionar chamava um método para adicionar a pessoa ao sistema passando o objeto que representava a pessoa como parâmetro. O problema é que na aplicação o parâmetro passado era simplesmente null. Já havia visto este problema, quando um colega estava brincando com uma solução parecida.

Comecei então a brincar com o escopo do meu componente. Quando coloquei ele no escopo de página o parâmetro começou a ser passado, porém quando utilizava o filtro e a listagem era re-renderizada o parâmetro que era passado era a referência para o objeto que representava a pessoa que estava na listagem logo que esta abria, ou seja, sem filtro nenhum.

Deve existir alguma explicação para este comportamento, porém não sei explicar qual é, apenas posso dizer que ao colocar a listagem no scope de conversação consegui resolver meus problemas, tudo funcionou perfeitamente, conseguia filtrar os resultados e clicar em um deles e o parâmetro passado a minha função era o objeto correspondente a pessoa a qual eu havia clicado, bingo! Ops, cedo demais para ficar tão feliz :(

Ao digitar muito rapidamente no filtro de busca o Seam gerava um erro: The conversation ended, timed out or was processing another reques. A solução foi bem simples, demorei apenas para encontrá-la: utilizar o componente Queue do Richfaces. Este faz com que as requisições Ajax sejam enfileiradas no browser e submetida apenas quando a requisição atual retornar. Isto evita que chegue uma requisição no servidor enquanto ele já está processando outra requisição durante uma conversação.

Só não fiquei satisfeito de colocar meu componente de listagem no escope de conversação por padrão. Para evitar isso criei um componente que cria e desinjeta tal componente no scope de conversação quando necessário.

Pronto, agora sou um programador feliz, posso utilizar Ajax na minha aplicação com Seam!

quarta-feira, 21 de outubro de 2009

GNOME 3.0: GNOME-Shell e GNOME Zeitgeist

O GNOME está se aproximando da sua versão 3.0. Os carros chefes para esta nova versão são dois softwares: GNOME-Shell e GNOME Zeitgest.

Ambos alteram a forma como se interage com o desktop e com os arquivos. O GNOME-Shell substitui o gerenciador de janelas, o painel e o lançador de aplicações atuais do GNOME, criando um ambiente desktop orientado a atividades com uma interface bem diferente da atual, afinal com este software o GNOME entra finalmente para era dos desktops 3D. Com certeza esta mudança trará muito estranheza para os usuários, porém um fato que gostei bastante foi que este lançamento não "quebrou" o desktop, pelo menos não por enquanto. Algumas distribuições simplesmente param de distribuir software antigo, por exemplo, na última versão do Kubuntu é difícil instalar algumas aplicações antigas do KDE, simplesmente porque a distribuição parou de empacotar as versões mais antigas de bibliotecas. Um grande prejuízo, principalmente no caso do KDE, onde muitas aplicações ainda não foram portadas para o KDE 4.

Alguns vídeos mostrando diversas funcionalidades do GNOME-Shell pode ser encontrado na página do projeto: http://live.gnome.org/GnomeShell/Screencasts

O GNOME Zeitgeist modifica como você vê os arquivos no computador adotando uma linha cronológica, ou seja, se você quiser ver quais arquivos acessou na semana passada é possível. Este é um software que ainda não testei, mas com certeza ainda teremos a possibilidade de ver nossos arquivos em forma de árvore, se não, ficarei muito desapontado, pois não acho que poder enxergar os arquivos apenas de forma cronológica seja a melhor solução.

O Zeitgeist também indexa páginas visitadas e outras fontes de informações. A ambição do projeto é interessante, mas não sei se irá vingar. Sempre que tentei utilizar indexadores, tipo tracker ou beagle, sempre achei que eles consumiam muito processamento e memória, além disso utilizava-os muito pouco. Sendo que as funcionalidades prometidas por tais software eu conseguia obter me organizando. Espero que o Zeitgeist me faça mudar de idéia, pois nada com um software diferente para dar um novo ar a uma mesma idéia!

sexta-feira, 4 de setembro de 2009

Mate o IE6

A algum tempo diversos sites passaram a apoiar a campanha "Mate o IE6". Basta você fazer uma busca por este tema no Google e vai encontrar diversas páginas.

Bem, porque estou fazendo este post? Acabei de olhar as estatística do blog e o IE6 subiu para o segundo lugar nos browser mais usados e provavelmente muito em breve será o primeiro, sendo assim, eu peço aos visitantes que passam por aqui: considerem atualizar seus browsers!

Existem apenas vantagens em se fazer isso: browsers mais seguros, modernos e compatíveis com padrões. Este último item pode não ser muito relevante para você, mas pode ter certeza, vai melhorar cada vez mais a vida dos programadores web.

Agora me diga uma coisa, quantos serviços web você usa hoje em dia? Pode ter certeza que esse número de serviços só vai aumentar, então faça um favor para os desenvolvedores poderem concentrar-se mais nos serviços e menos nos problemas do IE6: mate-o!

quinta-feira, 16 de julho de 2009

Formatar código-fonte para postagem em blogs

No meu último post sofri para formatar o código do meu bash script. Depois de algum tempo tentando deixá-lo com uma aparência razoável pensei que deveria ter alguma coisa pronta. Depois de procurar por algum tempo, encontrei: http://formatmysourcecode.blogspot.com/

Perfeito!!!

Trabalho repetitivo? Bash neles :P

Tenho uma base de dados que contém dados de trabalhos enviados a um evento. Além disso, possuo em um diretório os trabalhos em .rtf.

O que preciso fazer? Converter cada um dos trabalhos para .pdf e inserir no cabeçalho uma imagem do evento com seu ISSN. Além disso, a partir da área, título do trabalho, palavras-chave e autores, preciso gerar uma base de dados em um formato criado por nós para fazer buscas através de javascript nos anais do evento. Para isso, fiz um script que cuida dessas duas coisas.

Como fazia tempo que não mexia com bash script, estava meio enferrujado, mas nada que uma pesquisa no Google não ajude.

Consultei os dados do banco usando SQL e exportei o resultado para um arquivo CSV delimitado por TABs.

Aproveitei a estrutura para ler arquivos apresentadas por: http://bash.cyberciti.biz/file-management/read-a-file-line-by-line/

Além disso, para padronizar o nome dos arquivos, converti todas as informações para maiúsculo, sendo que em alguns lugares precisei retirar o acento das palavras, mas mais uma vez tive uma ajudinha: http://ubuntuforums.org/showthread.php?p=6641997

Porém a conversão das letras com acento não funcionou. Poderia ter aberto o arquivo no OpenOffice e convertido, porém acreditei que existiria um jeito de fazer isso no terminal e depois de muito procurar encontrei a solução: http://wjd.nu/notes/2009. Também vou transcrevê-la para não perdé-la, pois o site não dá nenhuma link direto a ela:

2009-01-26 - unicodeencodeerror / python redirect pipe

Python has excellent Unicode support. Most of the time it just works. However, when you want to redirect non-ASCII characters to a different program through shell redirection, you will run into a UnicodeEncodeError.
See the following example:
$ locale | grep LC_CTYPE
LC_CTYPE="en_US.UTF-8"
$ python -c 'print "\u20ac 3.50"'
\u20ac 3.50
$ python -c 'print u"\u20ac 3.50"'
€ 3.50
$ python -c 'print u"\u20ac 3.50"' | cat
Traceback (most recent call last):
File "", line 1, in 
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
Python calls something like isatty(3) ("is a TTY?") on the standard out file descriptor, and when that returns 0 (false) it forces stdout to use the ascii codec. This can be quite annoying if you want to use other shell tools (awk(1), grep(1), sed(1), tr(1) etc..) to quickly search through or modify the output stream.
The solution is to replace the stdout object with one that doesn't assume that a non-terminal only likes ASCII. Add the following to your python code:
import codecs, locale, sys
sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, 'replace')
It's not real pretty, but it works :-)
$ python -c 'import codecs, locale, sys
> sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace")
> print u"\u20ac 3.50"' | cat
€ 3.50

Escrevi/copiei uma macro n{d}o OpenOffice para converter os arquivo .rtf para .pdf e adicionar o cabeçalho com as informações do evento (esta parte me tomou bastante tempo, pois apesar de existir bastante informação de como modificar o cabeçalho e como inserir uma imagem através de macros, esta parte não funcionou no OOo 3.0 disponibilizado pelo GSB para o Slackware, quer dizer, o programa travava 99,9% das vezes e até eu perceber isso, achei que estava fazendo alguma coisa errada. No OOo 2.4 também disponiblizado pelo GSB o programa trava 0.01%. Ainda vou tentar descobrir porque!): http://www.oooforum.org/forum/viewtopic.phtml?t=3772

Sub test( cArg )
Print "|"+cArg+"|"
End Sub

Sub ConvertWordToPDF( cFile )
cURL = ConvertToURL( cFile )

' Open the document.
' Just blindly assume that the document is of a type that OOo will
' correctly recognize and open -- without specifying an import filter.

oDoc = StarDesktop.loadComponentFromURL( cURL, "_blank", 0, Array(MakePropertyValue( "Hidden", True )))

oGraph = oDoc.createInstance("com.sun.star.text.GraphicObject")
With oGraph
.GraphicURL = "file:///home/cerodrigues/kadu.jpg"
.AnchorType = com.sun.star.text.TextContentAnchorType.AS_CHARACTER
.Width = 6000
.Height = 6000
End With

oEstPagina = oDoc.StyleFamilies.getByName("PageStyles")
oPagPadrao = oEstPagina("Default")
oPagPadrao.HeaderIsOn = True
oTxtCabecalho = oPagPadrao.HeaderText
oCursorCabecalho = oTxtCabecalho.createTextCursor()
oTxtCabecalho.insertTextContent(oCursorCabecalho, oGraph, False)
'oText.insertTextContent( oCursor, oGraph, False )



cFile = Left( cFile, Len( cFile ) - 4 ) + ".pdf"
cURL = ConvertToURL( cFile )

' Save the document using a filter.
oDoc.storeToURL( cURL, Array(MakePropertyValue( "FilterName", "writer_pdf_Export" ),)

oDoc.close( True )
End Sub


Function MakePropertyValue( Optional cName As String, Optional uValue ) As com.sun.star.beans.PropertyValue
Dim oPropertyValue As New com.sun.star.beans.PropertyValue
If Not IsMissing( cName ) Then
oPropertyValue.Name = cName
EndIf
If Not IsMissing( uValue ) Then
oPropertyValue.Value = uValue
EndIf
MakePropertyValue() = oPropertyValue
End Function

Por fim, o script final ficou assim:


#!/bin/bash

# Código utilizado para consulta através do JS
# "área^diretório onde está o artigo/nome do arquivo sem extensão*título do arquivo|palavras chaves separadas por vírgula (final),$autor1@autor2@~"

rm sucesso.txt > /dev/null 2>&1
rm fracasso.txt > /dev/null 2>&1

COD_PROCESSADO=0
AREA_PROCESSADO=""
FCOUNT=0
AREA=""
NOVO="y"

FILE="./list_trabalhos_com_oral.csv"

toUpper() {
echo -n $1 | python -c 'import sys, locale, codecs; sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace"); print( sys.stdin.read().decode("utf8").upper() )'
}

# Coloca a descrição da forma de apresentação em minúsculo, tira os acentos, remove o início do texto "Sessão de " e substitui os espaços por _ e remove qualquer caracter de controle
processFrmApr() {
echo -n $1 | python -c 'import sys, locale, codecs; sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace"); print( sys.stdin.read().decode("utf8").lower() )' | unaccent utf-8 | sed 's,sessao de ,,' | sed 's, ,_,g' | sed 's,[[:cntrl:]],,g'
}

# Coloca a descrição da área de conhecimento em minúsculo, tira os acentos e substitui os espaços por _ e remove qualquer caracter de controle
processArCnh() {
echo -n $1 | python -c 'import sys, locale, codecs; sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace"); print( sys.stdin.read().decode("utf8").lower() )' | unaccent utf-8 | sed 's, ,_,g' | sed 's,[[:cntrl:]],,g'
}

# Descrição dos campos em FIELDS
# FIELDS[1] = Código do Banco de Dados
# FIELDS[2] = Título do Trabalho
# FIELDS[3] = Nome do Arquivo
# FIELDS[4] = Palavras Chaves separadas por ;
# FIELDS[5] = Autor
# FIELDS[6] = Área de conhecimento
# FIELDS[7] = Tipo do Trabalho

processLine () {

  for i in `seq 1 7`;
  do
      FIELDS[$i]=$(toUpper `echo $line | awk -F'\t' '{print $'$i'}'`)
  done

  # Gera o PDF dos arquivos submetidos e o arquivo para ser utilizado na busca pelo javascript
  # Se mudou a área que está sendo processada deve-se criar o arquivo que contêm os dados da área
  echo ${FIELDS[6]}
  if [ "x${FIELDS[6]}" != "x$AREA_PROCESSADO" ]; then
      AREA_PROCESSADO=${FIELDS[6]}
      # Termina o arquivo antigo
      echo -n "~" >> $AREA.txt
      AREA=$(processArCnh ${FIELDS[6]})
      rm $AREA.txt > /dev/null 2>&1
      touch $AREA.txt
      NOVO="y"
  fi
  echo $AREA_antiga

  # Se mudar o código é um novo trabalho, então faz a conversão para PDF e insere o cabeçalho do evento
  # Se mudou o código do trabalho, finaliza esta entrada no texto utilizado pelo javascript e inicia a nova
  if [ ${FIELDS[1]} != $COD_PROCESSADO ]; then
      if [ $COD_PROCESSADO != 0 -a "x$NOVO" != "xy" ]; then
          # Termina entrada apenas se não é a primeira mudança, pois o conteúdo anterior era vazio.
          echo -n "~" >> $AREA.txt
      fi
      COD_PROCESSADO=${FIELDS[1]}
      NOVO="x"

      echo "Convertendo arquivo: "${FIELDS[3]}

      # Cria pasta de comunicação oral ou painél se não existir
      FIELDS[7]=$(processFrmApr ${FIELDS[7]})
      echo -n ${FIELDS[7]}
      if [ ! -d ${FIELDS[7]} ]; then
          mkdir ${FIELDS[7]}
      fi

      # Cria a pasta da área de conhecimento do trabalho se não existir
      FIELDS[6]=$(processArCnh ${FIELDS[6]})
      if [ ! -d ${FIELDS[7]}/${FIELDS[6]} ]; then
          mkdir ${FIELDS[7]}/${FIELDS[6]}
      fi

      # Converte o arquivo para pdf e inclui o cabeçalho do SEU
      contador=1
      FCOUNT=`expr $FCOUNT + 1`
      while [ $contador -le 5 ]; do
          if [ -f upload2009/${FIELDS[3]}.pdf ]; then
              echo "Não é necessário converter"
              echo "Copiando" ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
              cp ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
              break
          fi

          if [ -f upload2009/${FIELDS[3]}.rtf ]; then
              echo "Iniciando conversão"
              /usr/lib/ooo-2.4/program/soffice.bin -invisible -headless "macro:///rtf2pdf.Conversion.ConvertWordToPDF(`pwd`/upload2009/${FIELDS[3]}.rtf)"
              # Arquivo convertido com sucesso
              if [ $? == 0 ]; then
                  echo "Copiando" ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  cp ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  break
              fi
          else
              echo "Não é necessário converter"
              if [ -f upload2009/${FIELDS[3]}.pdf ]; then
                  echo "Copiando" ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  cp ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  break
              fi
          fi
          contador=`expr $contador + 1`
      done

      if [ $contador -le 5 ]; then
          echo ${FIELDS[2]} convertido com sucesso >> sucesso.txt
      else
          echo ${FIELDS[2]} não convertido >> fracasso.txt
      fi
   
      # Insere no arquivo utilizado pelo javascript os dados do trabalho
      # Insere a área, o diretório onde está o artigo, o nome do artigo sem extensão e o título do artigo
      echo -n $AREA_PROCESSADO"^"${FIELDS[7]}"/"${FIELDS[6]}"/"$FCOUNT"*"${FIELDS[2]}"|" >> $AREA.txt
   
      # Insere as palavras chaves
      for i in `seq 1 3`;
      do
          PC[$i]=$(toUpper `echo ${FIELDS[4]} | awk -F';' '{print $'$i'}'`)
          echo -n ${PC[$i]}"," >> $AREA.txt
      done
      echo -n "$" >> $AREA.txt
   
      # Insere o primeiro autor
      echo -n ${FIELDS[5]}"@" >> $AREA.txt
  # Insere os demais autores
  else
      echo -n ${FIELDS[5]}"@" >> $AREA.txt
  fi
}

BAKIFS=$IFS
IFS=$(echo -en "\n\b")
exec 3<&0
exec 0<$FILE
while read line
do
  # use $line variable to process line in processLine() function
  processLine $line
done
exec 0<&3

# restore $IFS which was used to determine what the field separators are
IFS=$BAKIFS
exit 0


Muito legal esse monstrengo!!!

terça-feira, 30 de junho de 2009

Extrair o áudio de um vídeo no formato MP3

A linha de comando é mesmo um canivete suíço.

mencoder file.wmv -of rawaudio -oac mp3lame -ovc copy -o file.mp3


É como Master Foo disse uma vez: "There is more Unix-nature in one line of shell script than there is in ten thousands lines of C".

O google também é ótimo... achei essa linha em: http://knowledge.drun.net/viewtopic.php?id=174