Analizo

 view release on metacpan or  search on metacpan

lib/Analizo/Batch/Job.pm  view on Meta::CPAN

#     ['field2', 10],
#   ]
#
# In this case, B<metadata_hashref> must return the following:
#
#   {
#     'field1' => 'value1',
#     'field2' => 10,
#   }
#
sub metadata_hashref($) {
  my ($self) = @_;
  my %hash = map { $_->[0] => $_->[1] } @{$self->metadata()};
  return \%hash;
}

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

  $self->prepare();

lib/Analizo/Batch/Job.pm  view on Meta::CPAN

  if (!defined $metrics) {
    $metrics = Analizo::Metrics->new(model => $self->model);
    $metrics->data();
    $self->cache->set($metrics_cache_key, $metrics);
  }
  $self->metrics($metrics);

  $self->cleanup();
}

sub project_name($) {
  my ($self) = @_;
  return basename($self->directory);
}

sub cache($) {
  my ($self) = @_;
  $self->{cache} ||= CHI->new(driver => 'File', root_dir => _get_cache_dir());
}

sub _get_cache_dir {
  if ($ENV{ANALIZO_CACHE}) {
    return $ENV{ANALIZO_CACHE};
  }

  # automated test environment should not mess with the real cache

lib/Analizo/Batch/Job.pm  view on Meta::CPAN

  if ($program_path[0] eq '.') {
    shift @program_path;
  }
  if ($program_path[0] eq 't') {
    return tempdir(CLEANUP => 1);
  }

  return File::Spec->catfile(File::HomeDir->my_home, '.cache', 'analizo', $Analizo::VERSION)
}

sub tree_id($) {
  my ($self) = @_;
  my @input = sort($self->apply_filters('.'));
  my $sha1 = Digest::SHA->new;
  foreach my $input_file (@input) {
    $sha1->addfile($input_file);
  }
  return $sha1->hexdigest;
}

1;

lib/Analizo/Batch/Job/Git.pm  view on Meta::CPAN

use File::Spec;
use Digest::SHA qw/ sha1_hex /;
use File::Copy::Recursive qw(dircopy);
use File::Path qw(remove_tree);

sub new {
  my ($class, $directory, $id, $data) = @_;
  $class->SUPER::new(directory => $directory, actual_directory => $directory, id => $id, data => $data);
}

sub batch($$) {
  my ($self, $batch) = @_;
  if ($batch) {
    $self->{finder} = sub { $batch->find($_[0]); };
    $batch->share_filters_with($self);
  }
  return undef;
}

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

lib/Analizo/Batch/Output/CSV.pm  view on Meta::CPAN

    $self->_write_details($job->id, $details);
  }
}

my $__encoders = {
  _default => sub { $_[0] },
  ARRAY => sub { '"' . join(';', @{$_[0]}) . '"' },
  HASH => sub { '"' . join(';', map { join(':', $_, $_[0]->{$_}) } sort(keys(%{$_[0]}))) . '"' },
};

sub _encode_value($) {
  my ($value) = @_;
  my $encoder = $__encoders->{ref($value)} || $__encoders->{_default};
  return &$encoder($value);
}

sub _extract_short_names_of_metrics {
  my $metrics_instance = Analizo::Metrics->new;
  my @short_names = ();

  my %metrics_names = $metrics_instance->list_of_metrics();

lib/Analizo/Batch/Output/DB.pm  view on Meta::CPAN

package Analizo::Batch::Output::DB;
use strict;
use warnings;
use parent qw( Analizo::Batch::Output );
use DBI;
use Digest::SHA qw(sha1_hex);

sub database($) {
  my ($self) = @_;
  my $db = $self->file || 'output.sqlite3';
  if ($db =~ /^dbi:/) {
    return $db;
  } else {
    return 'dbi:SQLite:' . $db;
  }
}

sub push($$) {
  my ($self, $job) = @_;

  my $project_id = $self->_add_project($job->project_name);

  my $developer_id = $self->_add_developer($job);

  my $commit_id = $self->_add_commit($job, $project_id, $developer_id);

  $self->_add_modules($job, $commit_id, $project_id);
}

sub flush($) {
  my ($self) = @_;
  $self->{dbh}->disconnect();
}

sub _find_row_id($$@) {
  my ($self, $sql, @data) = @_;
  my $statement_id = 'st_find_' . sha1_hex($sql); # is this SHA1 needed at all?

  $self->{$statement_id} ||= $self->{dbh}->prepare($sql);
  my $list = $self->{dbh}->selectall_arrayref($self->{$statement_id}, {}, @data);
  if (scalar(@$list) == 0) {
    return undef;
  } else {
    return $list->[0]->[0];
  }
}

sub _add_project($$) {
  my ($self, $project) = @_;
  $self->{st_add_project}   ||= $self->{dbh}->prepare('INSERT INTO projects (name) values(?)');

  my $project_id = $self->_find_project($project);
  if (! $project_id) {
    $self->{st_add_project}->execute($project);
    $project_id = $self->_find_project($project);
  }

  return $project_id;
}

sub _find_project($$) {
  my ($self, $project) = @_;
  return $self->_find_row_id('SELECT id from projects where name = ?', $project);
}

sub _add_developer($$) {
  my ($self, $job) = @_;

  my $metadata = $job->metadata_hashref();
  my $name  = $metadata->{author_name};
  my $email = $metadata->{author_email};

  # FIXME unstested
  if (!$name || !$email) {
    return undef;
  }

lib/Analizo/Batch/Output/DB.pm  view on Meta::CPAN

  $self->{st_add_developer} ||= $self->{dbh}->prepare('INSERT INTO developers (name,email) VALUES (?,?)');
  my $developer_id = $self->_find_developer($name, $email);
  if (! $developer_id) {
    $self->{st_add_developer}->execute($name, $email);
    $developer_id = $self->_find_developer($name, $email);
  }

  return $developer_id;
}

sub _find_developer($$$) {
  my ($self, $name, $email) = @_;
  return $self->_find_row_id('SELECT id FROM developers WHERE name = ? AND email = ?', $name, $email);
}

sub _add_commit($$$$) {
  my ($self, $job, $project_id, $developer_id) = @_;
  unless ($self->_find_row_id('SELECT id FROM commits where id = ?', $job->id)) {
    my $metadata = $job->metadata_hashref;
    my $previous_commit_id = $metadata->{previous_commit_id};
    my $date = $metadata->{author_date};

    my ($summary, $details) = ({}, {});
    if ($job->metrics) {
      ($summary, $details) = $job->metrics->data();
    }
    my ($metric_placeholders_sql, $metric_column_names_sql, @metric_values) = _metrics_sql($summary, _project_metric_columns());

    $self->{st_insert_commit} ||= $self->{dbh}->prepare("INSERT INTO commits (id, project_id,developer_id,previous_commit_id,date,$metric_column_names_sql) VALUES(?,?,?,?,?,$metric_placeholders_sql)");
    $self->{st_insert_commit}->execute($job->id, $project_id, $developer_id, $previous_commit_id, $date, @metric_values);
  }
  return $job->id;
}

sub _add_modules($$$$) {
  my ($self, $job, $commit_id, $project_id) = @_;
  my $metadata = $job->metadata_hashref();

  my %module_versions = ();
  if ($metadata->{files}) {
    for my $file (keys(%{$metadata->{files}})) {
      for my $module ($job->model->module_by_file($file)) {
        unless($module_versions{$module}) {
          my $module_id = $self->_add_module($module, $project_id);

lib/Analizo/Batch/Output/DB.pm  view on Meta::CPAN

        if ($statuses =~ /^A+$/) {
          $self->_mark_as_added($commit_id, $module_version_id);
        } elsif ($statuses !~ /^D+$/) {
          $self->_mark_as_modified($commit_id, $module_version_id);
        }
      }
    }
  }
}

sub _add_module($$$) {
  my ($self, $module, $project_id) = @_;

  my $module_id = $self->_find_module($module, $project_id);
  if (!$module_id) {
    $self->{st_add_module} ||= $self->{dbh}->prepare('INSERT INTO modules (name, project_id) values (?,?)');
    $self->{st_add_module}->execute($module, $project_id);
    $module_id = $self->_find_module($module, $project_id);
  }

  return $module_id;
}

sub _find_module($$$) {
  my ($self, $module, $project_id) = @_;
  return $self->_find_row_id('SELECT id FROM modules WHERE name = ? AND project_id = ?', $module, $project_id);
}

sub _module_version_id {
  my @file_ids = @_;
  my $module_version_id;
  if (scalar(@file_ids) == 1) {
    $module_version_id = $file_ids[0];
  } else {

lib/Analizo/Batch/Output/DB.pm  view on Meta::CPAN

    $self->{st_add_module_version} ||= $self->{dbh}->prepare($statement);
    $self->{st_add_module_version}->execute($module_version_id, $module_id, @metric_values);
  }

  $self->{st_link_commit_and_module_version} ||= $self->{dbh}->prepare('INSERT INTO commits_module_versions (commit_id,module_version_id) VALUES (?,?)');
  $self->{st_link_commit_and_module_version}->execute($commit_id, $module_version_id);

  return $module_version_id;
}

sub _mark_as_added($$$) {
  my ($self, $commit_id, $module_version_id) = @_;
  $self->{st_mark_as_added} ||= $self->{dbh}->prepare('UPDATE commits_module_versions SET added = 1 WHERE commit_id = ? AND module_version_id = ?');
  $self->{st_mark_as_added}->execute($commit_id, $module_version_id);
}

sub _mark_as_modified($$$) {
  my ($self, $commit_id, $module_version_id) = @_;
  $self->{st_mark_as_modified} ||= $self->{dbh}->prepare('UPDATE commits_module_versions SET modified = 1 WHERE commit_id = ? AND module_version_id = ?');
  $self->{st_mark_as_modified}->execute($commit_id, $module_version_id);
}

sub _metrics_sql($@) {
  my ($metrics_hash, @metric_columns) = @_;
  my $metric_placeholders_sql = join(',', map { '?' } @metric_columns);
  my $metric_column_names_sql = join(',', @metric_columns);
  my @metric_values = map { $metrics_hash->{$_} } @metric_columns;
  return ($metric_placeholders_sql, $metric_column_names_sql, @metric_values)
}

# Initializes the database
#
# TODO the current approach of feeding DDL SQL statements directly to the
# database handle might not be good enough, since the SQL being written might
# not be portable to other databases than SQLite. It would be nice to have
# something similar to the rails migrations DSL here.
sub initialize($) {
  my ($self) = @_;
  $self->{dbh} = DBI->connect($self->database, undef, undef, { RaiseError => 1, InactiveDestroy => 1});
  # assume that if there is a table called `analizo_metadata`, then the database was already initialized
  if (!grep { $_ =~ /analizo_metadata/ } $self->{dbh}->tables()) {
    for my $statement (ddl_statements()) {
      $statement =~ s/\@\@MODULE_METRICS\@\@/_metric_columns_ddl(_module_metric_columns())/egm;
      $statement =~ s/\@\@PROJECT_METRICS\@\@/_metric_columns_ddl(_project_metric_columns())/egm;
      $statement =~ s/\@\@NUMERIC_AUTOINC_PK\@\@/_numeric_autoinc_pk($self->database)/egm;
      $self->{dbh}->do($statement);
    }
  }
}

my $DDL_INITIALIZED = 0;
my @DDL_STATEMENTS = ();
sub ddl_statements($) {
  if (!$DDL_INITIALIZED) {
    my $sql = '';
    while (my $line = <DATA>) {
      $sql .= $line;
      if ($line =~ /;\s*$/) {
        # SQL statement is ready
        CORE::push @DDL_STATEMENTS, $sql;
        $sql = '';
      }
    }
    $DDL_INITIALIZED = 1;
  }
  return @DDL_STATEMENTS;
}

use Analizo::Metrics;
use Analizo::Model;
my $__metric_columns = undef;
sub _metric_columns() {
  if (!$__metric_columns) {
    my $metrics = Analizo::Metrics->new(model => Analizo::Model->new);

    my %module_metrics = $metrics->list_of_metrics();
    my @module_metrics= keys(%module_metrics);

    my %project_metrics = $metrics->list_of_global_metrics();
    my @project_metrics = keys(%project_metrics);

    $__metric_columns = {
      module => \@module_metrics,
      project => \@project_metrics,
    };
  }
  return $__metric_columns;
}

sub _module_metric_columns() {
  return @{ _metric_columns()->{module} };
}

sub _project_metric_columns() {
  return @{ _metric_columns()->{project} };
}

sub _metric_columns_ddl {
  my @columns = @_;
  my $ddl = join(",\n  ", map { "$_ REAL"} @columns);
  return $ddl;
}

my %NUMERIC_AUTOINC_PK = (
  'sqlite'  => 'INTEGER PRIMARY KEY AUTOINCREMENT',
  'pg'      => 'SERIAL PRIMARY KEY',
);

sub _numeric_autoinc_pk($) {
  my ($database) = @_;
  my $dbtype = lc($database);
  $dbtype =~ s/^dbi:(.*):.*/$1/;
  my $sql = $NUMERIC_AUTOINC_PK{$dbtype};
  die("Database $dbtype is not supported!") if (!defined($sql));
  return $sql;
}

1;

lib/Analizo/Batch/Runner.pm  view on Meta::CPAN

package Analizo::Batch::Runner;

use parent qw(Class::Accessor::Fast);

sub new {
  my ($class, @args) = @_;
  return bless { @args }, $class;
}

sub run($$$) {
  my ($self, $batch, $output) = @_;
  $output->initialize();
  $self->actually_run($batch, $output);
  $output->flush();
}

# must be implemented by subclasses. Will receive as argument:
#
#   * the batch to be run
#   * the output object

lib/Analizo/Filter/Client.pm  view on Meta::CPAN

    push @{$self->{filters}}, @new_filters;
  }
  return $self->{filters};
}

sub has_filters {
  my ($self) = @_;
  return exists($self->{filters}) && exists($self->{filters}->[0]);
}

sub share_filters_with($$) {
  my ($self, $other) = @_;
  $other->{filters} = $self->{filters};
}

sub exclude {
  my ($self, @dirs) = @_;
  if (!$self->{excluding_dirs}) {
    $self->{excluding_dirs} = 1;
    $self->filters(Analizo::LanguageFilter->new);
  }

t/Analizo/Batch/Output/DB.t  view on Meta::CPAN

}

sub setup : Test(setup) {
  system("mkdir", "-p", $TMPDIR);
}

sub teardown : Test(teardown) {
  system("rm", "-rf", $TMPDIR);
}

sub table_created_ok($$) {
  my ($db, $table) = @_;
  my $dbh = DBI->connect("dbi:SQLite:$db");
  my $TABLE = uc($table);
  $table = lc($table);
  my @tables = $dbh->tables();
  my $projects_table = scalar(grep { lc($_) =~ /$table/ } @tables);
  ok($projects_table, "must create $TABLE table");
}

sub select_ok($$$) {
  my ($db, $query, $count) = @_;
  my $dbh = DBI->connect("dbi:SQLite:$db");
  my $rows = $dbh->selectall_arrayref($query);
  my $row_count = scalar(@$rows);
  is($row_count, $count, "[$query] returned $row_count rows instead of exactly $count");
}

sub select_one_ok($$) {
  my ($db, $query) = @_;
  select_ok($db, $query, 1);
}

__PACKAGE__->runtests;



( run in 0.745 second using v1.01-cache-2.11-cpan-65fba6d93b7 )