package CPANDB::Distribution; use 5.008005; use strict; use warnings; use DateTime 0.50 (); use ORLite::Statistics 0.03; our $VERSION = '0.16'; my $today = DateTime->today( time_zone => 'UTC' ); ###################################################################### # DateTime Integration sub perl_version { my $self = shift; my @rows = CPANDB::Requires->select( 'where distribution = ? and module = ? and phase = ?', $self->distribution, 'perl', 'runtime', ); if ( @rows ) { return $rows[0]->version; } else { return undef; } } sub uploaded_datetime { my $self = shift; my @date = split(/-/, $self->uploaded); DateTime->new( year => $date[0], month => $date[1], day => $date[2], @CPANDB::LOCATION, ); } sub age { my $class = shift; my $duration = $class->age_duration; return $duration->in_units('days'); } sub age_duration { my $class = shift; my $latest = $class->uploaded_datetime; my $today = DateTime->today( @CPANDB::LOCATION ); return $today - $latest; } sub age_months { $_[0]->age_duration->in_units('months'); } sub quartile { my $self = shift; # Get the boundary dates my @quartile = ref($self)->_quartile; # Find which quartile we are in my $uploaded = $self->uploaded; if ( $uploaded gt $quartile[0] ) { return 1; } elsif ( $uploaded gt $quartile[1] ) { return 2; } elsif ( $uploaded gt $quartile[2] ) { return 3; } else { return 4; } } my @QUADRANT = (); sub _quartile { return @QUADRANT if @QUADRANT; # Start with the total number of distributions my $class = shift; my $rows = $class->count; my $mod = $rows % 4; my $range = ($rows - $mod) / 4; # Find the last row in each quartile foreach ( 1 .. 4 ) { my $offset = ($range * $_) + $mod - 1; # Tweak the boundary rows to deal with row totals # that are not divisible by four. By generous about # moving edge cases up if so. if ( $mod - $_ > 0 ) { $offset = $offset - ( $mod - $_ ); } # Find the upload date for the resulting row my @object = $class->select("order by uploaded desc limit 1 offset $offset"); unless ( @object == 1 ) { die("Failed to find edge of quartile $_"); } push @QUADRANT, $object[0]->uploaded; } return @QUADRANT; } ###################################################################### # Graph Integration sub dependency_graph { require Graph::Directed; shift->_dependency( _class => 'Graph::Directed', @_ ); } sub dependants_graph { require Graph::Directed; shift->_dependants( _class => 'Graph::Directed', @_ ); } sub dependency_easy { require Graph::Easy; shift->_dependency( _class => 'Graph::Easy', @_ ); } sub dependants_easy { require Graph::Easy; shift->_dependants( _class => 'Graph::Easy', @_ ); } sub dependency_graphviz { require GraphViz; shift->_dependency( _class => 'GraphViz', @_ ); } sub dependants_graphviz { require GraphViz; shift->_dependants( _class => 'GraphViz', @_ ); } sub dependency_xgmml { require Graph::XGMML; my $self = shift; my @param = ( @_ == 1 ) ? ( OUTPUT => IO::File->new( shift, 'w' ) ) : ( @_ ); $self->_dependency( _class => 'Graph::XGMML', @param ); } sub dependants_xgmml { require Graph::XGMML; my $self = shift; my @param = ( @_ == 1 ) ? ( OUTPUT => IO::File->new( shift, 'w' ) ) : ( @_ ); $self->_dependants( _class => 'Graph::XGMML', @param ); } sub _dependency { my $self = shift; my %param = @_; my $class = delete $param{_class}; my $phase = delete $param{phase}; my $perl = delete $param{perl}; # Prepare support values for the algorithm my $add_node = $class->can('add_vertex') ? 'add_vertex' : 'add_node'; my $sql_where = 'where distribution = ?'; my @sql_param = (); if ( $phase ) { $sql_where .= ' and phase = ?'; push @sql_param, $phase; } if ( $perl ) { $sql_where .= ' and ( core is null or core >= ? )'; push @sql_param, $perl; } # Pass any remaining params to the graph constructor my $graph = $class->new( %param ); # Fill the graph via simple list recursion my @todo = ( $self->distribution ); my %seen = ( $self->distribution => 1 ); while ( @todo ) { my $name = shift @todo; $graph->$add_node( $name ); # Find the distinct dependencies for this node my %edge = (); my @deps = grep { not $edge{$_}++ } map { $_->dependency } CPANDB::Dependency->select( $sql_where, $name, @sql_param, ); foreach my $dep ( @deps ) { $graph->add_edge( $name => $dep ); } # Push the new ones to the list push @todo, grep { not $seen{$_}++ } @deps; } return $graph; } sub _dependants { my $self = shift; my %param = @_; my $class = delete $param{_class}; my $phase = delete $param{phase}; my $perl = delete $param{perl}; # Prepare support values for the algorithm my $add_node = $class->can('add_vertex') ? 'add_vertex' : 'add_node'; my $sql_where = 'where dependency = ?'; my @sql_param = (); if ( $phase ) { $sql_where .= ' and phase = ?'; push @sql_param, $phase; } if ( $perl ) { $sql_where .= ' and ( core is null or core >= ? )'; push @sql_param, $perl; } # Pass any remaining params to the graph constructor my $graph = $class->new( %param ); # Fill the graph via simple list recursion my @todo = ( $self->distribution ); my %seen = ( $self->distribution => 1 ); while ( @todo ) { my $name = shift @todo; next if $name =~ /^Task-/; next if $name =~ /^Acme-Mom/; $graph->$add_node( $name ); # Find the distinct dependencies for this node my %edge = (); my @deps = grep { not $edge{$_}++ } map { $_->distribution } CPANDB::Dependency->select( $sql_where, $name, @sql_param, ); foreach my $dep ( @deps ) { next if $dep =~ /^Task-/; next if $dep =~ /^Acme-Mom/; $graph->add_edge( $name => $dep ); } # Push the new ones to the list push @todo, grep { not $seen{$_}++ } @deps; } return $graph; } 1;