The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package BuzzSaw::Reporter;
use strict;
use warnings;

# $Id: Reporter.pm.in 21690 2012-08-23 07:47:01Z squinney@INF.ED.AC.UK $
# $Source:$
# $Revision: 21690 $
# $HeadURL: https://svn.lcfg.org/svn/source/tags/BuzzSaw/BuzzSaw_0_12_0/lib/BuzzSaw/Reporter.pm.in $
# $Date: 2012-08-23 08:47:01 +0100 (Thu, 23 Aug 2012) $

our $VERSION = '0.12.0';

use BuzzSaw::DateTime ();
use BuzzSaw::ReportLog ();
use BuzzSaw::Types qw(BuzzSawReportList);

use Readonly;

Readonly my $ONE_HOUR => 60 * 60;
Readonly my $ONE_DAY  => $ONE_HOUR * 24;
Readonly my $ONE_WEEK => $ONE_DAY * 7;

Readonly my @REPORT_TYPES => qw/hourly daily weekly monthly/;

use Moose;
use MooseX::Types::Moose qw(Bool);

with 'MooseX::Log::Log4perl', 'MooseX::SimpleConfig';

has '+configfile' => (
  default => '/etc/buzzsaw/reporter.yaml',
);

has 'all' => (
  is      => 'rw',
  isa     => Bool,
  default => 0,
);

has 'dryrun' => (
  is      => 'rw',
  isa     => Bool,
  default => 0,
);

has 'hourly' => (
  traits  => ['Array'],
  is      => 'ro',
  isa     => BuzzSawReportList,
  coerce  => 1,
  default => sub { [] },
  handles => {
    hourly_list => 'elements',
  },
);

has 'daily' => (
  traits  => ['Array'],
  is      => 'ro',
  isa     => BuzzSawReportList,
  coerce  => 1,
  default => sub { [] },
  handles => {
    daily_list => 'elements',
  },
);

has 'weekly' => (
  traits  => ['Array'],
  is      => 'ro',
  isa     => BuzzSawReportList,
  coerce  => 1,
  default => sub { [] },
  handles => {
    weekly_list => 'elements',
  },
);

has 'monthly' => (
  traits  => ['Array'],
  is      => 'ro',
  isa     => BuzzSawReportList,
  coerce  => 1,
  default => sub { [] },
  handles => {
    monthly_list => 'elements',
  },
);

has 'runlog' => (
  is      => 'ro',
  isa     => 'BuzzSaw::ReportLog',
  default =>  sub { BuzzSaw::ReportLog->new( store_after_change => 1 ) },
);

no Moose;
__PACKAGE__->meta->make_immutable;

sub generate_reports {
  my ( $self, @reports ) = @_;

  if ( scalar @reports == 0 ) {
    @reports = @REPORT_TYPES;
  }

  for my $report (@reports) {
    if ( $report eq 'hourly' ) {
      $self->run_hourly_reports;
    } elsif ( $report eq 'daily' ) {
      $self->run_daily_reports;
    } elsif ( $report eq 'weekly' ) {
      $self->run_weekly_reports;
    } elsif ( $report eq 'monthly' ) {
      $self->run_monthly_reports;
    } else {
      warn "Ignoring unsupported report type '$report'\n";
    }
  }

  return;
}

sub run_hourly_reports {
  my ($self) = @_;

  if ( $self->log->is_debug ) {
    $self->log->debug('Running hourly reports');
  }

  return $self->_run_reports( $ONE_HOUR, 'hourly', $self->hourly_list );
}

sub run_daily_reports {
  my ($self) = @_;

  if ( $self->log->is_debug ) {
    $self->log->debug('Running daily reports');
  }

  return $self->_run_reports( $ONE_DAY, 'daily', $self->daily_list );
}

sub run_weekly_reports {
  my ($self) = @_;

  if ( $self->log->is_debug ) {
    $self->log->debug('Running weekly reports');
  }

  return $self->_run_reports( $ONE_WEEK, 'weekly', $self->weekly_list );
}

sub run_monthly_reports {
  my ($self) = @_;

  if ( $self->log->is_debug ) {
    $self->log->debug('Running monthly reports');
  }

  my $now = BuzzSaw::DateTime->now();
  $now->set_time_zone('local');

  my $dur = DateTime::Duration->new( months => 1 );
  my $then = $now - $dur;
  my $one_month = $now->epoch() - $then->epoch();

  return $self->_run_reports( $one_month, 'monthly', $self->monthly_list );
}

sub _run_reports {
  my ( $self, $duration, $report_type, @reports ) = @_;

  my $runlog = $self->runlog;

  # This is a little bit hacky...
  my $get_timestamp_method = 'get_' . $report_type . '_timestamp';
  my $set_timestamp_method = 'set_' . $report_type . '_timestamp';
  my $has_timestamp_method = 'has_' . $report_type . '_timestamp';

  for my $report (@reports) {
    my $name = $report->name;

    my $needs_run = 0;
    if ( $self->all ) {
      $needs_run = 1;
    } elsif ( $runlog->$has_timestamp_method($name) ) {
      my $now  = time;
      my $prev = $runlog->$get_timestamp_method($name);

      if ( $now - $prev >= $duration ) {
        $needs_run = 1;
      }

    } else {
      $needs_run = 1;
    }

    if ($needs_run) {
      if ( $self->dryrun ) {
        $self->log->info("Dry-run: Would have run $name $report_type report");
      } else {
        $self->log->info("Running $name $report_type report");
        $report->generate();
        $runlog->$set_timestamp_method( $name, time );
      }
    }

  }

  return;
}

1;
__END__