La solución no es sencilla, porque se trata de una estructura tridimensional, de la que tenemos que modificar unos campos, y luego sacarlos de una determinada manera, y en un determinado orden. Y la sintaxis de Perl se complica a medida de que las estructuras se vuelven más complejas.
Esta es la primera solución, sin usar módulos:
Using perl Syntax Highlighting
#!/usr/bin/perl
use v5.14; # activa programación estricta, salida de advertencias y nuevas características
use autodie; # «Mejor morir que regresar con deshonor» --Proverbio Kinglon
####################################################################################
## Constantes
####################################################################################
my %medidas = (
cpu => {
caption => "Uso de la CPU",
xaxisname => "tiempo",
yaxisname => "CPU",
datos => [qw(
cpu_ready_summation
cpu_usage_average
)],
},
mem => {
caption => "Uso de la memoria",
xaxisname => "tiempo",
yaxisname => "Memoria",
datos => [qw(
mem_active_average
mem_consumed_average
mem_granted_average
mem_vmmemctl_average
)],
},
io => {
caption => "Uso de la E/S",
xaxisname => "tiempo",
yaxisname => "E/S",
datos => [qw(
datastore_totalreadlatency_average
datastore_totalwritelatency_average
)],
},
);
####################################################################################
## Argumentos
####################################################################################
@ARGV == 1 or die "Uso: $0 <archivo CSV a procesar>\n";
my $archivo_csv = shift;
####################################################################################
## Lectura del archivo
####################################################################################
my %log; # Almacena toda la información
my @campos; # Nombre de los campos, en el orden en que aparecen
my %campos; # Datos de cada línea
open my $INPUT, '<', $archivo_csv;
while (<$INPUT>) {
chomp;
s/"//g;
my @datos = split /;/; # Separo los campos
if ($. == 1) { # Si es la primera línea
@campos = @datos; # Leemos los nombres de los campos
}
else {
@campos{@campos} = @datos; # %campos <- @datos
my($dia, $mes, $year, $hora, $minuto, $segundo) = $campos{'Timestamp'} =~ /(\d+)/g;
$campos{'Timestamp'} = sprintf "%02d%02d%02dT%02d%02d%02d", $year, $mes, $dia, $hora, $minuto, $segundo;
$campos{'MetricId'} =~ s/[.]/_/g;
$log{ $campos{'Entity'} }->{ $campos{'MetricId'} }->{ $campos{'Timestamp'} } = $campos{'Value'};
}
}
close $INPUT;
#use Data::Dumper;
#say Dumper \%log; # comprobar que se genera bien la estructura
####################################################################################
## Salida
####################################################################################
for my $maquina (keys %log) { # por cada máquina
say $maquina;
for my $medida (keys %medidas) { # por cada medida
say "\t$medida";
open my $SALIDA, '>', "${maquina}_$medida.xml";
say $SALIDA
'<',
join(' ',
'chart',
'showvalue="0"',
qq(caption="$medidas{$medida}->{caption}"),
qq(xaxisname="$medidas{$medida}->{xaxisname}"),
qq(yaxisname="$medidas{$medida}->{yaxisname}"),
'palette="1"',
),
'>'
;
# fechas
say $SALIDA ' <categories>';
my @fechas = sort keys %{ $log{$maquina}->{ ${$medidas{$medida}->{datos}}[0] } };
for my $fecha (@fechas) {
say $SALIDA qq( <category label="$fecha" />);
}
say $SALIDA ' </categories>';
# por cada magnitud
for my $item (@{ $medidas{$medida}->{datos}}) {
say "\t\t$item";
say $SALIDA qq( <dataset SeriesName="$item">);
for my $fecha (@fechas) {
say $SALIDA qq( <set value="$log{$maquina}->{$item}->{$fecha}" />);
}
say $SALIDA qq( </dataset>);
}
say $SALIDA '</chart>';
close $SALIDA;
}
}
__END__
Coloreado en 0.007 segundos, usando
GeSHi 1.0.8.4
Lo único reseñable es que usamos los campos de la primera línea del CSV (línea 62) para construir un
hash (línea 65), y de esa manera, podemos acceder a los datos de las siguientes líneas sin tener que acordarnos que el 'Value' está en la posición 0, o el 'Timestamp' en el 1. Además, si en el futuro cambia de formato el CSV, el programa seguirá funcionando, aunque los campos cambien de posición.
Esta es otra solución, usando módulos y un poco más de Perl elaborado. Queda un poco más corta, claro.
Using perl Syntax Highlighting
#!/usr/bin/perl
use v5.14;
use autodie; # «Mejor morir que regresar con deshonor» --Proverbio Kinglon
use Text::CSV::Slurp;
use XML::Simple;
####################################################################################
## Constantes
####################################################################################
my %medidas = (
cpu => {
caption => "Uso de la CPU",
xaxisname => "tiempo",
yaxisname => "CPU",
datos => [qw(
cpu_ready_summation
cpu_usage_average
)],
},
mem => {
caption => "Uso de la memoria",
xaxisname => "tiempo",
yaxisname => "Memoria",
datos => [qw(
mem_active_average
mem_consumed_average
mem_granted_average
mem_vmmemctl_average
)],
},
io => {
caption => "Uso de la E/S",
xaxisname => "tiempo",
yaxisname => "E/S",
datos => [qw(
datastore_totalreadlatency_average
datastore_totalwritelatency_average
)],
},
);
####################################################################################
## Argumentos
####################################################################################
@ARGV == 1 or die "Uso: $0 <archivo CSV a procesar>\n";
my $archivo_csv = shift;
####################################################################################
## Lectura del archivo
####################################################################################
my $datos = Text::CSV::Slurp->load(file => $archivo_csv, sep_char => ';', quote_char => '"');
my %log;
for my $item ( @$datos ) {
my($dia, $mes, $year, $hora, $minuto, $segundo) = $item->{Timestamp} =~ /(\d+)/g;
$item->{Timestamp} = sprintf "%02d%02d%02dT%02d%02d%02d", $year, $mes, $dia, $hora, $minuto, $segundo;
$item->{MetricId} =~ s/[.]/_/g;
$log { $item->{Entity } }
->{ $item->{MetricId } }
->{ $item->{Timestamp} }
= $item->{Value };
}
####################################################################################
## Salida
####################################################################################
for my $maquina (keys %log) { # por cada máquina
say $maquina;
for my $medida (keys %medidas) { # por cada medida
say "\t$medida";
# la raíz de todos los males
my $xml = {
chart => {
showvalue => 0,
caption => $medidas{$medida}->{caption },
xaxisname => $medidas{$medida}->{xaxisname},
yaxisname => $medidas{$medida}->{yaxisname},
palette => 1,
}
};
# fechas
my @fechas = sort keys %{ $log{$maquina}->{ ${$medidas{$medida}->{datos}}[0] } };
$xml->{chart}->{categories}->{category} = [ map { { label => $_ } } @fechas ];
# por cada magnitud
for my $item (@{ $medidas{$medida}->{datos}}) {
say "\t\t$item";
# sacamos el conjunto de datos, para esas fechas
my @set = map { { value => $log{$maquina}->{$item}->{$_} } } @fechas;
push @{ $xml->{chart}->{dataset} }, {
SeriesName => $item,
set => [ @set ],
};
}
open my $SALIDA, '>', "${maquina}_$medida.xml";
print $SALIDA XMLout($xml, KeepRoot => 1);
close $SALIDA;
}
}
__END__
Coloreado en 0.003 segundos, usando
GeSHi 1.0.8.4
Bueno, son solo veinte líneas menos, pero queda un poco más claro, y más fácil de mantener en el futuro.
La salida son los nueve archivos, con este aspecto:
Using xml Syntax Highlighting
<chart caption="Uso de la CPU" palette="1" showvalue="0" xaxisname="tiempo" yaxisname="CPU">
<categories>
<category label="20121009T000000" />
<category label="20121009T020000" />
<category label="20121009T040000" />
<category label="20121009T060000" />
<category label="20121009T080000" />
<category label="20121009T100000" />
<category label="20121009T120000" />
<category label="20121009T140000" />
<category label="20121009T160000" />
<category label="20121009T180000" />
</categories>
<dataset SeriesName="cpu_ready_summation">
<set value="5557" />
<set value="5468" />
<set value="5243" />
<set value="5765" />
<set value="6066" />
<set value="6227" />
<set value="6385" />
<set value="5928" />
<set value="5893" />
<set value="5521" />
</dataset>
<dataset SeriesName="cpu_usage_average">
<set value="1,51" />
<set value="1,48" />
<set value="1,52" />
<set value="1,49" />
<set value="1,59" />
<set value="1,67" />
<set value="1,66" />
<set value="1,58" />
<set value="1,52" />
<set value="1,56" />
</dataset>
</chart>
Coloreado en 0.001 segundos, usando
GeSHi 1.0.8.4