XML em Perl, parte 3

xml_perl_3_code

Na primeira parte foi feita a instalação do módulo Simple-XML (que é uma informação útil e serve para qualquer outro módulo do Perl que precise ser instalado), já na egunda foi explicado um pouco sobre as estruturas de dados do Perl para se poder entender melhor como o módulo funciona e assim começar a trabalhar com o que foi importado do XML.

Agora  é a conclusão, com a gravação de arquivo XML a partir do próprio Perl e de brinde uma pequena aplicação utilizando todos este conhecimento.

Salvando um arquivo XML

Só para relembrar como está agora o agenda.xml, o nosso XML de exemplo:

<?xml version='1.0'?>
<agenda>
 <entry>
  <name>Fulano</name>
   <full>Fulano da Silva</full>
   <prefix>Sr.</prefix>
   <email>fulano@example.com</email>
   <phone>1234-5678</phone>
   <phone>1234-8765</phone>
   <address>Avenida Projetada, 700 sala A</address>
  </entry>
  <entry>
   <name>Sicrano</name>
   <full>Sicrano de Souza</full>
   <prefix>Dr.</prefix>
   <email>doutorsicrano@example.com</email>
   <address>Travessa A, 123</address>
   <phone>8765-4321</phone>
  </entry>
</agenda>

Hora de acrescentar registros nele. Mas como? Basta lembrar que todo o XML agora é uma estrutura de dados dentro Perl, logo basta acrescentar normalmente os itens ao hash, com no programa agenda4.pl:

#!/usr/bin/perl
use XML::Simple;
my $xml=new XML::Simple;
my $data=$xml->XMLin("agenda.xml");
# esto inserindo diretamente um hash por comodidade
# mas poderia fazer individualmente.
$data->{entry}{Beltrano}=
    {
        'full' => 'Beltrano Aparecido',
        'prefix' => 'Sr.',
        'address' => 'Largo da Matriz, 57 - Casa 1',
        'email' => 'beltrano@exemplo.com.br',
        'phone' => '9999-9999'
    };
print $xml->XMLout($data);
exit 0;

Ao ser executar o programa exibirá um arquivo XML na tela:

<opt>
  <entry name="Beltrano" address="Largo da Matriz, 57 - Casa 1"
   email="beltrano@exemplo.com.br" full="Beltrano Aparecido"
   phone="9999-9999" prefix="Sr." />
  <entry name="Fulano" address="Avenida Projetada, 700 sala A"
   email="fulano@examplo.com.br" full="Fulano da Silva" prefix="Sr.">
    <phone>1234-5678</phone>
    <phone>1234-8765</phone>
  </entry>
  <entry name="Sicrano" address="Travessa A, 123"
   email="doutorsicrano@examplo.com.br" full="Sicrano de Souza"
   phone="8765-4321" prefix="Dr." />
</opt>

Mas este não é o XML original!

Sim, ao menos não em sua forma original mas repare que ele apresenta as mesmas informações (o mesmo conteúdo). Tanto que funcionará normalmente com os demais programas de exemplo. Em nenhum momento explicamos ao XML::Simple como era o XML.

Vale ressaltar que a função XMLout() retorna um arquivo XML a partir do hash repassado a ele. É você quem decide o que fazer com este resultado. Você pode jogá-lo para a saída padrão (STDOUT), como no exemplo, armazená-lo em outra variável:

(...)
$data2=$xml->XMLout($data);
(...)

Ou mesmo salvá-lo como um novo arquivo XML (ou até sobrescrever o XML original):

(...)
open($fh,'>agenda2.xml');
XMLout($data, OutputFile => $fh);
close($fh);
(...)

A documentação do XML::Simple contém uma série de informações adicionais sobre opções que dão maior controle sobre a estrutura do hash e do XML gerados pelo módulo. Também é bom lembrar que o XML::Simple não é o único jeito de se trabalhar com XML em Perl, há outros módulos disponíveis como, por exemplo, o XML::MyXML e o XML::Smart, que possuem mais recursos, melhores opções de controle do XML lido/produzido e, consequentemente, maior complexidade de uso — razão pela qual preferi ficar apenas com o XML::Simple.

Exemplo da “vida real”

Agora um exemplo bem simples, prático e da “vida real”. Eu tenho um iPod Nano de 6ª geração e além de utilizá-lo para ouvir música faço uso do pedômetro interno para registrar minhas caminhadas (e quem sabe, um dia, minhas corridas). Cada exercício registrado no aparelho produz o arquivo lastWorkout.xml com a seguinte estrutura:

<runSummary>
  <workoutName>Exercício de Step</workoutName>
  <time>2015-05-10T12:31:50+00:00</time>
  <duration>688193</duration>
  <durationString>11'28"</durationString>
  <distance unit="km">0.8859</distance>
  <distanceString>0,89 km</distanceString>
  <paceRaw>776860</paceRaw>
  <pace>12'57" / km</pace>
  <calories>52</calories>
  <stepCounts>
    <walkBegin>0</walkBegin>
    <walkEnd>1085</walkEnd>
    <runBegin>0</runBegin>
    <runEnd>0</runEnd>
  </stepCounts>
</runSummary>>

Tudo bem, sei que uma caminhada de 890 metros não deveria ser considerada exercício, mas era o conteúdo no último registro no aparelho quando comecei a escrever o texto. Até acredito que outros modelos de iPod e/ou iPhone (não imagino ninguém correndo com um iPad na cintura) que tenham um recurso similar gerem um arquivo igual, ou muito próximo, deste.

Curiosidade, o iPod Nano (ao menos o modelo de 6ª geração) não usa iOS e sim uma versão do firmware dos iPod originais visualmente adaptada para se parecer com o iOS.

A cada nova sessão o aparelho pega o último lastWorkout.xml, e cria dentro do diretório ./latest um novo arquivo chamado “yyyy-mm-dd hh;mm;ss.xml” (usando a data e hora está disponível dentro da tag <time>) que contém além do XML original uma série de novas informações obtidas com o processamento arquivo e de outras fonte — veja que o lastWorkout.xml está inserido neste trecho:

<sportsData type="pedometer">
  <vers>10</vers>
  <runSummary>
    <workoutName>Exercício de Step</workoutName>
    <time>2015-05-05T10:39:08+00:00</time>
    <duration>214886</duration>
    <durationString>3'35"</durationString>
    <distance unit="km">0.3329</distance>
    <distanceString>0,33 km</distanceString>
    <paceRaw>645444</paceRaw>
    <pace>10'45" / km</pace>
    <calories>19</calories>
    <stepCounts>
      <walkBegin>0</walkBegin>
      <walkEnd>392</walkEnd>
      <runBegin>0</runBegin>
      <runEnd>0</runEnd>
    </stepCounts>
  </runSummary>
  <userInfo>
    <weight>80.0</weight>
    <device></device>
    <hrsID>00000069</hrsID>
  </userInfo>
  <ipodInfo>
    <model>MC688LL</model>
    <softwareVersion>1.2 (36B10147)</softwareVersion>
    (...)
</sportsData>

Não irei incluir todas as informações do arquivo mas o segue são informações de configuração, preferências do usuário e, claro, chaves X.509 que autenticam a veracidade de tudo que está registrado. Motivo? Este arquivo pode ser utilizado para alimentar, via iTunes, o histórico do Nike+ Running e exercícios são coisa séria e ninguém gosta quando de pessoas que mentem sobre o assunto. 🙂

Modelando o programa

Algo bem simples, tendo como base os arquivos XML originais do iPod — colocados anteriormente dentro do diretório ./data, e implementando as seguintes funcionalidades:

  1. Exibição de um “Histórico de exercícios” contendo: maior distância, distância total, tempo total, calorias consumidas, número de passos e o comprimento médio da passada (já explico este cara) — basicamente a mesma informação exibida no aparelho;
  2. Sumarização de todos os arquivos XML do iPod (colocados em um diretório específico) e produzindo um novo arquivo XML contendo os dados que serão usados pelo histórico — para não precisar calcular a todo instante e
  3. Cálculo do comprimento médio de uma passada (no caso específico do iPod só a partir da versão 1.2 do firmware que ele passou fazer o registro da distância percorrida) esta opção permite estimar as distâncias nos registros antigos.

Não serão usadas todas as informações dos arquivos XML, o objetivo é demonstrar justamente a importação de um arquivo XML, a consulta das informações contidas nele, o uso destas para a criação de novas informações e o armazenamento destes dados em um outro arquivo XML.

O programa

Chamei o programa carinhosamente de ped e o disponibilizei na área do blog da minha conta do GitHub, basta baixar os três arquivos dele — ped.pl, ped.phtml e ped.ptxt — e se precisar de algumas amostas pegue o sample.sh.

O uso é bem simples:

$ ./ped 
ped - sumariza os arquivos XML produzidos pelo programa Fitness++ dos 
iPod Nano 6th generation.
Utilize:
  atualizar - atualiza o histórico de exercícios.
  exibir - exibe o histórico de exercícios
  exibir html - para o histórico em HTML.
  passadas - para calcular a passada média.

Para visualizar o histórico de exercícios bastaria digitar:

$ ./ped exibir
Sem histórico registrado, utilize 'atualizar' primeiro.

Mas como não existe ainda informação alguma consolidada dentro de ped.xml (aliás, ele nem existe ainda) não será apresentado um resultado. Primeiro, deve-se atualizar os dados e o histórico estará disponível:

$ ./ped exibir
HISTÓRICO DE EXERCÍCIOS
Exercícios       : 200
Maior distância  : 19.06 km
Distância total  : 409.16 km
Tempo total      : 169h 36m 11.26s
Calorias         : 35924
Número de passos : 733021
  Caminhando     : 733021
  Correndo       : 0
  Passadas       : ~ m

Repare que falta algo, a informação sobre o comprimento das passadas precisa ser calculada:

$ ./ped passadas
Utilizado(s) 98 registro(s) para o cálculo.
Um passo equivale a 0.83171 metro(s).

Com a passada calculada, atualize as informações e exiba um histórico de exercícios digno de se tornar público:

$ ./ped exibir
HISTÓRICO DE EXERCÍCIOS
Exercícios       : 200
Maior distância  : 19.06 km
Distância total  : 616.94 km
Tempo total      : 169h 36m 11.26s
Calorias         : 35924
Número de passos : 733021
  Caminhando     : 733021
  Correndo       : 0
  Passadas       : ~0.83171 m

Mas é claro que esta saída em texto não é visualmente atraente. Também acrescentei uma saída em HTML que se utiliza o Bootstrap para cuidar do CSS e das fontes do Google para melhorar o visual, assim só precisei criar um único arquivo. Para gerar uma saída HTML basta usar:

$ FILE=/tmp/ped_$( date +%G_%m_%d ).html ;\
  ./ped exibir html > $FILE ;\
  webbrowser-app $FILE

Você pode substituir o webbrowser-app por qualquer outro navegador.

O resultado final é este aqui:

ped_webapp

E já que o Bootstrap está cuidando de toda a parte chata,  ele também deixou tudo quase pronto para dispositivos móveis (daí eu recorri a um servidor HTTP):

ped_mobile

Para que o programa não ficasse muito longo eu removi algumas eventuais verificações e também não me preocupei com registros em unidades diferentes de quilômetros (o XML “diz” que há a possibilidade mas preferi não me aprofundar).

Tem bastante coisa interessante no código, por exemplo, o uso de templates tanto para a saída no console quanto em HTML. Aliás, ambas as saídas produzidas são bastante simples — falo de conteúdo e não de forma — e poderiam ser (bastante) melhoradas com a inclusão da subdivisão das informações em ano, mês e dia, produção gráficos etc. As informações estão disponíveis em XML e basta trabalhá-las corretamente.

Finalizando

E uma última dica antes de encerrar a série, os arquivos XML tem algumas regras quanto aos nomes dos elementos que o XML::Simple “simplesmente” ignora, portanto é bom saber quais são antes de sair criando arquivos inválidos por aí.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s