package Lire::Report; use strict; use Carp; use POSIX qw/ strftime /; use Lire::DataTypes qw/ check_superservice check_number /; use Lire::I18N qw/ set_fh_encoding /; use Lire::Utils qw/ xml_encode check_param check_object_param /; =pod =head1 NAME Lire::Report - Interface to a Lire report. =head1 SYNOPSIS use Lire::ReportParser::ReportBuilder; my $parser = new Lire::ReportParser::ReportBuilder; my $report = $parser->parse( "report.xml" ); print $report->superservice(), " report\n"; print "Generated on ", scalar( localtime( $report->date() ) ), "\n"; print "Timespan starts on ", scalar( localtime( $report->timespan_start() ) ), "\n"; print "Timespan ends on ", scalar( localtime( $report->timespan_start() ) ), "\n"; foreach my $s ( $report->sections() ) { print "Section: '", $s->title(), "' has ", scalar $s->subreports(), " subreports in it\n"; } =head1 DESCRIPTION This module offers an API to the report generated by Lire. The Lire::ReportParser::ReportBuilder(3pm) can be used to create a Lire::Report object from an XML file that complies with the Lire Report Markup Language DTD. =head1 CONSTRUCTOR A Lire::Report object can be built from an XML report file, from the new() method or from a Lire::ReportConfig object. =head2 new( [ $timespan_period ], [ $timespan_start ], [ $timespan_end ] ) Creates a new Lire::Report. The $timespan_period, $timespan_start and $timespan_end parameters initialize the attributes of the same name. =cut sub new { my ( $class, $period, $start, $end ) = @_; my $self = bless { '_version' => "2.1", '_generator' => __PACKAGE__ . "(3pm)", '_date' => time, '_start' => undef, '_end' => undef, '_period' => undef, '_sections' => [], }, $class; $self->timespan_period( $period ) if defined $period; $self->timespan_start( $start ) if defined $start; $self->timespan_end( $end ) if defined $end; return $self; } =pod =head1 OBJECT METHODS =head2 version() Returns the version number of the report format which was used in the external representation of this report. The current version is 2.1. =cut sub version { return $_[0]{'_version'}; } =pod =head2 generator([$generator) Returns the generator string that will be outputted in comments when the write_report() method is used. One can change that value by passing a new value through the $generator parameter. =cut sub generator { $_[0]{'_generator'} = $_[1] if defined $_[1]; return $_[0]{'_generator'}; } =pod =head2 date( [ $new_date ] ) Returns (and optionanly changes) the date in seconds since epoch on which this report was generated. =cut sub date { my ( $self, $date ) = @_; if ( @_ == 2 ) { check_param( $date, 'date', \&check_number, "'date' parameter should be a number of seconds since the epoch" ); $self->{'_date'} = $date; } return $self->{'_date'}; } =pod =head2 timespan_period( [ $new_period ] ) Returns (and optionnally changes) the period of the report. The period can be 'hourly', 'daily', 'weekly', 'monthly' or 'yearly'. When the period is undef, it is an arbitrary period. =cut sub timespan_period { my ( $self, $period ) = @_; if ( @_ == 2 ) { check_param( $period, 'period', qr/^(hourly|daily|weekly|monthly|yearly)$/, "'period' parameter should be one of 'hourly', 'daily', 'weekly', 'monthly' or 'yearly'" ) if defined $period; $self->{'_period'} = $period; } return $self->{'_period'}; } =pod =head2 timespan_start( [ $new_start ] ) Returns (and optionnally changes) the start of the timespan covered by this report in seconds since epoch. =cut sub timespan_start { my ( $self, $start ) = @_; if ( @_ == 2 ) { if ( $start ) { check_param( $start, 'start', \&check_number, "'start' parameter should be a number of seconds since the epoch" ); croak "'start' parameter is greater than timespan_end()" if $self->{'_end'} && $start > $self->{'_end'}; } $self->{'_start'} = $start ? $start : undef; } return $self->{'_start'}; } =pod =head2 timespan_end( [ $new_end ] ) Returns (and optionnally changes) the end of the timespan covered by this report in seconds since epoch. =cut sub timespan_end { my ( $self, $end ) = @_; if ( @_ == 2 ) { if ( $end ) { check_param( $end, 'end', \&check_number, "'end' parameter should be a number of seconds since the epoch" ); croak "'end' parameter is smaller than timespan_start()" if $self->{'_start'} && $end < $self->{'_start'}; } $self->{'_end'} = $end ? $end : undef; } return $self->{'_end'}; } =pod =head2 title( [$title] ) Returns the report's title, if it has one. If the $title is parameter is set, the report's title will be set to this new value. =cut sub title { $_[0]{'title'} = $_[1] if @_ == 2; return $_[0]{'title'}; } =pod =pod =head2 description( [$new_desc] ) Returns the report description. This description is encoded in DocBook XML. If the $description parameter is set, this method will set the description to this new value. If the $description parameter is undef, that description will be removed. =cut sub description { $_[0]->{'description'} = $_[1] if @_ == 2; return $_[0]->{'description'}; } =pod =head2 sections( ) Returns the report's sections as an array. This will be an array of Lire::Report::Section objects. =cut sub sections { return @{$_[0]{'_sections'}}; } =pod =head2 add_section( $section ) Adds a section to this report. The $section parameter should be a Lire::Report::Section object. =cut sub add_section { my ( $self, $section ) = @_; check_object_param( $section, 'section', 'Lire::Report::Section' ); push @{$self->{'_sections'}}, $section; return; } =pod =head2 subreport_by_id( $id ) Returns the Lire::Report::Subreport object with $id. Returns undef when there is no subreport with that ID. =cut sub subreport_by_id { my ( $self, $id ) = @_; check_param( $id, 'id' ); foreach my $section ( @{$self->{'_sections'}} ) { foreach my $subreport ( $section->subreports() ) { return $subreport if $subreport->id() eq $id; } } return undef; } =pod =head2 schemas() Returns an array reference containing all the schemas used by this report. =cut sub schemas { my $self = $_[0]; my %schemas = (); foreach my $sect ( $self->sections() ) { foreach my $sub ( $sect->subreports() ) { foreach my $id ( @{$sub->schemas()} ) { $schemas{$id} = 1; } } } return [ sort keys %schemas ]; } =pod =head2 write_report( [FH] ); Write the report in XML format on the FH filehandle or STDOUT if omitted. This method takes care of adding stuff like the XML header C?xml version=[...]>. It encodes the XML report in UTF-8, using Lire::I18N::set_fh_encoding(). =cut sub write_report { my ( $self, $fh ) = @_; $fh ||= \*STDOUT; set_fh_encoding( $fh, 'UTF-8' ); my $time = strftime '%Y-%m-%d %H:%M:%S %Z', localtime $self->date(); my $period_string = ""; if ( $self->timespan_start() ) { my $stime = strftime( '%Y-%m-%d %H:%M:%S %Z', localtime $self->timespan_start() ); my $etime = strftime( '%Y-%m-%d %H:%M:%S %Z', localtime $self->timespan_end() ); $period_string = "$stime - $etime"; } else { $period_string = "Unknown Period"; } print $fh < {'_version'}//EN" "http://www.logreport.org/LRML/$self->{'_version'}/lrml.dtd"> EOF print $fh " ", xml_encode( $self->title() ), "\n" if defined $self->title(); print $fh ' ', $time, "\n"; { no warnings 'uninitialized'; print $fh ' {'_period'} ? "period=\"$self->{'_period'}\" " : "" ), 'start="', $self->{'_start'}, '" end="', $self->{'_end'}, '">', $period_string, "\n"; } print $fh " ", $self->description(), "\n\n" if ( $self->description() ); foreach my $s ( $self->sections() ) { $s->write_report( $fh, 1 ); } print $fh "\n"; return; } # Creates an identifier for Subreport's type $type. # This creates an ID that will make sure that the # subreport would be merged identically than with the # previous merging algorithm which didn't merge by id. sub create_subreport_id { my ( $self, $type ) = @_; $self->{'_id_cache'}{$type} ||= 0; return $type . "." . $self->{'_id_cache'}{$type}++; } =pod =head2 delete( ) Removes all circular references so that the object can be freed. =cut sub delete { my $self = $_[0]; foreach my $s ( $self->sections() ) { foreach my $r ( $s->subreports() ) { $r->delete(); } } return; } # keep perl happy 1; __END__ =pod =head1 SEE ALSO Lire::ReportParser::ReportBuilder(3pm) Lire::Report::Section(3pm) Lire::Report::Subreport(3pm) Lire::Report::Entry(3pm) Lire::Report::Group(3pm) Lire::ReportParser(3pm) =head1 AUTHOR Francis J. Lacoste =head1 VERSION $Id: Report.pm,v 1.35 2006/07/23 13:16:29 vanbaal Exp $ =head1 COPYRIGHT Copyright (C) 2002, 2004 Stichting LogReport Foundation LogReport@LogReport.org This file is part of Lire. Lire is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program (see COPYING); if not, check with http://www.gnu.org/copyleft/gpl.html. =cut