[Bio] / SeedViewer / MetagenomeAnalysis.pm Repository:
ViewVC logotype

Diff of /SeedViewer/MetagenomeAnalysis.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.8, Wed May 7 20:31:19 2008 UTC revision 1.22, Mon Aug 25 22:16:45 2008 UTC
# Line 7  Line 7 
7    
8  use FIG_Config;  use FIG_Config;
9  use DBI;  use DBI;
10    use Data::Dumper;
11    
12  1;  1;
13    
   
   
14  use constant QUERY_DEFAULTS =>  use constant QUERY_DEFAULTS =>
15    { 1 => { evalue => '1e-05', align_len => 50 }, # RDP    { 1 => { evalue => '1e-05', align_len => 50 }, # RDP
16      2 => { evalue => '0.01' }, # SEED      2 => { evalue => '0.01' }, # SEED
# Line 21  Line 20 
20      6 => { evalue => '0.01' }, # Subsystem      6 => { evalue => '0.01' }, # Subsystem
21    };    };
22    
   
   
   
23  sub new {  sub new {
24    my ($class, $job) = @_;    my ($class, $job) = @_;
25    
# Line 70  Line 66 
66                 dbh => $dbh,                 dbh => $dbh,
67                 key2taxa => undef,                 key2taxa => undef,
68                 query => {},                 query => {},
69                       dbid_cache => {},
70               };               };
71    bless $self, $class;    bless $self, $class;
72    
# Line 80  Line 77 
77    
78  }  }
79    
   
   
   
80  sub job {  sub job {
81    return $_[0]->{job};    return $_[0]->{job};
82  }  }
# Line 100  Line 94 
94    return $_[0]->{dbtable};    return $_[0]->{dbtable};
95  }  }
96    
97    sub dbtable_best_psc {
98      unless (defined $_[0]->{dbtable}) {
99        $_[0]->{dbtable} = 'tax_sim_best_by_psc_'.$_[0]->job->id;
100      }
101      return $_[0]->{dbtable};
102    }
103    
104    sub dbtable_best_iden {
105      unless (defined $_[0]->{dbtable}) {
106        $_[0]->{dbtable} = 'tax_sim_best_by_iden_'.$_[0]->job->id;
107      }
108      return $_[0]->{dbtable};
109    }
110    
111  sub get_key2taxa_mapping {  sub get_key2taxa_mapping {
112    
# Line 144  Line 151 
151  }  }
152    
153    
154    #
155    # Determine the correct dbid for this job. Use sims.database_list
156    # to find the version that the analysis was run with.
157    #
158  sub get_dataset_id {  sub get_dataset_id {
159    # mapping contained in table db_type      my($self, $dataset) = @_;
   # unstable, changes every time  
   my $db_ids = { 'SEED' => 2,  
                  'Greengenes' => 3,  
                  'RDP' => 1,  
                  'LSU' => 4,  
                  'SSU' => 5,  
                  'Subsystem' => 6,  
                };  
   return $db_ids->{ $_[1] } || undef;  
 }  
160    
161        my $id = $self->{dbid_cache}->{$dataset};
162        return $id if defined($id);
163    
164        my($dbname, $type) = split(/:/, $dataset);
165    
166        my $dbs = $self->job->metaxml->get_metadata('sims.database_list');
167    
168        my @this = grep { $_->{name} eq $dbname } @$dbs;
169        if (@this)
170        {
171            my $vers = $this[0]->{version};
172    
173            #
174            # Now we can find the dbid ni the database.
175            #
176            my $res = $self->dbh->selectcol_arrayref(qq(SELECT dbid
177                                                        FROM seq_db
178                                                        WHERE name = ? AND version = ?
179                                                            AND tax_db_name = ?), undef,
180                                                     $dbname, $vers, $type);
181            if (@$res)
182            {
183                #print STDERR "Found @$res for $dbname $type $vers\n";
184                $id = $res->[0];
185                $self->{dbid_cache}->{$dataset} = $id;
186                return $id;
187            }
188            #print STDERR "Did not find anything for dataset='$dataset' '$dbname' '$type' '$vers'\n";
189        }
190        #print STDERR "did not find a vers for dataset='$dataset' $dbname $type\n" . Dumper($dbs);
191    }
192    
193  #******************************************************************************  #******************************************************************************
194  #* MANAGING QUERY CRITERIA  #* MANAGING QUERY CRITERIA
# Line 332  Line 364 
364  #******************************************************************************  #******************************************************************************
365    
366    
   
   
   
367  =pod  =pod
368    
369  =item * B<get_sequence> (I<sequence_id>)  =item * B<get_sequence> (I<sequence_id>)
# Line 364  Line 393 
393    
394  }  }
395    
396    =pod
397    
398    =item * B<get_sequences_fasta> (I<sequence_ids>)
399    
400    Retrieve the sequences for a given list of I<sequence_ids> from the metagenome job directory in fasta format.
401    
402    =cut
403    
404    sub get_sequences_fasta {
405      my ($self, $ids) = @_;
406    
407      # get the path to the sequence file
408      my $sequence_file = $self->job->org_dir.'/contigs';
409    
410      # hash the ids
411      my %idh = map { $_ => 1 } @$ids;
412    
413      # store the result
414      my $sequence = '';
415    
416      # iterate over the sequence file
417      open(FASTA, "<$sequence_file") or die "Unable to read metagenome sequences: $!";
418      while(<FASTA>) {
419    
420        # look only at id lines
421        next unless /^\>/;
422    
423        # get the id
424        my ($id) = /^\>(.+)/;
425        chomp($id);
426    
427        # find the id in the requested id hash
428        next unless exists ($idh{$id});
429    
430        # id was found, delete it from the hash
431        delete $idh{$id};
432    
433        # print the header line
434        $sequence .= $_;
435    
436        # print all the following lines to the result
437        while(<FASTA>) {
438          last if /^>/;
439          $sequence .= $_;
440        }
441    
442        last unless (scalar(keys(%idh)));
443      }
444    
445      return $sequence;
446    
447    }
448    
449  =pod  =pod
450    
# Line 377  Line 458 
458  sub get_hits_count {  sub get_hits_count {
459    my ($self, $dataset) = @_;    my ($self, $dataset) = @_;
460    
461    my $table = $self->dbtable;    my $table = $self->dbtable_best_psc;
462    my $dbid  = $self->get_dataset_id($dataset);    my $dbid  = $self->get_dataset_id($dataset);
463    my $where = $self->get_where_clause();    my $where = $self->get_where_clause();
464    $where = ($where) ? "and $where" : '';    $where = ($where) ? "and $where" : '';
465    
466    my $sth = $self->dbh->prepare("select count(*) from ( select id1, min(rank_psc) from $table where dbid=$dbid $where group by id1) as b");    my $sth = $self->dbh->prepare("select count(distinct id1) from  $table where dbid=$dbid $where");
467    $sth->execute;    $sth->execute;
468    my ($result) = $sth->fetchrow_array;    my ($result) = $sth->fetchrow_array;
469    
# Line 407  Line 488 
488  sub get_group_counts {  sub get_group_counts {
489    my ($self, $dataset, $group, $filter1, $filter2) = @_;    my ($self, $dataset, $group, $filter1, $filter2) = @_;
490    
491    my $table = $self->dbtable;    my $table = $self->dbtable_best_psc;
492    my $dbid  = $self->get_dataset_id($dataset);    my $dbid  = $self->get_dataset_id($dataset);
493    my $where = $self->get_where_clause();    my $where = $self->get_where_clause();
494    $where = ($where) ? "and $where" : '';    $where = ($where) ? "and $where" : '';
# Line 418  Line 499 
499    push @filters, "tax_group_2='$filter2'" if($filter2);    push @filters, "tax_group_2='$filter2'" if($filter2);
500    my $filter = (scalar(@filters)) ? 'and '.join(' and ', @filters) : '';    my $filter = (scalar(@filters)) ? 'and '.join(' and ', @filters) : '';
501    
502    my $sth = $self->dbh->prepare("select s.$group as tax, count(*) as num from ( select id1, min(rank_psc) as rank from $table where dbid=$dbid $where group by id1) as b inner join $table as s on b.id1=s.id1 and b.rank=s.rank_psc where dbid=$dbid $filter group by s.$group");    print STDERR "select $group as tax, count(*) as num from $table where dbid=$dbid $where $filter group by tax";
503      my $sth = $self->dbh->prepare("select $group as tax, count(*) as num from $table where dbid=$dbid $where $filter group by tax");
504    $sth->execute;    $sth->execute;
505    my $result = $sth->fetchall_arrayref();    my $result = $sth->fetchall_arrayref();
506    
507      #print STDERR "get_group_counts: ds=$dataset group=$group filter1=$filter1 filter2=$filter2\n";
508      #print STDERR Dumper($result);
509    return $result;    return $result;
510    
511  }  }
# Line 439  Line 523 
523  sub get_taxa_counts {  sub get_taxa_counts {
524    my ($self, $dataset) = @_;    my ($self, $dataset) = @_;
525    
526    my $table = $self->dbtable;    my $table = $self->dbtable_best_psc;
527    my $dbid  = $self->get_dataset_id($dataset);    my $dbid  = $self->get_dataset_id($dataset);
528    my $where = $self->get_where_clause();    my $where = $self->get_where_clause();
529    $where = ($where) ? "and $where" : '';    $where = ($where) ? "and $where" : '';
530    
531    my $sth = $self->dbh->prepare("select s.tax_str as tax, count(*) as num from ( select id1, min(rank_psc) as rank from $table where dbid=$dbid $where group by id1) as b inner join $table as s on b.id1=s.id1 and b.rank=s.rank_psc where dbid=$dbid group by s.tax_str");    my $sth = $self->dbh->prepare("select tax_str as tax, count(*) from $table where dbid=$dbid $where group by tax");
532    
533    $sth->execute;    $sth->execute;
534    my $result = $sth->fetchall_arrayref();    my $result = $sth->fetchall_arrayref();
# Line 453  Line 537 
537    
538  }  }
539    
540    sub get_tax2genomeid {
541      my ($self, $taxstring) = @_;
542    
543      my ($genomeid) = $self->dbh->selectrow_array(qq(SELECT seq_num
544                                                    FROM rdp_to_tax
545                                                    WHERE tax_str= ?), undef, $taxstring);
546    
547      return $genomeid;
548    }
549    
550    
551  =pod  =pod
552    
# Line 466  Line 560 
560  sub get_subsystem_counts {  sub get_subsystem_counts {
561    my ($self, $dataset) = @_;    my ($self, $dataset) = @_;
562    
563    my $table = $self->dbtable;    my $table = $self->dbtable_best_psc;
564    my $dbid  = $self->get_dataset_id($dataset);    my $dbid  = $self->get_dataset_id($dataset);
565    my $where = $self->get_where_clause();    my $where = $self->get_where_clause();
566    $where = ($where) ? "and $where" : '';    $where = ($where) ? "and $where" : '';
567    
568    #print STDERR "select s.tax_group_1, s.tax_group_2, s.tax_group_3, s.tax_str, count(*) as num from ( select id1, min(rank_psc) as rank from $table where dbid=$dbid $where group by id1) as b inner join $table as s on b.id1=s.id1 and b.rank=s.rank_psc where dbid=$dbid group by s.tax_group_1, s.tax_group_2, s.tax_group_3";    my $sth = $self->dbh->prepare("select tax_group_1, tax_group_2, tax_group_3, tax_str, count(*) as num from $table where dbid=$dbid $where group by tax_group_1, tax_group_2, tax_group_3, tax_str");
  #die;  
   my $sth = $self->dbh->prepare("select s.tax_group_1, s.tax_group_2, s.tax_group_3, s.tax_str, count(*) as num from ( select id1, min(rank_psc) as rank from $table where dbid=$dbid $where group by id1) as b inner join $table as s on b.id1=s.id1 and b.rank=s.rank_psc where dbid=$dbid group by s.tax_group_1, s.tax_group_2, s.tax_group_3");  
569    
570    $sth->execute;    $sth->execute;
571    my $result = $sth->fetchall_arrayref();    my $result = $sth->fetchall_arrayref();
# Line 497  Line 589 
589  sub get_sequence_subset {  sub get_sequence_subset {
590    my ($self, $dataset, $filter) = @_;    my ($self, $dataset, $filter) = @_;
591    
592    my $table = $self->dbtable;    my $table = $self->dbtable_best_psc;
593    my $dbid  = $self->get_dataset_id($dataset);    my $dbid  = $self->get_dataset_id($dataset);
594    my $where = $self->get_where_clause();    my $where = $self->get_where_clause();
595    $where = ($where) ? "and $where" : '';    $where = ($where) ? "and $where" : '';
596    
597    my $sth = $self->dbh->prepare("select s.id1, s.ali_ln, s.id2, s.tax_str from ( select id1, min(rank_psc) as rank from $table where dbid=$dbid $where group by id1) as b inner join $table as s on b.id1=s.id1 and b.rank=s.rank_psc where dbid=$dbid and s.tax_str like '$filter%'");    $filter =~ s/'/''/g;
598    
599      my $sth = $self->dbh->prepare("select id1, ali_ln, id2, tax_str, logpsc, bsc, iden, b1, e1, b2, e2 from $table where dbid=$dbid $where and tax_str like '$filter%'");
600    $sth->execute;    $sth->execute;
601    my $result = $sth->fetchall_arrayref();    my $result = $sth->fetchall_arrayref();
602    
# Line 511  Line 604 
604    
605  }  }
606    
607    =pod
608    
609    =item * B<get_sequence_subset_genome> (I<genome>)
610    
611    Given a dataset name (db_id), this method returns all sequence ids,
612    the alignment length, the match id and the taxonomy string for all
613    sequences which match the criteria and have their tax_str start with
614    the filter string I<filter>.
615    
616    =cut
617    
618    sub get_sequence_subset_genome {
619      my ($self, $genome) = @_;
620    
621      my $table = $self->dbtable_best_psc;
622      my $dbid  = $self->get_dataset_id("SEED:seed_genome_tax");
623      my $where = $self->get_where_clause();
624      $where = ($where) ? "and $where" : '';
625    
626      my ($tax_id) = $self->dbh->selectrow_array(qq(SELECT tax_str
627                                                    FROM rdp_to_tax
628                                                    WHERE seq_num= ?), undef, $genome);
629    
630      if($tax_id =~ /(\S+)\s/){
631        $tax_id = $1;
632      }
633    
634      my $sth = $self->dbh->prepare(qq(SELECT id1, ali_ln, id2, tax_str, logpsc, bsc, iden, b1, e1, b2, e2
635                                       FROM $table
636                                       WHERE dbid=? $where and tax_str=?));
637    
638    
639      $sth->execute($dbid, $tax_id);
640      my $result = $sth->fetchall_arrayref();
641    
642      return $result;
643    
644    }
645    
646  =pod  =pod
647    
# Line 526  Line 657 
657  sub get_recruitment_plot_data {  sub get_recruitment_plot_data {
658    my ($self, $genome) = @_;    my ($self, $genome) = @_;
659    
660    my $table = $self->dbtable;    my $table = $self->dbtable_best_psc;
661    my $dbid  = $self->get_dataset_id("SEED");    my $dbid  = $self->get_dataset_id("SEED:seed_genome_tax");
662    my $where = $self->get_where_clause();    my $where = $self->get_where_clause();
663    $where = ($where) ? "and $where" : '';    $where = ($where) ? "and $where" : '';
664    
665    my ($tax_id) = $self->dbh->selectrow_array("select tax_str from rdp_to_tax where seq_num='". $genome . "'");    my ($tax_id) = $self->dbh->selectrow_array(qq(SELECT tax_str
666                                                    FROM rdp_to_tax
667                                                    WHERE seq_num= ?), undef, $genome);
668    
669    my $sth = $self->dbh->prepare("select s.id1, s.id2, s.b2, s.e2, s.logpsc from ( select id1, min(rank_psc) as rank from $table where dbid=$dbid $where group by id1) as b inner join $table as s on b.id1=s.id1 and b.rank=s.rank_psc where dbid=$dbid and s.tax_str='$tax_id'");    if($tax_id =~ /(\S+)\s/){
670        $tax_id = $1;
671      }
672    
673    $sth->execute;    my $sth = $self->dbh->prepare(qq(SELECT id1, id2, b2, e2, logpsc
674                                       FROM $table
675                                       WHERE dbid=? $where and tax_str=?));
676    
677    
678      $sth->execute($dbid, $tax_id);
679    my $result = $sth->fetchall_arrayref();    my $result = $sth->fetchall_arrayref();
680    
681    return $result;    return $result;
# Line 564  Line 704 
704    my $dbid  = $self->get_dataset_id($dataset);    my $dbid  = $self->get_dataset_id($dataset);
705    $limit = 10 unless ($limit);    $limit = 10 unless ($limit);
706    
707    my $sth = $self->dbh->prepare("select id2, tax_str, logpsc, bsc, ali_ln, iden, b1, e1 from $table where id1=$id and dbid=$dbid and rank_psc<$limit;");    my $sth = $self->dbh->prepare(qq(SELECT id2, tax_str, logpsc, bsc, ali_ln,
708    $sth->execute;                                          iden, b1, e1
709                                       FROM $table
710                                       WHERE id1=? AND dbid=? AND rank_psc < ?));
711      $sth->execute($id, $dbid, $limit);
712    
713      my $result = $sth->fetchall_arrayref();
714    
715      return $result;
716    
717    }
718    
719    =item * B<get_best_hit_for_sequence> (I<seq_id>, I<dataset>)
720    
721    Given a sequence id I<seq_id> (id1) and a dataset name (db_id), this method returns
722    the first row of hit data for this sequence.
723    It returns (match id, taxonomy string, log evalue, bitscore, alignment length,
724    percent identity, start1, end1) per hit.
725    
726    =cut
727    
728    sub get_best_hit_for_sequence {
729      my ($self, $id, $dataset) = @_;
730    
731      my $table = $self->dbtable;
732      my $dbid  = $self->get_dataset_id($dataset);
733    
734      my $sth = $self->dbh->prepare(qq(SELECT id2, tax_str, logpsc, bsc, ali_ln,
735                                            iden, b1, e1, b2, e2
736                                       FROM $table
737                                       WHERE id1=? AND dbid=?));
738      $sth->execute($id, $dbid);
739    
740    my $result = $sth->fetchall_arrayref();    my $result = $sth->fetchall_arrayref();
741    
742    return $result;    return $result;
# Line 598  Line 769 
769    
770  =pod  =pod
771    
772    =item * B<get_genome_id> (I<tax_str>)
773    
774    =cut
775    
776    sub get_genome_id {
777      my ($self, $tax_str) = @_;
778      $tax_str =~ s/'/''/g;
779      my $retval =  $self->dbh->selectrow_array("select seq_num from rdp_to_tax where tax_str='". $tax_str . "'");
780      if (ref($retval) eq 'ARRAY') {
781        return $retval->[0];
782      } else {
783        return $retval;
784      }
785    }
786    
787    =pod
788    
789    =item * B<get_mg_comparison_table_metadata>
790    
791    returns the metadata (column names, headers) for the metagenome tables used in comparing metagenomes
792    
793    =cut
794    sub get_mg_comparison_table_metadata{
795        my ($self) = @_;
796        my $column_metadata = {};
797        my $desc = $self->data('dataset_desc');
798        my $metagenome = $self->application->cgi->param('metagenome') || '';
799        my $next_col;
800    
801        if (dataset_is_phylo($desc)){
802            $column_metadata->{Domain} = {'value'=>'Domain',
803                                          'header' => { name => 'Domain', filter => 1, operator => 'combobox',
804                                                        visible => 0, show_control => 1 },
805                                            'order' => 1,
806                                            'visible' => 1,
807                                            'group' => 'permanent'
808                                            };
809            $column_metadata->{level1} = {'value'=>'Taxa Level 1',
810                                          'header' => { name => '', filter => 1, operator => 'combobox',
811                                                        sortable => 1, width => 150 },
812                                            'order' => 2,
813                                            'visible' => 1,
814                                            'group' => 'permanent'
815                                            };
816            $column_metadata->{level2} = {'value'=>'Taxa Level 2',
817                                          'header' => { name => '', filter => 1, operator => 'combobox',
818                                                        width => 150 },
819                                            'order' => 3,
820                                            'visible' => 1,
821                                            'group' => 'permanent'
822                                            };
823            $column_metadata->{level3} = {'value'=>'Taxa Level 3',
824                                          'header' => { name => '', filter => 1, operator => 'combobox',
825                                                        width => 150 },
826                                            'order' => 4,
827                                            'visible' => 1,
828                                            'group' => 'permanent'
829                                            };
830            $column_metadata->{organism} = {'value'=>'Organism',
831                                            'header' => { name => 'Organism Name', filter => 1 },
832                                                'order' => 5,
833                                                'visible' => 1,
834                                            'group' => 'permanent'};
835            $next_col = 6;
836        }
837        elsif (dataset_is_metabolic($desc)){
838            $column_metadata->{hierarchy1} = {'value'=>'Subsystem Hierarchy 1',
839                                              'header' => { name => 'Subsystem Hierarchy 1', filter => 1,
840                                                            operator => 'combobox', width => 150, sortable => 1 },
841                                                    'order' => 1,
842                                                    'visible' => 1,
843                                                    'group' => 'permanent'
844                                                    };
845            $column_metadata->{hierarchy2} = {'value'=>'Subsystem Hierarchy 1',
846                                              'header' => { name => 'Subsystem Hierarchy 2', filter => 1,
847                                                            width => 150  },
848                                                    'order' => 2,
849                                                    'visible' => 1,
850                                                    'group' => 'permanent'
851                                                    };
852            $column_metadata->{hierarchy3} = {'value'=>'Subsystem Hierarchy 1',
853                                              'header' => { name => 'Subsystem Name', filter => 1,
854                                                            sortable => 1,  width => 150  },
855                                                    'order' => 3,
856                                                    'visible' => 1,
857                                                    'group' => 'permanent'
858                                                    };
859            $next_col = 4;
860        }
861    
862        # add your metagenome to permanent and add the other possible metagenomes to the select listbox
863        # check for available metagenomes
864        my $rast = $self->application->data_handle('RAST');
865        my $available = {};
866        if (ref($rast)) {
867            my $public_metagenomes = &get_public_metagenomes($self->app->dbmaster, $rast);
868            foreach my $pmg (@$public_metagenomes) {
869                $column_metadata->{$pmg->[0]} = {'value' => 'Public - ' . $pmg->[1],
870                                                 'header' => { name => $pmg->[0],
871                                                                     filter => 1,
872                                                                     operators => ['equal', 'unequal', 'less', 'more'],
873                                                                     sortable => 1,
874                                                                     width => 150,
875                                                                     tooltip => $pmg->[1] . '(' . $pmg->[0] . ')'
876                                                                     },
877                                                                     };
878                if ($pmg->[0] eq $metagenome){
879                    $column_metadata->{$pmg->[0]}->{order} = $next_col;
880                    $column_metadata->{$pmg->[0]}->{visible} = 1;
881                    $column_metadata->{$pmg->[0]}->{group} = 'permanent';
882                }
883                else{
884                    $column_metadata->{$pmg->[0]}->{visible} = 0;
885                    $column_metadata->{$pmg->[0]}->{group} = 'metagenomes';
886                }
887            }
888    
889            if ($self->application->session->user) {
890    
891                my $mgs = $rast->Job->get_jobs_for_user($self->application->session->user, 'view', 1);
892    
893          # build hash from all accessible metagenomes
894                foreach my $mg_job (@$mgs) {
895                    $column_metadata->{$mg_job->genome_id} = {'value' => 'Private - ' . $mg_job->genome_name,
896                                                              'header' => { name => $mg_job->genome_id,
897                                                                            filter => 1,
898                                                                            operators => ['equal', 'unequal', 'less', 'more'],
899                                                                            sortable => 1,
900                                                                            width => 150,
901                                                                            tooltip => $mg_job->genome_name . '(' . $mg_job->genome_id . ')'
902                                                                            },
903                                                                            };
904                    if ( ($mg_job->metagenome) && ($mg_job->genome_id eq $metagenome) ) {
905                        $column_metadata->{$mg_job->genome_id}->{order} = $next_col;
906                        $column_metadata->{$mg_job->genome_id}->{visible} = 1;
907                        $column_metadata->{$mg_job->genome_id}->{group} = 'permanent';
908                    }
909                    else{
910                        $column_metadata->{$mg_job->genome_id}->{visible} = 0;
911                        $column_metadata->{$mg_job->genome_id}->{group} = 'metagenomes';
912                    }
913                }
914            }
915        }
916        else {
917        # no rast/user, no access to metagenomes
918        }
919    
920        return $column_metadata;
921    }
922    
923    
924    =pod
925    
926  =back  =back
927    
928  =cut  =cut

Legend:
Removed from v.1.8  
changed lines
  Added in v.1.22

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3