File Coverage

blib/lib/Geo/StormTracker/Main.pm
Criterion Covered Total %
statement 15 500 3.0
branch 0 156 0.0
condition 0 63 0.0
subroutine 5 29 17.2
pod 7 8 87.5
total 27 756 3.5


line stmt bran cond sub pod time code
1             package Geo::StormTracker::Main;
2              
3 1     1   5 use Carp;
  1         2  
  1         64  
4 1     1   10 use File::Path;
  1         1  
  1         51  
5 1     1   688 use Geo::StormTracker::Data;
  1         2  
  1         79  
6 1     1   13 use strict;
  1         2  
  1         86  
7 1     1   6 use vars qw($VERSION);
  1         3  
  1         7847  
8              
9             $VERSION = '0.02';
10              
11             #---------------------------------------------------------
12             sub new {
13 0     0 1   my $self=shift;
14 0           my $base_path=shift;
15              
16 0           my ($msg)=undef;
17 0           my $HR={};
18              
19             #Check to see if the base path was given
20 0 0 0       unless (( defined($base_path) ) and (-e $base_path)){
21 0           $msg = "The new method's mandatory base path argument was not provided or didn't exist!";
22 0           carp $msg,"\n";
23 0           return (undef,$msg);
24             }#unless
25              
26             #Insure that the base path has a trailing slash.
27 0           $base_path =~ s!/*$!/!;
28              
29 0           $HR->{'base_path'}=$base_path;
30              
31 0           bless $HR,'Geo::StormTracker::Main';
32 0           return ($HR,undef);
33             }#new
34             #---------------------------------------------------------
35             sub add_advisory {
36 0     0 1   my $self=shift;
37 0           my $adv_obj=shift;
38 0           my $force=shift;
39 0           my $counter=shift;
40              
41 0           my ($header_frag,$region_code,$last_digits,$good)=undef;
42 0           my ($data_obj_found,$recent_storms_AR,$recent_frags_AR, $event_num, $region)=undef;
43 0           my ($paths_HR,$success,$error,$year,$msg,$new_path,$root_paths,$new_data_obj)=undef;
44              
45 0           ($header_frag,$region_code,$last_digits)=$self->_disect_header($adv_obj);
46              
47 0           ($good,$error)=$self->_check_region_syntax($region_code);
48              
49 0 0         unless ($good){
50 0           return (undef, undef, undef, undef, $error);
51             }
52              
53 0           ($paths_HR,$error)=$self->_all_paths_by_region($region_code);
54              
55             #Directory does not exist so create one.
56 0 0 0       if ( (!defined $paths_HR) and ( !$self->_region_dir_exists($region_code) ) ){
57 0           ($good,$error)=$self->_create_region_dir($region_code);
58 0 0 0       unless ((defined $good) and ($good)){
59 0           return (undef, undef, undef, undef, $error);
60             }#unless
61             }#if
62              
63              
64 0 0         if (defined $paths_HR){
65              
66 0           ($recent_storms_AR,$recent_frags_AR)=$self->_recent_storms($paths_HR->{$region_code});
67              
68 0           $data_obj_found=$self->_associate_advisory_with_storm($adv_obj,$recent_storms_AR,$recent_frags_AR);
69              
70             }#if
71              
72 0 0 0       if ((defined $paths_HR) and (defined $data_obj_found)){
73 0           ($success,$error)=$data_obj_found->insert_advisory($adv_obj,$force);
74 0 0         if ($success){
75 0           ($region, $year, $event_num)=$self->_region_year_and_event_number($data_obj_found);
76 0           return ($data_obj_found, $region, $year, $event_num ,$error);
77             }
78             else {
79 0           return (undef, undef, undef, undef, $error);
80             }#if/else
81             }
82             else {
83 0           $year=$self->_find_release_year($adv_obj);
84 0 0         unless (defined($year)) {
85 0           $msg="Advisory has a bad year in its release ";
86 0           $msg.="date and cound not be added to database!";
87 0           carp $msg,"\n";
88 0           return (undef, undef, undef, undef, $msg);
89             }#unless
90              
91             #Come up with a new path.
92 0           ($new_path, $event_num)=$self->_compose_new_path($year,$paths_HR,$region_code);
93              
94 0           $root_paths=$self->_root_paths($new_path);
95              
96 0 0         unless ($root_paths){
97 0           $msg="Couldn't create root directories to $new_path!";
98 0           carp $msg,"\n";
99 0           return (undef, undef, undef, undef, $msg);
100             }#unless
101              
102             #Shiny_new method only succeeds if the path doesn't already exist.
103 0           ($new_data_obj,$error)=Geo::StormTracker::Data->shiny_new($new_path, $region_code, $year, $event_num);
104              
105             #Make sure the new_data_obj is good.
106             #If it is not then sleep for 2 seconds and rerun recall this subroutine.
107 0 0         unless (defined $new_data_obj){
108 0           $counter=$self->_increment_counter($counter);
109 0 0         if ($counter >3){
110 0           $msg="Failed at adding advisory!";
111 0           carp $msg,"\n";
112 0           return (undef, undef, undef, undef, $msg);
113             }
114             else {
115 0           sleep 2;
116 0           $self->add_advisory($adv_obj,$force,$counter);
117             }
118             }#unless
119              
120             #If this is the last advisory to be issured then make the storm inactive.
121             #Use the secret 3rd argument to insert_advisory to make this happen.
122 0 0         if ($adv_obj->is_final){
123 0           ($success,$error)=$new_data_obj->insert_advisory($adv_obj,$force,1);
124             }
125             else {
126 0           ($success,$error)=$new_data_obj->insert_advisory($adv_obj,$force);
127             }
128              
129 0 0         if ($success){
130 0           ($region, $year, $event_num)=$self->_region_year_and_event_number($new_data_obj);
131 0           return ($new_data_obj, $region, $year, $event_num, $error);
132             }
133             else {
134 0           return (undef, undef, undef, $error);
135             }#if/else
136             }#if/else
137              
138             }#add_advisory
139             #---------------------------------------------------------
140             sub _region_year_and_event_number {
141 0     0     my $self=shift;
142 0           my $data_obj=shift;
143            
144 0           my ($region, $year, $event_num, $path)=undef;
145              
146 0           $path=$data_obj->get_path();
147              
148 0           $path =~ m!/(\w+)/(\d{4})/(\d+)/?$!;
149              
150 0           $region=$1;
151 0           $year=$2;
152 0           $event_num=$3;
153              
154 0 0 0       if ((defined $region) and (defined $year) and (defined $event_num)){
      0        
155 0           return ($region, $year, $event_num);
156             }
157             else {
158 0           return (undef, undef, undef);
159             }#else
160              
161             }#_year_and_event_number
162             #---------------------------------------------------------
163             #$region_exists=$self->_region_dir_exists($region_code)
164             sub _region_dir_exists {
165 0     0     my $self=shift;
166 0           my $region_code=shift;
167              
168 0           my $region_path=$self->{'base_path'}."$region_code/";
169              
170 0 0 0       if ((-e $region_path) and (-d $region_path)){
171 0           return 1;
172             }
173             else {
174 0           return 0;
175             }#if/else
176              
177             }#_region_dir_exists
178             #---------------------------------------------------------
179             #($good,$error)=$self->_create_region_dir($region_code);
180             sub _create_region_dir {
181 0     0     my $self=shift;
182 0           my $region_code=shift;
183              
184 0           my ($region_path, $success, $msg)=undef;
185            
186 0           $region_path=$self->{'base_path'}."$region_code/";
187              
188 0 0 0       if ((-e $region_path) and (-d $region_path)){
189 0           $msg="The directory $region_path already exists and will not be created by _create_region_dir!";
190 0           carp $msg,"\n";
191 0           return (undef,$msg);
192             }#if
193              
194 0           $success=mkdir($region_path,0777);
195            
196 0 0 0       if ((defined $success) and ($success)){
197 0           return (1,undef);
198             }
199             else {
200 0           $msg="The directory $region_path could not be created by _create_region_dir!";
201 0           carp $msg,"\n";
202 0           return (undef,$msg);
203             }
204             }#_create_region_dir
205             #---------------------------------------------------------
206             sub _check_region_syntax {
207 0     0     my $self=shift;
208 0           my $region_code=shift;
209              
210 0           my $msg=undef;
211              
212 0 0         unless ($region_code =~ m!^\w{2}$!) {
213 0           $msg="Region code is syntatically incorrect!";
214 0           carp $msg,"\n";
215 0           return (undef, $msg);
216             }#unless
217              
218 0           return (1, undef);
219             }#_check_region_syntax
220             #---------------------------------------------------------
221             sub _root_paths {
222 0     0     my $self=shift;
223 0           my $path=shift;
224              
225 0           my ($short_path)=undef;
226              
227             #Make sure path has trailing slash.
228 0           $path=~s!/*$!/!;
229              
230 0           $short_path=$path;
231 0           $short_path=~s!/[^/]*/$!/!;
232 0           mkpath([$short_path], 0, 0777);
233              
234 0 0 0       if ((-e $short_path) and (-d $short_path)){
235 0           return 1;
236             }
237             else {
238 0           return undef;
239             }#if/else
240              
241             }#_root_paths
242             #---------------------------------------------------------
243             sub _increment_counter {
244 0     0     my $self=shift;
245 0           my $counter=shift;
246 0 0         if (defined $counter){
247 0           $counter++;
248             }
249             else {
250 0           $counter=1;
251             }
252 0           return $counter;
253             }#_increment_counter
254             #---------------------------------------------------------
255             sub _compose_new_path {
256 0     0     my $self=shift;
257 0           my $year=shift;
258 0           my $paths_HR=shift;
259 0           my $region_code=shift;
260              
261 0           my ($last_used_path, $early_path, $last_used_year)=undef;
262 0           my ($last_used_event, $next_event, $next_path, $matches)=undef;
263 0           my @reversed_paths=();
264              
265 0 0 0       if (
      0        
266 0           (defined $paths_HR) and
267             (defined $paths_HR->{$region_code}) and
268             (scalar(@{$paths_HR->{$region_code}}) > 0)
269             ) {
270              
271 0           @reversed_paths=(reverse @{$paths_HR->{$region_code}});
  0            
272 0           $last_used_path=$reversed_paths[$#reversed_paths];
273              
274 0           $last_used_path=~ s!/*$!/!;
275 0           $matches=($last_used_path=~ m!/(\d{4})/(\d+)/$! );
276              
277 0 0 0       return (undef, undef) unless ((defined $matches) and ($matches));
278              
279 0           $last_used_year=$1;
280 0           $last_used_event=$2;
281              
282 0 0         if ($last_used_year == $year){
283 0           $next_event=$last_used_event+1;
284             }
285             else {
286 0           $next_event=1;
287             }
288             }
289             else {
290 0           $next_event=1;
291             }#if/else
292              
293 0           $next_path=$self->{'base_path'}."$region_code/$year/$next_event/";
294              
295 0           return ($next_path, $next_event);
296             }#_compose_new_path
297             #---------------------------------------------------------
298             sub _find_release_year {
299 0     0     my $self=shift;
300 0           my $adv_obj=shift;
301              
302 0           my ($release_time,$matches)=undef;
303              
304 0           $release_time=$adv_obj->release_time();
305 0           $release_time =~ s!\s*$!!;
306              
307 0           $matches=( $release_time =~ m!\s(\d{4})$! );
308              
309 0 0 0       if ((defined $matches) and ($matches)){
310 0           return $1;
311             }
312             else {
313 0           return undef;
314             }#if/else
315              
316             }#_find_release_year
317             #---------------------------------------------------------
318             #Path array must be in the same order as that returned by _all_paths_by_region method.
319             sub _recent_storms {
320 0     0     my $self=shift;
321 0           my $paths_AR=shift;
322              
323 0           my ($path,$data_obj,$adv_obj,$header_frag,$region_code,$last_digits)=undef;
324 0           my ($grep_count, $error)=undef;
325 0           my @recent_storms=();
326 0           my @recent_header_frags=();
327              
328 0 0         unless (defined @{$paths_AR}){
  0            
329 0           return (undef,undef);
330             }#unless
331              
332 0           foreach $path (reverse @{$paths_AR}){
  0            
333 0           ($data_obj,$error)=Geo::StormTracker::Data->new($path);
334 0 0         next unless (defined $data_obj);
335              
336 0           $adv_obj=$data_obj->current_advisory();
337 0 0         next unless (defined $adv_obj);
338              
339 0           ($header_frag,$region_code,$last_digits)=$self->_disect_header($adv_obj);
340 0           $grep_count=grep {$_ eq $header_frag} @recent_header_frags;
  0            
341              
342 0 0         if (!$grep_count){
343 0           push (@recent_header_frags,$header_frag);
344 0           push (@recent_storms,$data_obj);
345             }
346             else {
347 0           return (\@recent_storms, \@recent_header_frags);
348             }#if/else
349             }#foreach
350 0           return (\@recent_storms, \@recent_header_frags);
351             }#_recent_storms
352             #---------------------------------------------------------
353             sub _associate_advisory_with_storm {
354 0     0     my $self=shift;
355 0           my $target_adv_obj=shift;
356 0           my $recent_storms_AR=shift;
357 0           my $recent_frags_AR=shift;
358              
359             #$too_old and $way_too_old should be in seconds.
360             #These are delta times.
361 0           my $too_old=60*60*24*60;#60 days old
362 0           my $way_too_old=60*60*24*90;#90 days old
363              
364 0           my ($max_i, $i, $target_header_frag, $target_region_code, $target_last_digits)=undef;
365 0           my ($matched_storm_obj, $matched_current_adv_obj, $old_epoch_time, $new_epoch_time)=undef;
366 0           my ($time_delta)=undef;
367              
368 0           ( $target_header_frag, $target_region_code, $target_last_digits)=$self->_disect_header($target_adv_obj);
369              
370 0           $max_i = scalar(@{$recent_storms_AR});
  0            
371 0           for ($i=0; $i < $max_i; $i++){
372 0 0         if (@{$recent_frags_AR}[$i] eq $target_header_frag){
  0            
373 0           $matched_storm_obj=@{$recent_storms_AR}[$i];
  0            
374             #return @{$recent_storms_AR}[$i];
375             }#if
376             }#for
377              
378             #If a matching storm was found, make sure it is not an old one.
379             #If it is an old one then return undef;
380             #Otherwise return the matching storm object.
381 0 0         if ($matched_storm_obj){
382            
383 0           $matched_current_adv_obj=$matched_storm_obj->current_advisory();
384              
385             #$old_epoch_time=$self->_extract_epoch_date($matched_current_adv_obj);
386 0           $old_epoch_time=$matched_current_adv_obj->epoch_date();
387            
388             #$new_epoch_time=$self->_extract_epoch_date($target_adv_obj);
389 0           $new_epoch_time=$target_adv_obj->epoch_date();
390              
391 0 0 0       if (($old_epoch_time) and ($new_epoch_time)) {
392              
393 0           $time_delta=$new_epoch_time-$old_epoch_time;
394              
395 0 0 0       if ($time_delta >= $way_too_old){
    0          
396 0           return undef;
397             }
398             elsif (($time_delta >= $too_old) and ($target_adv_obj->advisory_number == 1)) {
399 0           return undef;
400             }
401             else {
402 0           return $matched_storm_obj;
403             }#if/elsif/else
404             }
405             else {
406 0 0 0       if (
407             ($target_adv_obj->advisory_number == 1) and
408             ($matched_current_adv_obj->advisory_number != 1)
409             ){
410 0           return undef;
411             }
412             else {
413 0           return $matched_storm_obj;
414             }#if/else
415              
416             }#if/else
417             }
418             #If a matching storm wasn't found then return undef.
419             else {
420 0           return undef;
421             }#if/else
422              
423             }#_associate_advisory_with_storm
424             #---------------------------------------------------------
425             sub _disect_header {
426 0     0     my $self=shift;
427 0           my $arg=shift;
428              
429 0           my ($wmo_header,$matches,$region_code,$last_digits,$header_frag,$msg)=undef;
430              
431 0 0         if (ref $arg){
432 0           $wmo_header=$arg->wmo_header();
433             }
434             else {
435 0           $wmo_header=$arg;
436             }
437            
438 0           $matches=($wmo_header =~ m!^(WT(\w{2})(\d{2}))\s!);
439              
440 0 0         unless ($matches){
441 0           $msg="Bad wmo header in advisory!";
442 0           $msg=" Bad advisory has wmo header of $wmo_header!";
443 0           croak $msg,"\n";
444             }
445 0           $region_code=$2;
446 0           $last_digits=$3;
447 0           $header_frag=$1;
448              
449 0           return ($header_frag,$region_code,$last_digits);
450              
451             }#_disect_header
452             #---------------------------------------------------------
453             sub specific_storm {
454 0     0 1   my $self=shift;
455 0           my $region_code=shift;
456 0           my $year=shift;
457 0           my $event_number=shift;
458            
459 0           my ($good, $msg, $data_obj, $path)=undef;
460            
461 0           ($good,$msg)=$self->_check_region_syntax($region_code);
462            
463 0 0         unless ($good){
464 0           return (undef,$msg);
465             }
466            
467 0           $path=$self->{'base_path'}."$region_code/$year/$event_number/";
468              
469             #new method will fail unless storm already exists.
470 0           ($data_obj,$msg)=Geo::StormTracker::Data->new($path);
471              
472 0           return ($data_obj,$msg);
473              
474             }#specific_storm
475             #---------------------------------------------------------
476             #Region is extracted from the abreviated WMO header
477             #last counter argument is a secret.
478             sub add_advisory_by_year_and_event {
479 0     0 1   my $self=shift;
480 0           my $adv_obj=shift;
481 0           my $year=shift;
482 0           my $event_number=shift;
483 0           my $force=shift;
484 0           my $counter=shift;
485              
486 0           my ($success, $data_obj, $header_frag, $region_code)=undef;
487 0           my ($last_digits, $good, $msg, $error, $path, $root_paths)=undef;
488              
489 0           ($header_frag,$region_code,$last_digits)=$self->_disect_header($adv_obj);
490              
491 0           ($good,$msg)=$self->_check_region_syntax($region_code);
492              
493 0 0         unless ($good){
494 0           return (undef,$msg);
495             }
496              
497             #Make Region directory if necessary
498 0 0         unless ($self->_region_dir_exists($region_code)){
499 0           ($good,$error)=$self->_create_region_dir($region_code);
500 0 0         return (undef,$error) unless ($good);
501             }#unless
502              
503 0           $path=$self->{'base_path'}."$region_code/$year/$event_number/";
504              
505             #If the path exists then this should be a pre-existing storm, so use new method.
506 0 0 0       if ((-e $path) and (-d $path)){
507 0           ($data_obj,$msg)=Geo::StormTracker::Data->new($path);
508             }
509             #If the path does not exist then this should be a brand new storm, so use shiny_new method.
510             else {
511             #Make sure all the base paths exist.
512 0           $root_paths=$self->_root_paths($path);
513 0 0         unless ($root_paths){
514 0           $msg="Couldn't create root directories to $path!";
515 0           carp $msg,"\n";
516 0           return (undef,$msg);
517             }#unless
518             #Call the shiny_new method.
519 0           ($data_obj,$msg)=Geo::StormTracker::Data->shiny_new($path, $region_code, $year, $event_number);
520             }#if/else
521              
522             #If something went wrong then try several times before failing.
523             #This will take account of two processes competing against each other.
524 0 0         unless (defined $data_obj){
525 0           $counter=$self->_increment_counter($counter);
526 0 0         if ($counter >3){
527 0           $msg .= "Failed at adding advisory!";
528 0           carp $msg,"\n";
529 0           return (undef,$msg);
530             }
531             else {
532 0           sleep 2;
533 0           $self->add_advisory_by_year_and_event($adv_obj,$year,$event_number,$force,$counter);
534             }#if/else
535             }#unless
536              
537             #If this is the last advisory to be issured then make the storm inactive.
538             #Don't go the other way though, and make it active if it isn't the last advisory.
539             #Use the secret 3rd argument to insert_advisory to make this happen.
540 0 0         if ($adv_obj->is_final){
541 0           ($success,$error)=$data_obj->insert_advisory($adv_obj,$force,1);
542             }
543             else {
544 0           ($success,$error)=$data_obj->insert_advisory($adv_obj,$force);
545             }#if/else
546              
547 0           return ($success,$error);
548              
549             }#add_advisory_by_year_and_event
550             #---------------------------------------------------------
551             sub _croak_on_bad_region_syntax {
552 0     0     my $self=shift;
553 0           my $region=shift;
554              
555 0           my $msg=undef;
556            
557 0 0 0       unless ((defined $region) and ($region =~ m!^\w{2}$!)) {
558 0           $msg="Target region code $region ";
559 0           $msg .= "is not a two alphanumeric character string!";
560 0           croak $msg,"\n";
561             }
562              
563 0           return 1;
564              
565             }#_croak_on_bad_region_syntax
566             #---------------------------------------------------------
567             #Searches base path to find every storm path.
568             #Paths are sorted lexically by region identifier and
569             #then subsorted numerically by year and advisory number.
570             #The oldest paths will first.
571             #Every directory returned will have a trailing slash.
572             #In the event that a target region has been specified then only thatd
573             #region's directory will be searched.
574             sub _all_paths_by_region {
575 0     0     my $self=shift;
576 0           my $target_region_code=shift;
577              
578 0           my ($base_path, $possible_region_dir, $region, $region_dir, $year_dir, $event_dir)=undef;
579 0           my ($target_exists, $msg, $good, $error, $path_to_match)=undef;
580 0           my @dir_list=();
581 0           my @region_dirs=();
582 0           my @event_dir_list=();
583 0           my @final_dir_list=();
584 0           my $paths_by_region_HR={};
585              
586             #Place base path in an easy to use variable.
587             #New method already insured that the base path has a trailing slash.
588 0           $base_path=$self->{'base_path'};
589              
590             #Search top level base path dir for various regions.
591 0           @dir_list=$self->_dir_listing($base_path);
592 0           foreach $possible_region_dir (@dir_list){
593 0 0         next unless $possible_region_dir =~ m!/\w{2}$!;
594 0           push (@region_dirs,$possible_region_dir);
595             }#foreach
596              
597             #Sort region directories lexically.
598 0           @region_dirs=sort _sort_dirs_lexically @region_dirs;
599              
600             #Check to see if a target region code was defined.
601             #If so then only check for that region's paths.
602             #Do this by modifying @region_dirs to only include the target region.
603 0 0         if (defined $target_region_code){
604              
605             #Check target_region syntax.
606             #_croak_on_bad_region_syntax will croak if the region fails the test.
607             #$self->_croak_on_bad_region_syntax($target_region_code);
608 0           ($good,$error)=$self->_check_region_syntax($target_region_code);
609              
610 0 0         unless ($good){
611 0           return (undef,$error);
612             }
613              
614             #Look for the target_region_code in the region directories found.
615 0           $path_to_match=$self->{'base_path'}.$target_region_code;
616 0           $target_exists=grep {$_ eq $path_to_match} @region_dirs;
  0            
617            
618 0 0         if ($target_exists){
619             #@region_dirs=($target_region_code);
620 0           @region_dirs=($path_to_match);
621             }
622             else {
623 0           $msg = "Directory for region $target_region_code was not found!";
624 0           carp $msg,"\n";
625 0           return (undef,$msg);
626             }#if/else
627              
628             }#if
629              
630             #Find every year and event in every region and make one nice big array
631             #with all paths found.
632 0           foreach $region_dir (@region_dirs) {
633              
634             #Make sure final dir list is clean.
635 0           @final_dir_list=();
636              
637             #Find every year directory for this region.
638 0           @dir_list=$self->_dir_listing($region_dir);
639              
640             #Only keep directories that look like a 4 digit year.
641 0           @dir_list=grep {m!/\d{4}$!} @dir_list; #notice nice y2k compliance
  0            
642            
643             #Sort year directories numerically
644 0           @dir_list = sort _sort_dirs_numerically @dir_list;
645              
646 0           foreach $year_dir (@dir_list){
647             #Find every weather event directory for this year.
648 0           @event_dir_list=$self->_dir_listing($year_dir);
649              
650             #Only keep directories that look like a number;
651 0           @event_dir_list=grep {m!/\d+$!} @event_dir_list;
  0            
652              
653             #Sort events numerically.
654 0           @event_dir_list=sort _sort_dirs_numerically @event_dir_list;
655              
656             #Push the event directories onto the final directory list.
657 0           push (@final_dir_list,@event_dir_list);
658             }#foreach
659              
660             #Add trailing slash to every directory in the final directory list.
661             #@final_dir_list = map {s!/*$!/!} @final_dir_list;
662 0           map {s!/*$!/!} @final_dir_list;
  0            
663              
664             #Put information into path by region hash ref.
665 0           $region_dir =~ m!/(\w{2})$!;
666 0           $region=$1;
667 0           $paths_by_region_HR->{$region}=[@final_dir_list];
668             }#foreach
669              
670 0           return ($paths_by_region_HR, undef);
671              
672             }#_all_paths_by_region
673             #---------------------------------------------------------
674             sub _sort_dirs_numerically {
675 0     0     $a =~ m!/(\d+)$!;
676 0           my $a_num = $1;
677 0           $b =~ m!/(\d+)$!;
678 0           my $b_num = $1;
679 0           return $a_num <=> $b_num;
680             }#_sort_dirs_numerically
681             #---------------------------------------------------------
682             sub _sort_dirs_lexically {
683 0     0     $a =~ m!/([^/]+)$!;
684 0           my $a_var = $1;
685 0           $b =~ m!/([^/]+)$!;
686 0           my $b_var = $1;
687 0           return $a_var cmp $b_var;
688             }#_sort_dirs_lexically
689             #---------------------------------------------------------
690             #No trailing slash on directory pathnames returned.
691             sub _dir_listing {
692 0     0     my $self=shift;
693 0           my $dir_name=shift;
694              
695 0           my ($d, $msg)=undef;
696 0           my @dir_list=();
697 0           my @dir_clean_list=();
698              
699             #Make sure $dir_name has a trailing slash.
700 0           $dir_name =~ s!/*$!/!;
701              
702             #Go find out what files are in the dir_name directory.
703 0           $d=IO::Dir->new();
704 0           $d->open($dir_name);
705 0 0         unless (defined($d)){
706 0           $msg = "Had trouble reading $dir_name directory!";
707 0           carp $msg,"\n";
708 0           return undef;
709             }
710 0           @dir_list=$d->read();
711 0           $d->close();
712              
713             #Get rid of . and .. as directory names.
714 0           @dir_clean_list=grep !/^(\.|\.\.)$/, @dir_list;
715              
716             #Make dir_clean_list array have full pathnames.
717 0           map {$_=$dir_name.$_} @dir_clean_list;
  0            
718              
719             #Weed out any files which are not directories.
720 0           @dir_list=();
721 0           @dir_list=grep {-d $_} @dir_clean_list;
  0            
722              
723 0           return @dir_list;
724             }#_dir_listing
725             #---------------------------------------------------------
726             sub all_storms_by_region {
727 0     0 1   my $self=shift;
728 0           my $target_region_code=shift;
729              
730 0           my ($data_obj,$path,$all_paths_by_region_HR)=undef;
731 0           my ($region,$data_objects_by_region_HR)=undef;
732 0           my ($good,$error)=undef;
733 0           my @all_paths=();
734 0           my @all_data_objects=();
735              
736 0 0         if (defined $target_region_code){
737              
738 0           ($good,$error)=$self->_check_region_syntax($target_region_code);
739 0 0         unless ($good){
740 0           return (undef,$error);
741             }#unless
742             }#if
743              
744 0           ($all_paths_by_region_HR,$error)=$self->_all_paths_by_region($target_region_code);
745 0 0         unless (defined $all_paths_by_region_HR){
746 0           return (undef,$error);
747             }#unless
748              
749 0           foreach $region (keys %{$all_paths_by_region_HR}) {
  0            
750              
751             #Make sure @all_data_objects is empty.
752 0           @all_data_objects=();
753              
754 0           foreach $path (@{$all_paths_by_region_HR->{$region}}){
  0            
755 0           ($data_obj,$error)=Geo::StormTracker::Data->new($path);
756 0 0         if (defined $data_obj){
757 0           push (@all_data_objects,$data_obj);
758             }
759             else {
760 0           carp $error,"\n";
761             }#if/else
762             }#foreach
763            
764 0           $data_objects_by_region_HR->{$region}=[@all_data_objects];
765              
766             }#foreach
767              
768 0           return ($data_objects_by_region_HR,undef);
769              
770             }#all_storms_by_region
771             #---------------------------------------------------------
772             sub all_storms_by_region_and_year {
773 0     0 1   my $self=shift;
774 0           my $target_region_code=shift;
775 0           my $target_year=shift;
776              
777 0           my ($data_obj,$path,$all_paths_by_region_HR)=undef;
778 0           my ($region,$data_objects_by_region_HR)=undef;
779 0           my ($good,$error, $match, $msg, $year, $event_number)=undef;
780              
781 0 0         if (defined $target_region_code){
782              
783 0           ($good,$error)=$self->_check_region_syntax($target_region_code);
784 0 0         unless ($good){
785 0           return (undef,$error);
786             }#unless
787             }#if
788              
789 0           ($all_paths_by_region_HR,$error)=$self->_all_paths_by_region($target_region_code);
790 0 0         unless (defined $all_paths_by_region_HR){
791 0           return (undef, $error);
792             }#unless
793              
794 0           foreach $region (keys %{$all_paths_by_region_HR}) {
  0            
795              
796             #Make sure @all_data_objects is empty.
797              
798 0           foreach $path (@{$all_paths_by_region_HR->{$region}}){
  0            
799              
800 0           $match=($path =~ m!/(\d{4})/(\d+)/$!);
801              
802 0 0         if (!$match){
803 0           $msg="Geo::StormTracker::Main::all_storms_by_region_and_year had trouble with a pattern match. ";
804 0           $msg.="Returned values may be in error!";
805 0           carp $msg,"\n";
806 0           next;
807             }
808             else {
809 0           $year=$1;
810 0           $event_number=$2;
811             }#if/else
812              
813 0 0         if (defined $target_year){
814 0 0         next unless ($year == $target_year);
815             }#if
816              
817 0           ($data_obj,$error)=Geo::StormTracker::Data->new($path);
818 0 0         if (defined $data_obj){
819 0 0         unless (defined $data_objects_by_region_HR->{$region}->{$year}){
820 0           $data_objects_by_region_HR->{$region}->{$year}=[];
821             }#unless
822              
823 0           push (@{$data_objects_by_region_HR->{$region}->{$year}},$data_obj);
  0            
824             }
825             else {
826 0           carp $error,"\n";
827             }#if/else
828             }#foreach
829             }#foreach
830              
831 0           return ($data_objects_by_region_HR, undef);
832              
833             }#all_storms_by_region_and_year
834             #---------------------------------------------------------
835             #Unless a target_region is specified the returned has
836             #will have a key for every region found.
837             sub all_active_storms_by_region {
838 0     0 1   my $self=shift;
839 0           my $target_region_code=shift;
840            
841 0           my ($data_obj,$path,$all_paths_by_region_HR)=undef;
842 0           my ($region,$data_objects_by_region_HR)=undef;
843 0           my ($good, $error, $is_active)=undef;
844 0           my @all_paths=();
845 0           my @all_data_objects=();
846            
847 0 0         if (defined $target_region_code){
848            
849 0           ($good,$error)=$self->_check_region_syntax($target_region_code);
850 0 0         unless ($good){
851 0           return (undef,$error);
852             }#unless
853             }#if
854            
855 0           ($all_paths_by_region_HR,$error)=$self->_all_paths_by_region($target_region_code);
856 0 0         unless (defined $all_paths_by_region_HR){
857 0           return (undef,$error);
858             }#unless
859            
860 0           foreach $region (keys %{$all_paths_by_region_HR}) {
  0            
861            
862             #Make sure @all_data_objects is empty.
863 0           @all_data_objects=();
864            
865 0           foreach $path (@{$all_paths_by_region_HR->{$region}}){
  0            
866              
867 0           ($data_obj,$error)=Geo::StormTracker::Data->new($path);
868              
869 0 0         unless (defined $data_obj){
870 0           carp $error,"\n";
871 0           next;
872             }#unless
873              
874 0           ($is_active,$error)=$data_obj->is_active();
875            
876 0 0         unless (defined $is_active){
877 0           carp $error,"\n";
878 0           next;
879             }#unless
880              
881 0 0         push (@all_data_objects,$data_obj) if ($is_active);
882              
883             }#foreach
884            
885 0           $data_objects_by_region_HR->{$region}=[@all_data_objects];
886            
887             }#foreach
888            
889 0           return ($data_objects_by_region_HR,undef);
890              
891             }#all_active_storms_by_region
892             #---------------------------------------------------------
893             #Every storm with a current advisory >= $epoch_retire
894             #and every active storm will be in the list.
895             #Unless a target_region is specified the returned hash ref
896             #will have a key for every region found.
897             #($data_HR, $error)=$main_obj->current_storms_by_region($epoch_retire, $region_code);
898             sub current_storms_by_region {
899 0     0 0   my $self=shift;
900 0           my $epoch_retire=shift;
901 0           my $target_region_code=shift;
902            
903 0           my ($data_obj,$path,$all_paths_by_region_HR)=undef;
904 0           my ($region,$data_objects_by_region_HR)=undef;
905 0           my ($good, $error, $is_active, $msg)=undef;
906 0           my ($current_adv, $epoch_date)=undef;
907 0           my @all_paths=();
908 0           my @all_data_objects=();
909            
910 0 0         if (defined $target_region_code){
911            
912 0           ($good,$error)=$self->_check_region_syntax($target_region_code);
913 0 0         unless ($good){
914 0           return (undef,$error);
915             }#unless
916             }#if
917              
918 0 0         unless (defined $epoch_retire){
919 0           $msg="Epoch retire date was not specified!";
920 0           carp $msg, "\n";
921 0           return (undef, $msg);
922             }#unless
923            
924 0           ($all_paths_by_region_HR,$error)=$self->_all_paths_by_region($target_region_code);
925 0 0         unless (defined $all_paths_by_region_HR){
926 0           return (undef,$error);
927             }#unless
928            
929 0           foreach $region (keys %{$all_paths_by_region_HR}) {
  0            
930            
931             #Make sure @all_data_objects is empty.
932 0           @all_data_objects=();
933            
934 0           foreach $path (@{$all_paths_by_region_HR->{$region}}){
  0            
935              
936 0           ($data_obj,$error)=Geo::StormTracker::Data->new($path);
937              
938 0 0         unless (defined $data_obj){
939 0           carp $error,"\n";
940 0           next;
941             }#unless
942              
943 0           ($is_active,$error)=$data_obj->is_active();
944            
945 0 0         unless (defined $is_active){
946 0           carp $error,"\n";
947 0           next;
948             }#unless
949            
950 0           $current_adv=$data_obj->current_advisory();
951              
952 0 0         unless (defined $current_adv){
953 0           carp "One of the data objects does not have any advisories!";
954 0           next;
955             }#unless
956              
957 0           $epoch_date=$current_adv->epoch_date();
958              
959 0 0         carp "Epoch date not defined for one of the advisories!" unless (defined $epoch_date);
960              
961 0 0 0       if (
      0        
962             ($is_active) or
963             ( (defined $epoch_date) and ($epoch_date >= $epoch_retire) )
964             ){
965 0           push (@all_data_objects,$data_obj);
966             }#if
967              
968             }#foreach
969            
970 0           $data_objects_by_region_HR->{$region}=[@all_data_objects];
971            
972             }#foreach
973            
974 0           return ($data_objects_by_region_HR,undef);
975              
976             }#current_storms_by_region
977             #---------------------------------------------------------
978              
979             1;
980             __END__