line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# -*-CPerl-*- |
2
|
|
|
|
|
|
|
# Last changed Time-stamp: <2015-02-06 16:29:26 mtw> |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Bio::ViennaNGS::UCSC; |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
843
|
use Exporter; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
37
|
|
7
|
1
|
|
|
1
|
|
4
|
use version; our $VERSION = qv('0.12_15'); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
8
|
1
|
|
|
1
|
|
54
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
24
|
|
9
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
16
|
|
10
|
1
|
|
|
1
|
|
456
|
use Template; |
|
1
|
|
|
|
|
14220
|
|
|
1
|
|
|
|
|
23
|
|
11
|
1
|
|
|
1
|
|
6
|
use Cwd; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
50
|
|
12
|
1
|
|
|
1
|
|
3
|
use File::Basename; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
41
|
|
13
|
1
|
|
|
1
|
|
5
|
use IPC::Cmd qw(can_run run); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
36
|
|
14
|
1
|
|
|
1
|
|
4
|
use File::Share ':all'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
146
|
|
15
|
1
|
|
|
1
|
|
5
|
use Path::Class; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
45
|
|
16
|
1
|
|
|
1
|
|
4
|
use Data::Dumper; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
32
|
|
17
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
48
|
|
18
|
1
|
|
|
1
|
|
10
|
use Bio::ViennaNGS::UCSC; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2716
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our @EXPORT_OK = qw( make_assembly_hub make_track_hub ); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
our @EXPORT = (); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^# |
27
|
|
|
|
|
|
|
#^^^^^^^^^^^ Subroutines ^^^^^^^^^^# |
28
|
|
|
|
|
|
|
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^# |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub make_assembly_hub{ |
31
|
0
|
|
|
0
|
1
|
|
my ($fasta_path, $filesdir, $basedir, $baseURL, $log) = @_; |
32
|
0
|
|
|
|
|
|
my ($basename,$dir,$ext); |
33
|
0
|
|
|
|
|
|
my $this_function = (caller(0))[3]; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
#check arguments |
36
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function] \$fasta_path does not exist\n") |
37
|
|
|
|
|
|
|
unless (-e $fasta_path); |
38
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function] \$basedir does not exist\n") |
39
|
|
|
|
|
|
|
unless (-d $basedir); |
40
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function]: no URL (network location for upload to UCSC) provided") |
41
|
|
|
|
|
|
|
unless(defined $baseURL); |
42
|
|
|
|
|
|
|
|
43
|
0
|
0
|
|
|
|
|
if (defined $log){ |
44
|
0
|
0
|
|
|
|
|
open(LOG, ">>", $log) or croak "$!"; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
0
|
0
|
|
|
|
|
unless ($baseURL =~ /\/$/) { $baseURL .= "/"; } |
|
0
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
49
|
0
|
|
|
|
|
|
my $tmp_path = dist_file('Bio-ViennaNGS', "hub.txt" ); |
50
|
0
|
|
|
|
|
|
($basename,$dir,$ext) = fileparse($tmp_path,qr/\..*/); |
51
|
0
|
|
|
|
|
|
my $template_path = dir($dir,"template-AssemblyHub"); |
52
|
|
|
|
|
|
|
|
53
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function] template directory not found\n") |
54
|
|
|
|
|
|
|
unless (-d $template_path); |
55
|
0
|
0
|
|
|
|
|
my $faToTwoBit = can_run('faToTwoBit') or |
56
|
|
|
|
|
|
|
croak ("ERROR [$this_function] faToTwoBit is not installed!"); |
57
|
|
|
|
|
|
|
|
58
|
0
|
0
|
|
|
|
|
my $bedToBigBed = can_run('bedToBigBed') or |
59
|
|
|
|
|
|
|
croak ("ERROR [$this_function] bedToBigBed is not installed!"); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# bedfiles path |
62
|
0
|
|
|
|
|
|
my @parsedHeader = parse_fasta_header($fasta_path,$this_function); |
63
|
0
|
|
|
|
|
|
my $unchecked_accession = $parsedHeader[0]; |
64
|
0
|
|
|
|
|
|
my $scientificName = $parsedHeader[1]; |
65
|
0
|
|
|
|
|
|
my $accession = valid_ncbi_accession($unchecked_accession); |
66
|
|
|
|
|
|
|
# create assembly hub directory structure |
67
|
0
|
|
|
|
|
|
my $assembly_hub_name = "assemblyHub"; |
68
|
0
|
|
|
|
|
|
my $assembly_hub_directory = dir($basedir, $assembly_hub_name); |
69
|
0
|
|
|
|
|
|
my $genome_assembly_name = $accession; |
70
|
0
|
|
|
|
|
|
my $genome_assembly_directory = dir($assembly_hub_directory,$genome_assembly_name); |
71
|
0
|
|
|
|
|
|
mkdir $assembly_hub_directory; |
72
|
0
|
|
|
|
|
|
mkdir $genome_assembly_directory; |
73
|
0
|
0
|
|
|
|
|
if (defined $log){ |
74
|
0
|
|
|
|
|
|
print LOG "LOG Base directory: $assembly_hub_directory\n"; |
75
|
0
|
|
|
|
|
|
print LOG "LOG Assembly Hub directory: $genome_assembly_directory\n"; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
#2-bit fasta file conversion |
79
|
0
|
|
|
|
|
|
my $fa_modified = file($assembly_hub_directory, $accession.".fa"); |
80
|
0
|
|
|
|
|
|
modify_fasta_header($fasta_path,$fa_modified,$accession); |
81
|
0
|
|
|
|
|
|
my $twoBit = file($genome_assembly_directory, $accession.".2bit"); |
82
|
0
|
|
|
|
|
|
my $fastaToTwobit_cmd = "$faToTwoBit $fa_modified $twoBit"; |
83
|
0
|
0
|
|
|
|
|
if (defined $log){ print LOG "LOG [$this_function] $fastaToTwobit_cmd\n";} |
|
0
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
85
|
0
|
|
|
|
|
|
my( $success, $error_message, $full_buf, $stdout_buf, $stderr_buf ) = |
86
|
|
|
|
|
|
|
run( command => $fastaToTwobit_cmd, verbose => 0 ); |
87
|
0
|
0
|
|
|
|
|
if( !$success ) { |
88
|
0
|
|
|
|
|
|
print STDERR "ERROR [$this_function] External command call unsuccessful\n"; |
89
|
0
|
|
|
|
|
|
print STDERR "ERROR: this is what the command printed:\n"; |
90
|
0
|
|
|
|
|
|
print join "", @$full_buf; |
91
|
0
|
|
|
|
|
|
croak $!; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
#template definition |
95
|
0
|
|
|
|
|
|
my $template = Template->new({ |
96
|
|
|
|
|
|
|
INCLUDE_PATH => ["$template_path"], |
97
|
|
|
|
|
|
|
RELATIVE=>1, |
98
|
|
|
|
|
|
|
}); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
#construct hub.txt |
101
|
0
|
|
|
|
|
|
my $hubtxt_path = file($assembly_hub_directory,"hub.txt")->stringify; |
102
|
0
|
|
|
|
|
|
my $hubtxt_file = "hub.txt"; |
103
|
0
|
|
|
|
|
|
my $hubtxt_vars = |
104
|
|
|
|
|
|
|
{ |
105
|
|
|
|
|
|
|
hubName => $accession, |
106
|
|
|
|
|
|
|
shortLabel => $accession, |
107
|
|
|
|
|
|
|
longLabel => $accession, |
108
|
|
|
|
|
|
|
genomesFile => "genome.txt", |
109
|
|
|
|
|
|
|
email => 'email', |
110
|
|
|
|
|
|
|
descriptionURL => "$baseURL" . "description.html" |
111
|
|
|
|
|
|
|
}; |
112
|
0
|
0
|
|
|
|
|
$template->process($hubtxt_file,$hubtxt_vars,"$hubtxt_path") || |
113
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
#construct genome.txt |
116
|
0
|
|
|
|
|
|
my $genometxt_path = file($assembly_hub_directory, "genome.txt")->stringify; |
117
|
0
|
|
|
|
|
|
my $genometxt_file = "genome.txt"; |
118
|
0
|
|
|
|
|
|
my $genometxt_vars = |
119
|
|
|
|
|
|
|
{ |
120
|
|
|
|
|
|
|
genome => $accession, |
121
|
|
|
|
|
|
|
trackDb => file($accession, "trackDb.txt"), |
122
|
|
|
|
|
|
|
groups => file($accession, "groups.txt"), |
123
|
|
|
|
|
|
|
description => "$accession", |
124
|
|
|
|
|
|
|
twoBitPath => file($accession,$accession.".2bit"), |
125
|
|
|
|
|
|
|
organism => "organism", |
126
|
|
|
|
|
|
|
defaultPos => $accession, |
127
|
|
|
|
|
|
|
orderKey => "10", |
128
|
|
|
|
|
|
|
scientificName => "$scientificName", |
129
|
|
|
|
|
|
|
htmlPath => file($accession,"description.html") |
130
|
|
|
|
|
|
|
}; |
131
|
0
|
0
|
|
|
|
|
$template->process($genometxt_file,$genometxt_vars,$genometxt_path) or |
132
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
#construct description.html |
135
|
0
|
|
|
|
|
|
my $description_html_path = file($genome_assembly_directory, "description.html")->stringify; |
136
|
0
|
|
|
|
|
|
my $description_html_file = "description.html"; |
137
|
0
|
|
|
|
|
|
my $description_html_vars = |
138
|
|
|
|
|
|
|
{ |
139
|
|
|
|
|
|
|
imageLink => "imageLink", |
140
|
|
|
|
|
|
|
imageSource => "imageSource", |
141
|
|
|
|
|
|
|
imageAlternative => "imageAlternative", |
142
|
|
|
|
|
|
|
taxonomicName => "taxonomicName", |
143
|
|
|
|
|
|
|
imageOrigin => "imageOrigin", |
144
|
|
|
|
|
|
|
imageOriginDescription => "imageOriginDescription", |
145
|
|
|
|
|
|
|
ucscId => "ucscId", |
146
|
|
|
|
|
|
|
sequencingId => "sequencingId", |
147
|
|
|
|
|
|
|
assemblyDate => "assemblyDate", |
148
|
|
|
|
|
|
|
genbankAccessionId => "genbankAccessionId", |
149
|
|
|
|
|
|
|
ncbiGenomeInformationLink => "ncbiGenomeInformationLink", |
150
|
|
|
|
|
|
|
ncbiGenomeInformationDescription => "ncbiGenomeInformationDescription", |
151
|
|
|
|
|
|
|
ncbiAssemblyInformationLink => "ncbiAssemblyInformationLink", |
152
|
|
|
|
|
|
|
ncbiAssemblyInformationDescription => "ncbiAssemblyInformationDescription", |
153
|
|
|
|
|
|
|
bioProjectInformationLink => "bioProjectInformationLink", |
154
|
|
|
|
|
|
|
bioProjectInformationDescription => "bioProjectInformationDescription", |
155
|
|
|
|
|
|
|
sequenceAnnotationLink => "sequenceAnnotationLink" |
156
|
|
|
|
|
|
|
}; |
157
|
0
|
0
|
|
|
|
|
$template->process($description_html_file,$description_html_vars,$description_html_path) or |
158
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
|
my $groups = make_group("annotation", "Annotation", "1", "0"); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
#construct group.txt |
163
|
0
|
|
|
|
|
|
my $group_txt_path = file($genome_assembly_directory, "groups.txt")->stringify; |
164
|
0
|
|
|
|
|
|
my $group_txt_file = 'groups.txt'; |
165
|
0
|
|
|
|
|
|
my $group_txt_vars = |
166
|
|
|
|
|
|
|
{ |
167
|
|
|
|
|
|
|
groups => "$groups", |
168
|
|
|
|
|
|
|
}; |
169
|
0
|
0
|
|
|
|
|
$template->process($group_txt_file,$group_txt_vars,$group_txt_path) or |
170
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
my $chromosome_size = retrieve_chromosome_size($fasta_path); |
174
|
0
|
|
|
|
|
|
my $chromosome_size_filepath = file($genome_assembly_directory,"$accession.chrom.sizes"); |
175
|
0
|
|
|
|
|
|
write_chromosome_size_file($chromosome_size_filepath,$accession,$chromosome_size); |
176
|
0
|
|
|
|
|
|
convert_tracks($filesdir, $genome_assembly_directory, $accession, $bedToBigBed, $chromosome_size_filepath); |
177
|
0
|
|
|
|
|
|
my @trackfiles = retrieve_tracks($genome_assembly_directory, $baseURL, $assembly_hub_name, $accession); |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
my $tracksList; |
180
|
0
|
|
|
|
|
|
foreach my $track (@trackfiles){ |
181
|
0
|
|
|
|
|
|
my $trackString = make_track(@$track); |
182
|
0
|
|
|
|
|
|
$tracksList .= $trackString; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
#construct trackDb.txt |
186
|
0
|
|
|
|
|
|
my $trackDb_txt_path = file($genome_assembly_directory, "trackDb.txt")->stringify; |
187
|
0
|
|
|
|
|
|
my $trackDb_txt_file = 'trackDb.txt'; |
188
|
0
|
|
|
|
|
|
my $trackDb_txt_vars = |
189
|
|
|
|
|
|
|
{ |
190
|
|
|
|
|
|
|
tracks => "$tracksList" |
191
|
|
|
|
|
|
|
}; |
192
|
0
|
0
|
|
|
|
|
$template->process($trackDb_txt_file,$trackDb_txt_vars,$trackDb_txt_path) or |
193
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
194
|
|
|
|
|
|
|
|
195
|
0
|
0
|
|
|
|
|
if (defined $log){ |
196
|
0
|
|
|
|
|
|
print LOG "LOG Assembly Hub created\n"; |
197
|
0
|
|
|
|
|
|
close(LOG); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub make_track_hub{ |
202
|
0
|
|
|
0
|
1
|
|
my ($species, $filesdir, $basedir, $baseURL, $chrom_sizes_file, $chrom_size_file, $log) = @_; |
203
|
0
|
|
|
|
|
|
my ($basename,$dir,$ext); |
204
|
0
|
|
|
|
|
|
my $this_function = (caller(0))[3]; |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
#check arguments |
207
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function] \no species provided\n") |
208
|
|
|
|
|
|
|
unless ($species); |
209
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function] \$basedir does not exist\n") |
210
|
|
|
|
|
|
|
unless (-d $basedir); |
211
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function]: no URL (network location for upload to UCSC) provided") |
212
|
|
|
|
|
|
|
unless(defined $baseURL); |
213
|
|
|
|
|
|
|
|
214
|
0
|
0
|
|
|
|
|
if (defined $log){ |
215
|
0
|
0
|
|
|
|
|
open(LOG, ">>", $log) or croak "$!"; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
0
|
0
|
|
|
|
|
unless ($baseURL =~ /\/$/) { $baseURL .= "/"; } |
|
0
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
|
my $tmp_path = dist_file('Bio-ViennaNGS', "hub.txt" ); |
221
|
0
|
|
|
|
|
|
($basename,$dir,$ext) = fileparse($tmp_path,qr/\..*/); |
222
|
0
|
|
|
|
|
|
my $template_path = dir($dir,"template-TrackHub"); |
223
|
|
|
|
|
|
|
|
224
|
0
|
0
|
|
|
|
|
croak ("ERROR [$this_function] template directory not found\n") |
225
|
|
|
|
|
|
|
unless (-d $template_path); |
226
|
0
|
0
|
|
|
|
|
my $faToTwoBit = can_run('faToTwoBit') or |
227
|
|
|
|
|
|
|
croak ("ERROR [$this_function] faToTwoBit is not installed!"); |
228
|
|
|
|
|
|
|
|
229
|
0
|
0
|
|
|
|
|
my $bedToBigBed = can_run('bedToBigBed') or |
230
|
|
|
|
|
|
|
croak ("ERROR [$this_function] bedToBigBed is not installed!"); |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# create track hub directory structure |
233
|
0
|
|
|
|
|
|
my $track_hub_name = "trackHub"; |
234
|
0
|
|
|
|
|
|
my $track_hub_directory = dir($basedir, $track_hub_name); |
235
|
0
|
|
|
|
|
|
my $genome_assembly_name = $species; |
236
|
0
|
|
|
|
|
|
my $genome_assembly_directory = dir($track_hub_directory,$genome_assembly_name); |
237
|
0
|
|
|
|
|
|
mkdir $track_hub_directory; |
238
|
0
|
|
|
|
|
|
mkdir $genome_assembly_directory; |
239
|
0
|
0
|
|
|
|
|
if (defined $log){ |
240
|
0
|
|
|
|
|
|
print LOG "LOG Base directory: $track_hub_directory\n"; |
241
|
0
|
|
|
|
|
|
print LOG "LOG Track Hub directory: $genome_assembly_directory\n"; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
#template definition |
245
|
0
|
|
|
|
|
|
my $template = Template->new({ |
246
|
|
|
|
|
|
|
INCLUDE_PATH => ["$template_path"], |
247
|
|
|
|
|
|
|
RELATIVE=>1, |
248
|
|
|
|
|
|
|
}); |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
#construct hub.txt |
251
|
0
|
|
|
|
|
|
my $hubtxt_path = file($track_hub_directory,"hub.txt")->stringify; |
252
|
0
|
|
|
|
|
|
my $hubtxt_file = "hub.txt"; |
253
|
0
|
|
|
|
|
|
my $hubtxt_vars = |
254
|
|
|
|
|
|
|
{ |
255
|
|
|
|
|
|
|
hubName => $species, |
256
|
|
|
|
|
|
|
shortLabel => $species, |
257
|
|
|
|
|
|
|
longLabel => $species, |
258
|
|
|
|
|
|
|
genomesFile => "genome.txt", |
259
|
|
|
|
|
|
|
email => 'email', |
260
|
|
|
|
|
|
|
descriptionURL => "$baseURL" . "description.html" |
261
|
|
|
|
|
|
|
}; |
262
|
0
|
0
|
|
|
|
|
$template->process($hubtxt_file,$hubtxt_vars,"$hubtxt_path") || |
263
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
#construct genome.txt |
266
|
0
|
|
|
|
|
|
my $genometxt_path = file($track_hub_directory, "genome.txt")->stringify; |
267
|
0
|
|
|
|
|
|
my $genometxt_file = "genome.txt"; |
268
|
0
|
|
|
|
|
|
my $genometxt_vars = |
269
|
|
|
|
|
|
|
{ |
270
|
|
|
|
|
|
|
genome => $species, |
271
|
|
|
|
|
|
|
trackDb => file($species, "trackDb.txt"), |
272
|
|
|
|
|
|
|
groups => file($species, "groups.txt"), |
273
|
|
|
|
|
|
|
description => "$species", |
274
|
|
|
|
|
|
|
twoBitPath => file($species,$species.".2bit"), |
275
|
|
|
|
|
|
|
organism => "organism", |
276
|
|
|
|
|
|
|
defaultPos => $species, |
277
|
|
|
|
|
|
|
orderKey => "10", |
278
|
|
|
|
|
|
|
scientificName => "scientificName", |
279
|
|
|
|
|
|
|
htmlPath => file($species,"description.html") |
280
|
|
|
|
|
|
|
}; |
281
|
0
|
0
|
|
|
|
|
$template->process($genometxt_file,$genometxt_vars,$genometxt_path) or |
282
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
283
|
|
|
|
|
|
|
|
284
|
0
|
0
|
|
|
|
|
if(-e $chrom_sizes_file){ |
285
|
0
|
|
|
|
|
|
convert_tracks($filesdir, $genome_assembly_directory, $species, $bedToBigBed, $chrom_sizes_file); |
286
|
|
|
|
|
|
|
}else{ |
287
|
0
|
|
|
|
|
|
my $chromosome_sizes = fetch_chrom_sizes($species); |
288
|
0
|
|
|
|
|
|
my $chromosome_size_filepath = file($genome_assembly_directory,"$species.chrom.sizes"); |
289
|
0
|
|
|
|
|
|
write_chromosome_sizes_file($chromosome_size_filepath,$chromosome_sizes); |
290
|
0
|
|
|
|
|
|
convert_tracks($filesdir, $genome_assembly_directory, $species, $bedToBigBed, $chromosome_size_filepath); |
291
|
|
|
|
|
|
|
} |
292
|
0
|
|
|
|
|
|
my @trackfiles = retrieve_tracks($genome_assembly_directory, $baseURL, $track_hub_name, $species); |
293
|
|
|
|
|
|
|
|
294
|
0
|
|
|
|
|
|
my $tracksList; |
295
|
0
|
|
|
|
|
|
foreach my $track (@trackfiles){ |
296
|
0
|
|
|
|
|
|
my $trackString = make_track(@$track); |
297
|
0
|
|
|
|
|
|
$tracksList .= $trackString; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
#construct trackDb.txt |
301
|
0
|
|
|
|
|
|
my $trackDb_txt_path = file($genome_assembly_directory, "trackDb.txt")->stringify; |
302
|
0
|
|
|
|
|
|
my $trackDb_txt_file = 'trackDb.txt'; |
303
|
0
|
|
|
|
|
|
my $trackDb_txt_vars = |
304
|
|
|
|
|
|
|
{ |
305
|
|
|
|
|
|
|
tracks => "$tracksList" |
306
|
|
|
|
|
|
|
}; |
307
|
0
|
0
|
|
|
|
|
$template->process($trackDb_txt_file,$trackDb_txt_vars,$trackDb_txt_path) or |
308
|
|
|
|
|
|
|
croak "Template process failed: ", $template->error(), "\n"; |
309
|
|
|
|
|
|
|
|
310
|
0
|
0
|
|
|
|
|
if (defined $log){ |
311
|
0
|
|
|
|
|
|
print LOG "LOG Track Hub created\n"; |
312
|
0
|
|
|
|
|
|
close(LOG); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub convert_tracks{ |
317
|
0
|
|
|
0
|
0
|
|
my ($filesdir,$genome_assembly_directory,$accession,$bedToBigBed,$chromosome_size_filepath) = @_; |
318
|
0
|
|
|
|
|
|
my $currentDirectory = getcwd; |
319
|
0
|
0
|
|
|
|
|
chdir $filesdir or croak $!; |
320
|
0
|
|
|
|
|
|
my @bedfiles = <*.bed>; |
321
|
0
|
|
|
|
|
|
foreach my $bedfile (@bedfiles){ |
322
|
0
|
|
|
|
|
|
my $bed_file_path = file($filesdir,$bedfile); |
323
|
0
|
|
|
|
|
|
my $filename = $bedfile; |
324
|
0
|
|
|
|
|
|
$filename =~ s/.bed$//; |
325
|
0
|
|
|
|
|
|
my $bigbed_file = $filename . ".bb"; |
326
|
0
|
|
|
|
|
|
my $bigbed_file_path = file($genome_assembly_directory,$bigbed_file); |
327
|
0
|
|
|
|
|
|
`$bedToBigBed $bed_file_path $chromosome_size_filepath $bigbed_file_path`; |
328
|
|
|
|
|
|
|
} |
329
|
0
|
0
|
|
|
|
|
chdir $currentDirectory or croak $!; |
330
|
0
|
|
|
|
|
|
return 1; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub retrieve_tracks{ |
334
|
0
|
|
|
0
|
0
|
|
my ($directoryPath,$baseURL,$assembly_hub_name,$accession) = @_; |
335
|
0
|
|
|
|
|
|
my $currentDirectory = getcwd; |
336
|
0
|
0
|
|
|
|
|
chdir $directoryPath or croak $!; |
337
|
0
|
|
|
|
|
|
my @trackfiles = <*.bb>; |
338
|
0
|
|
|
|
|
|
my @tracks; |
339
|
0
|
|
|
|
|
|
my $counter = 0; |
340
|
0
|
|
|
|
|
|
foreach my $trackfile (@trackfiles){ |
341
|
0
|
|
|
|
|
|
my $filename = $trackfile; |
342
|
0
|
|
|
|
|
|
$filename =~ s/.bb$//; |
343
|
0
|
|
|
|
|
|
my @filenameSplit = split(/\./, $filename); |
344
|
0
|
|
|
|
|
|
my $fileaccession = $filenameSplit[0]; |
345
|
0
|
|
|
|
|
|
my $tag = $filenameSplit[1]; |
346
|
0
|
|
|
|
|
|
my $id = lc($tag); |
347
|
0
|
|
|
|
|
|
my $track = "refseq_" . $id; |
348
|
0
|
|
|
|
|
|
my $dir = dir($baseURL,$assembly_hub_name,$accession); |
349
|
|
|
|
|
|
|
#my $bigDataUrl = file($dir, $trackfile); |
350
|
0
|
|
|
|
|
|
my $bigDataUrl = file($trackfile); |
351
|
0
|
|
|
|
|
|
my $shortLabel = "RefSeq " . $tag; |
352
|
0
|
|
|
|
|
|
my $longLabel = "RefSeq " . $tag; |
353
|
0
|
|
|
|
|
|
my $type = "bigBed 12 ."; |
354
|
0
|
|
|
|
|
|
my $autoScale = "off"; |
355
|
0
|
|
|
|
|
|
my $bedNameLabel = "Gene Id"; |
356
|
0
|
|
|
|
|
|
my $searchIndex = "name"; |
357
|
|
|
|
|
|
|
#my $colorByStrand = "100,205,255 55,155,205"; |
358
|
0
|
|
|
|
|
|
my $colorByStrand = retrieve_color($counter ); |
359
|
0
|
|
|
|
|
|
my $visibility = "pack"; |
360
|
0
|
|
|
|
|
|
my $group = "annotation"; |
361
|
0
|
|
|
|
|
|
my $priority = "10"; |
362
|
0
|
|
|
|
|
|
my @track = ($tag,$track,$bigDataUrl,$shortLabel,$longLabel,$type,$autoScale,$bedNameLabel,$searchIndex,$colorByStrand,$visibility,$group,$priority); |
363
|
0
|
|
|
|
|
|
my $trackreference = \@track; |
364
|
0
|
|
|
|
|
|
push(@tracks, $trackreference); |
365
|
0
|
|
|
|
|
|
$counter++; |
366
|
|
|
|
|
|
|
} |
367
|
0
|
0
|
|
|
|
|
chdir $currentDirectory or croak $!; |
368
|
0
|
|
|
|
|
|
return @tracks; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub retrieve_color{ |
372
|
0
|
|
|
0
|
0
|
|
my $counter = shift; |
373
|
0
|
|
|
|
|
|
my $digitnumber = length($counter); |
374
|
0
|
|
|
|
|
|
my $colorcode; |
375
|
0
|
0
|
|
|
|
|
if($digitnumber>1){ |
376
|
0
|
|
|
|
|
|
$colorcode = $counter % 10; |
377
|
|
|
|
|
|
|
}else{ |
378
|
0
|
|
|
|
|
|
$colorcode = $counter; |
379
|
|
|
|
|
|
|
} |
380
|
0
|
|
|
|
|
|
my @color; |
381
|
|
|
|
|
|
|
#green |
382
|
0
|
|
|
|
|
|
$color[0] = "133,154,0 133,154,0"; |
383
|
|
|
|
|
|
|
#cyan |
384
|
0
|
|
|
|
|
|
$color[1] = "42,162,152 42,162,152"; |
385
|
|
|
|
|
|
|
#blue |
386
|
0
|
|
|
|
|
|
$color[2] = "38,140,210 38,140,210"; |
387
|
|
|
|
|
|
|
#violet |
388
|
0
|
|
|
|
|
|
$color[3] = "108,114,196 108,114,196"; |
389
|
|
|
|
|
|
|
#magenta |
390
|
0
|
|
|
|
|
|
$color[4] = "211,55,130 211,55,130"; |
391
|
|
|
|
|
|
|
#red |
392
|
0
|
|
|
|
|
|
$color[5] = "220,51,47 220,51,47"; |
393
|
|
|
|
|
|
|
#orange |
394
|
0
|
|
|
|
|
|
$color[6] = "203,76,22 203,76,22"; |
395
|
|
|
|
|
|
|
#yellow |
396
|
0
|
|
|
|
|
|
$color[7] = "181,138,0 181,138,0"; |
397
|
|
|
|
|
|
|
#black |
398
|
0
|
|
|
|
|
|
$color[8] = "0,44,54 0,44,54"; |
399
|
|
|
|
|
|
|
#grey |
400
|
0
|
|
|
|
|
|
$color[9] = "88,111,117 88,111,117"; |
401
|
|
|
|
|
|
|
|
402
|
0
|
|
|
|
|
|
return $color[$colorcode]; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub make_group{ |
406
|
0
|
|
|
0
|
0
|
|
my ($name, $label, $priority, $defaultIsClosed) = @_; |
407
|
0
|
|
|
|
|
|
my $group ="name $name\nlabel $label\npriority $priority\ndefaultIsClosed $defaultIsClosed\n"; |
408
|
0
|
|
|
|
|
|
return $group; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
sub make_track{ |
412
|
0
|
|
|
0
|
0
|
|
my ($tag, $track, $bigDataUrl, $shortLabel, $longLabel, $type, $autoScale, $bedNameLabel, $searchIndex, $colorByStrand, $visibility, $group, $priority) = @_; |
413
|
0
|
|
|
|
|
|
my $trackEntry ="#$tag\ntrack $track\nbigDataUrl $bigDataUrl\nshortLabel $shortLabel\nlongLabel $longLabel\ntype $type\nautoScale $autoScale\nbedNameLabel $bedNameLabel\nsearchIndex $searchIndex\ncolorByStrand $colorByStrand\nvisibility $visibility\ngroup $group\npriority $priority\n\n"; |
414
|
0
|
|
|
|
|
|
return $trackEntry; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub valid_ncbi_accession{ |
418
|
|
|
|
|
|
|
# receives a NCBI accession ID, with or without version number |
419
|
|
|
|
|
|
|
# returns NCBI accession ID without version number |
420
|
0
|
|
|
0
|
0
|
|
my $acc = shift; |
421
|
0
|
0
|
|
|
|
|
if ($acc =~ /^(N[CST]\_\d{6})\.\d+?$/){ |
|
|
0
|
|
|
|
|
|
422
|
0
|
|
|
|
|
|
return $acc; # NCBI accession ID without version |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
elsif ($acc =~ /^(N[CST]\_\d{6})$/){ |
425
|
0
|
|
|
|
|
|
return $1; # NCBI accession ID without version |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
else { |
428
|
0
|
|
|
|
|
|
return 0; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub parse_fasta_header{ |
433
|
0
|
|
|
0
|
0
|
|
my $filepath = shift; |
434
|
0
|
|
|
|
|
|
my $this_function = shift; |
435
|
0
|
|
|
|
|
|
open my $file, '<', "$filepath"; |
436
|
0
|
|
|
|
|
|
my $fastaheader = <$file>; |
437
|
0
|
|
|
|
|
|
chomp $fastaheader; |
438
|
0
|
|
|
|
|
|
close $file; |
439
|
|
|
|
|
|
|
#>gi|556503834|ref|NC_000913.3| Escherichia coli str. K-12 substr. MG1655 |
440
|
0
|
0
|
|
|
|
|
if($fastaheader=~/^>gi|/){ |
441
|
0
|
|
|
|
|
|
print LOG "#NCBI fasta header detected\n"; |
442
|
0
|
|
|
|
|
|
my @headerfields = split(/\|/, $fastaheader); |
443
|
0
|
|
|
|
|
|
my $accession = $headerfields[3]; |
444
|
0
|
|
|
|
|
|
my $scientificName = $headerfields[4]; |
445
|
0
|
|
|
|
|
|
my @ids; |
446
|
0
|
|
|
|
|
|
push(@ids,$accession); |
447
|
0
|
|
|
|
|
|
push(@ids,$scientificName); |
448
|
0
|
|
|
|
|
|
return @ids; |
449
|
|
|
|
|
|
|
}else{ |
450
|
0
|
|
|
|
|
|
my $fastaid = $fastaheader; |
451
|
0
|
|
|
|
|
|
$fastaid=~s/^>//; |
452
|
0
|
0
|
|
|
|
|
if(valid_ncbi_accession($fastaid)){ |
453
|
0
|
|
|
|
|
|
print LOG "#Header contains just valid NCBI accession number\n"; |
454
|
0
|
|
|
|
|
|
my @ids; |
455
|
0
|
|
|
|
|
|
push(@ids,$fastaid); |
456
|
0
|
|
|
|
|
|
push(@ids,"scientific name not set"); |
457
|
0
|
|
|
|
|
|
return @ids; |
458
|
|
|
|
|
|
|
}else{ |
459
|
0
|
|
|
|
|
|
print LOG "#No valid accession/ ncbi header\n"; |
460
|
0
|
|
|
|
|
|
croak ("ERROR [$this_function] \$fasta_path does not contain a valid accession/ ncbi header\n"); |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
sub write_chromosome_size_file{ |
466
|
0
|
|
|
0
|
0
|
|
my $filepath = shift; |
467
|
0
|
|
|
|
|
|
my $chromosome_name = shift; |
468
|
0
|
|
|
|
|
|
my $chromosome_size = shift; |
469
|
0
|
|
|
|
|
|
my $entry = $chromosome_name . "\t" . $chromosome_size . "\n"; |
470
|
0
|
|
|
|
|
|
open CHROMFILE, '>', "$filepath"; |
471
|
0
|
|
|
|
|
|
print CHROMFILE $entry; |
472
|
0
|
|
|
|
|
|
close CHROMFILE; |
473
|
0
|
|
|
|
|
|
return 1; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
sub write_chromosome_sizes_file{ |
477
|
0
|
|
|
0
|
0
|
|
my $filepath = shift; |
478
|
0
|
|
|
|
|
|
my $chromosome_sizes_reference = shift; |
479
|
0
|
|
|
|
|
|
my %chromosome_sizes = %{$chromosome_sizes_reference}; |
|
0
|
|
|
|
|
|
|
480
|
0
|
|
|
|
|
|
open CHROMFILE, '>', "$filepath"; |
481
|
0
|
|
|
|
|
|
foreach my $chromosome_name ( keys %chromosome_sizes){ |
482
|
0
|
|
|
|
|
|
my $chromosome_size = $chromosome_sizes{$chromosome_name}; |
483
|
0
|
|
|
|
|
|
my $entry = $chromosome_name . "\t" . $chromosome_size . "\n"; |
484
|
0
|
|
|
|
|
|
print CHROMFILE $entry; |
485
|
|
|
|
|
|
|
} |
486
|
0
|
|
|
|
|
|
close CHROMFILE; |
487
|
0
|
|
|
|
|
|
return 1; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub retrieve_chromosome_size{ |
491
|
0
|
|
|
0
|
0
|
|
my $inputFilepath = shift; |
492
|
0
|
|
|
|
|
|
open INFILE, '<', $inputFilepath; |
493
|
0
|
|
|
|
|
|
my @newfasta; |
494
|
0
|
|
|
|
|
|
my $chromosome_size = 0; |
495
|
0
|
|
|
|
|
|
my $header_skipped = 0; |
496
|
0
|
|
|
|
|
|
while (<INFILE>) { |
497
|
0
|
0
|
|
|
|
|
if($header_skipped){ |
498
|
0
|
|
|
|
|
|
chomp; |
499
|
0
|
|
|
|
|
|
$chromosome_size += length($_); |
500
|
|
|
|
|
|
|
}else{ |
501
|
0
|
|
|
|
|
|
$header_skipped = 1; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
} |
504
|
0
|
|
|
|
|
|
close INFILE; |
505
|
0
|
|
|
|
|
|
return $chromosome_size; |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub modify_fasta_header{ |
509
|
0
|
|
|
0
|
0
|
|
my $inputFilepath = shift; |
510
|
0
|
|
|
|
|
|
my $outputFilepath = shift; |
511
|
0
|
|
|
|
|
|
my $header = shift; |
512
|
|
|
|
|
|
|
|
513
|
0
|
|
|
|
|
|
open INFILE, '<', "$inputFilepath"; |
514
|
0
|
|
|
|
|
|
my @newfasta; |
515
|
0
|
|
|
|
|
|
while (<INFILE>) { |
516
|
0
|
|
|
|
|
|
push(@newfasta, $_); |
517
|
|
|
|
|
|
|
} |
518
|
0
|
|
|
|
|
|
close INFILE; |
519
|
|
|
|
|
|
|
#@newfasta = @newfasta[ 1 .. $#newfasta ]; |
520
|
0
|
|
|
|
|
|
shift @newfasta; |
521
|
0
|
|
|
|
|
|
unshift(@newfasta,">".$header."\n"); |
522
|
|
|
|
|
|
|
|
523
|
0
|
|
|
|
|
|
open OUTFILE, '>', "$outputFilepath"; |
524
|
0
|
|
|
|
|
|
foreach my $line (@newfasta) { |
525
|
0
|
|
|
|
|
|
print OUTFILE $line; |
526
|
|
|
|
|
|
|
} |
527
|
0
|
|
|
|
|
|
close OUTFILE; |
528
|
0
|
|
|
|
|
|
return 1; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
1; |
532
|
|
|
|
|
|
|
__END__ |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=head1 NAME |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
Bio::ViennaNGS::UCSC - Perl extension for easy UCSC Genome Browser |
537
|
|
|
|
|
|
|
integration. |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=head1 SYNOPSIS |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
use Bio::ViennaNGS::UCSC; |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=head1 DESCRIPTION |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
ViennaNGS::UCSC is a Perl extension for managing routine tasks with the |
546
|
|
|
|
|
|
|
UCSC Genome Browser. It comes with a set of utilities that serve as |
547
|
|
|
|
|
|
|
reference implementation of the routines implemented in the library. All |
548
|
|
|
|
|
|
|
utilities are located in the 'scripts' folder. |
549
|
|
|
|
|
|
|
The main functionality is provided by the make_assembly_hub function. |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=head2 EXPORT |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Routines: |
554
|
|
|
|
|
|
|
make_assembly_hub, make_track_hub |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
Variables: |
557
|
|
|
|
|
|
|
none |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=head3 make_assembly_hub() |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
Build assembly hubs for the UCSC genome browser. |
562
|
|
|
|
|
|
|
This function takes 4 parameters: |
563
|
|
|
|
|
|
|
1. absolute path of input fasta file(e.g. /home/user/input.fa) |
564
|
|
|
|
|
|
|
2. path to the ouput directory (e.g. /home/user/assemblyhubs/) |
565
|
|
|
|
|
|
|
3. base URL where the output folder will be placed for upload to the UCSC genome browser (e.g. http://www.foo.com/folder/) |
566
|
|
|
|
|
|
|
4. path for the log file (/home/user/logs/assemblyhubconstructionlog) |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head3 make_track_hub() |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
Build track hubs for the UCSC genome browser. |
571
|
|
|
|
|
|
|
This function takes 4 parameters: |
572
|
|
|
|
|
|
|
1. chromosome id as used in existing ucsc assembly hub (e.g. chr1) |
573
|
|
|
|
|
|
|
2. path to the ouput directory (e.g. /home/user/assemblyhubs/) |
574
|
|
|
|
|
|
|
3. base URL where the output folder will be placed for upload to the UCSC genome browser (e.g. http://www.foo.com/folder/) |
575
|
|
|
|
|
|
|
4. path for the log file (/home/user/logs/assemblyhubconstructionlog) |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=head1 SEE ALSO |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=over |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=item L<Bio::ViennaNGS> |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=back |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=head1 AUTHORS |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=over |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=item Michael T. Wolfinger, E<lt>michael@wolfinger.euE<gt> |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item Florian Eggenhofer, E<lt>florian.eggenhofer@univie.ac.atE<gt> |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=back |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
Copyright (C) 2015 Michael T. Wolfinger, E<lt>michael@wolfinger.euE<gt> |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
601
|
|
|
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.10.0 or, |
602
|
|
|
|
|
|
|
at your option, any later version of Perl 5 you may have available. |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, |
605
|
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
606
|
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=cut |