quinta-feira, 16 de julho de 2009

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!!!