source: trunk/soft/ObsData/ObsData/Repository.pm @ 165

Last change on this file since 165 was 165, checked in by thauvin, 19 years ago
  • increase verbosity of some message
  • Property cvs2svn:cvs-rev set to 1.55
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1# $Id$
2
3package ObsData::Repository;
4
5use strict;
6use warnings;
7use File::Path;
8use POSIX qw(getcwd);
9use ObsData;
10use ObsData::Archive;
11use Config::IniFiles;
12
13our $CVSID = q$Id$;
14our $CVSREV = (q$Revision$ =~ /^Revision: (.*) $/)[0];
15
16=head1 METHODS
17
18=head2 ObsData::Repository
19
20=head3 new($parent, %param)
21
22Create a new ObsData::Repository object
23
24=over 4
25
26=item $parent
27
28A ObsData::Repository to take information
29
30=item %param
31
32A list of paramter to set in new object:
33
34=over 4
35
36=item datatype
37
38If set, only this datatype will be checked
39
40=item obs
41
42The obsveratory where data come from, informationnal only
43
44=item dir
45
46The based directory where archive are located
47
48=item glob
49
50Apply this glob to search files in dir
51
52=item statusfile
53
54The file where status info should be written
55
56=item status
57
58The Config::IniFiles object
59
60=back
61
62=back
63
64=cut
65
66sub new {
67    my ($class, $parent, %param) = @_;
68
69    my $or = { %$parent };
70    foreach (keys %param) {
71        $or->{$_} = $param{$_};
72    }
73   
74    $or->{obsdata} or die "Error #1de8d015, please insult programmer";
75    $or->{dir} or die "No dir given";
76    -d $or->{dir} or return undef;
77   
78    $or->{status} ||= new Config::IniFiles(
79        -file => (($or->{statusfile} && -f $or->{statusfile}) ? $or->{statusfile} : undef),
80        # -default => '.', # Is this a good idea
81    );
82    $or->{statusfile} ||= $or->{status}->GetFileName;
83
84    bless($or, $class);
85
86    $or;
87}
88
89sub DESTROY {
90    my ($self) = @_;
91    $self->logging(0, "Destroy ObsData::Repository for %s", ref($self));
92    $self->save_status;
93}
94
95=head3 update_global_status
96
97Set standard information into status ini file
98
99=cut
100
101sub update_global_status {
102    my ($self) = @_;
103    $self->{status}->AddSection('.');
104    $self->{status}->SetSectionComment('.',
105        $ObsData::CVSID,
106        $ObsData::Repository::CVSID,
107        scalar(localtime),
108    );
109
110    my $hostname = `hostname`;
111    chomp($hostname);
112    $self->{status}->newval('.', 'hostname', $hostname);
113    $self->{status}->SetParameterComment(
114        '.',
115        'hostname',
116        'The computer host name'
117    );
118
119    $self->{status}->newval('.', 'directory', $self->{dir});
120    $self->{status}->SetParameterComment(
121        '.',
122        'directory', 
123        'Directory where archive where located'
124    );
125   
126    $self->{status}->newval('.', 'runfrom', getcwd());
127    $self->{status}->SetParameterComment(
128        '.',
129        'runfrom',
130        'The current directory'
131    );
132}
133
134=head3 save_status
135
136Save current information about parsed files into status file
137
138=cut
139
140sub save_status {
141    my ($self) = @_;
142    $self->{statusfile} or return;
143    if (!$self->{dry_run}) {
144        $self->update_global_status;
145        $self->{status}->WriteConfig($self->{statusfile});
146    }
147}
148
149=head3 dump_status($output)
150
151Dump current status file into $output, see Config::IniFiles,
152function WriteConfig
153
154=cut
155
156sub dump_status {
157    my ($self, $output) = @_;
158    $self->update_global_status;
159    $self->{status}->WriteConfig($output);
160}
161
162=head3 logging($level, $msg, ...)
163
164Pass a message for logging to ObsData Object. See L<ObsData>
165
166=cut
167
168sub logging {
169    my $self = shift;
170    $self->{obsdata}->logging(@_);
171}
172
173=head2 ObsData::Repository::dir
174
175The child class to handle directory
176
177=cut
178
179package ObsData::Repository::dir;
180
181our @ISA = qw(ObsData::Repository);
182
183=head3 new
184
185=cut
186
187sub new {
188    my ($class, $infos, %param) = @_;
189
190    my $or = ObsData::Repository->new($infos, %param) or return;
191
192    bless($or, $class);
193}
194
195=head3 findfile
196
197Return the list of files needing to be checked
198
199=cut
200
201sub findfile {
202    my ($self) = @_;
203    my @files;
204
205    if (!-d $self->{dir}) {
206        $self->logging(3,
207            "directory %s does not exists",
208            $self->{dir},
209        );
210        return;
211    }
212
213    my $patern = $self->{patern} || '*';
214    foreach my $f (glob("$self->{dir}/$patern")) {
215
216        -f $f or next; # skip no regular files
217        $f =~ m/obsdata\.ini$/ and next; # avoid default status file
218       
219        my $bf = $f;
220        $bf =~ s!^\Q$self->{dir}\E/*!!;
221        push(@files, $bf);
222    }
223    @files
224}
225
226=head3 process
227
228Process all files in directory
229
230=cut
231
232sub process {
233    my ($self) = @_;
234    $self->logging(0, "%s() start for %s", (caller(0))[3], $self->{dir});
235
236    foreach my $file ($self->findfile) {
237        my $orda = ObsData::Repository::dir::archive->new(
238            $self,
239            archivefile => $file,
240        );
241        if(!$orda->do_continue('do_archive')) {
242            $self->logging(0,
243                "Archive %s/%s allready parsed, skipping",
244                $self->{dir},
245                $file
246            );
247            next;
248        }
249        $orda->process;
250    }
251}
252
253=head2 ObsData::Repository::dir::archive
254
255Object to handle archive and to dispatch data
256
257=cut
258
259package ObsData::Repository::dir::archive;
260
261our @ISA = qw(ObsData::Repository);
262
263=head3 new
264
265=cut
266
267sub new {
268    my ($class, $infos, %param) = @_;
269
270    my $or = ObsData::Repository->new($infos, %param) or return;
271    # dir directory to parse
272    $or->{archivefile} or die "No archivefile given";
273
274    # $or->{interative_callback}
275    $or->{_cache_checks} = {};
276   
277    $or->logging(0,
278        "Preparing to parse %s/%s",
279         $or->{dir},
280         $or->{archivefile}
281    );
282
283    bless($or, $class);
284}
285
286=head3 load_archive
287
288Try to read archive, return 1 on success
289
290=cut
291
292sub load_archive {
293    my ($self) = @_;
294    if ($self->{Oarchive}) {
295        return 1;
296    } else {
297        $self->{Oarchive} = ObsData::Archive->new("$self->{dir}/$self->{archivefile}");
298        if ($self->{Oarchive}) {
299            $self->logging(0,
300                "Archive %s/%s properly load",
301                $self->{dir}, $self->{archivefile}
302            );
303            return 1;
304        } else {
305            $self->logging(4, 
306                "Can't handle archive %s/%s: %s, skipping",
307                $self->{dir}, $self->{archivefile},
308                ObsData::Archive->error
309            );
310            return 0;
311        }
312    }
313}
314
315=head2 getvalue
316
317=cut
318
319sub getvalue {
320    my ($self, $var, $default) = @_;
321    return $self->{status}->val($self->{archivefile}, $var, $default);
322}
323
324=head2 setvalue
325
326=cut
327
328sub setvalue {
329    my ($self, $var, $val, $comment) = @_;
330    if (!$self->{status}->SectionExists($self->{archivefile})) {
331        $self->{status}->AddSection($self->{archivefile});
332    }
333    $self->{status}->newval($self->{archivefile}, $var, $val) if(defined($val));
334    $self->{status}->SetParameterComment(
335        $self->{archivefile}, $var, $comment
336    ) if(defined($comment));
337}
338
339=head2 get_data_value
340
341=cut
342
343sub get_data_value {
344    my ($self, $datafile, $var, $default) = @_;
345    return $self->getvalue(
346        sprintf('data_%s_%s', $var, $datafile),
347        $var, 
348        $default
349    );
350}
351
352=head2 set_data_value
353
354=cut
355
356sub set_data_value {
357    my ($self, $datafile, $var, $val, $comment) = @_;
358    $self->setvalue(
359        sprintf('data_%s_%s', $var, $datafile),
360        $val,
361        $comment
362    );
363}
364
365=head3 checks
366
367=cut
368
369my $checks = {
370    archive_exists => sub {
371        my ($self) = @_;
372        my $res;
373        if($self->{status}->SectionExists($self->{archivefile})) {
374            $res = 1;
375        } else {
376            $res = 0;
377        }
378        return(
379            $res, 
380            $self->logging(0,
381                "%s/%s is%s new archive",
382                $self->{dir},
383                $self->{archivefile},
384                $res ? " not" : "",
385            )
386        );     
387    },
388
389    archive_size => sub {
390        my ($self) = @_;
391        my $res;
392        if (defined(my $oldsize = $self->getvalue('size')) &&
393            defined(my $size = (stat("$self->{dir}/$self->{archivefile}"))[7])) {
394            my $res = $size <=> $oldsize;
395            return $res, $self->logging(0,
396                "Size for %s/%s %s",
397                $self->{dir},
398                $self->{archivefile},
399                $res == 0 ?
400                    sprintf("does not differ (%d)", $size) :
401                    sprintf("differ %d => %d", $oldsize, $size)
402            );
403        }
404    },
405
406    archive_mtime => sub {
407        my ($self) = @_;
408        my $res;
409        if (defined(my $oldmtime = $self->getvalue('mtime')) &&
410            defined(my $mtime = (stat("$self->{dir}/$self->{archivefile}"))[9])) {
411            my $res = $mtime <=> $oldmtime;
412            return $res, $self->logging(0,
413                "Mtime for %s/%s %s",
414                $self->{dir},
415                $self->{archivefile},
416                $res == 0 ?
417                    sprintf("does not differ (%d)", $mtime) :
418                    sprintf("differ %d => %d", $oldmtime, $mtime)
419            );
420        }
421    },
422
423    dest_exists => sub {
424        my ($self, $check, $datafile, $dest) = @_;
425        if (-e $dest) {
426            return 1, "$dest exists";
427        } else {
428            return 0, "$dest don't exists";
429        }
430    },
431
432    data_extracted => sub {
433        my ($self, $check, $datafile) = @_;
434        my $last_process = $self->get_data_value($datafile, 'processtime');
435        if ($last_process) {
436            $self->logging(0, "%s/%s has been already processed at %s",
437                $self->{archivefile},
438                $datafile,
439                scalar(localtime($last_process)),
440            );
441        }
442        $last_process
443    },
444};
445
446#sub DESTROY {
447#    my ($self) = @_;
448#    $self->logging(0, "Destroy ObsData::Repository::archive for %s", ref($self));
449#    $self->SUPER::DESTROY();
450#}
451
452=head3 do_check($check, @args)
453
454Perform $check with @args if exists and return result of check
455
456=cut
457
458sub do_check {
459    my ($self, $check, @args) = @_;
460   
461    if (!$checks->{$check}) {
462        die $self->logging(5,
463            "check %s does not exists, exiting now",
464            $check
465        );
466    }
467   
468    return $checks->{$check}->($self, $check, @args);
469}
470
471=head3 default_checks($why, @args)
472
473Perform defaults checks
474
475=cut
476
477sub default_checks {
478    my ($self, $why, @args) = @_;
479   
480    my ($code, $text);
481   
482    if ($why eq 'do_archive') {
483        ($code, $text) = $self->do_check('archive_size');
484        if ($code) {
485            $self->logging(3, $text);
486        }
487
488        ($code, $text) = $self->do_check('archive_mtime');
489        if ($code) {
490            $self->logging(3, $text);
491        }
492       
493        ($code, $text) = $self->do_check('archive_exists');
494        if ($code != 0) {
495            $self->logging(0, "%s => skiping", $text);
496            return 0;
497        }
498    } elsif ($why eq 'do_data') {
499   
500    } elsif ($why eq 'overwrite') {
501        ($code, $text) = $self->do_check('dest_exists', @args);
502        if ($code) {
503            $self->logging(0, "%s", $text);
504            return 0;
505        } else {
506            $self->logging(0, "%s", $text);
507            return 1;
508        }
509    }
510   
511    1;
512}
513
514=head3 do_continue($why, @args)
515
516Perform default or call the interactive callback.
517
518    $why is the reason of the call
519    @args are argument to passed to callback
520
521=cut
522
523sub do_continue {
524    my ($self, $why, @args) = @_;
525
526    if ($self->{interative_callback}) {
527        return $self->{interative_callback}->($self, $why, @args);
528    } else {
529        return $self->default_checks($why, @args);
530    }
531}
532
533=head3 list_archive
534
535Return archive content
536
537=cut
538
539sub list_archive {
540    my ($self) = @_;
541    $self->load_archive or return;
542   
543    return $self->{Oarchive}->ls;
544}
545
546=head3 update_status
547
548Update status information about current archive
549
550=cut
551
552sub update_status {
553    my ($self) = @_;
554    my @filestats = stat("$self->{dir}/$self->{archivefile}");
555   
556    $self->setvalue('mtime', $filestats[9]);
557    $self->setvalue('size', $filestats[7]);
558    $self->setvalue('configtime', $self->{obsdata}->config_mtime);
559}
560
561# Sub data function:
562
563
564
565=head3 get_data_dest($datafile)
566
567Return the datatype and destination for $datafile
568
569=cut
570
571sub get_data_dest {
572    my ($self, $datafile) = @_;
573
574    my ($t, $d) = $self->{obsdata}->build_dest_filename(
575        $datafile,
576        $self->{datatype}
577    );
578    $self->logging(0, "%s/%s file, datatype %s, destination %s",
579        $self->{archivefile},
580        $datafile,
581        $t || '(none)',
582        $d || '(none)',
583    );
584    if ($t) {
585        $self->set_data_value($datafile, 'type', $t);
586    }
587    return($t, $d);
588}
589
590=head3 extract_data($datafile, $datatype, $destfile)
591
592Extract data file into $destfile, $datafile is informationnal only.
593
594$datatype and $destfile are the result of L<get_data_dest>.
595
596A basic call is:
597    $o->extract_data($file, $self->get_data_dest($file));
598
599Return 1 on success
600
601=cut
602
603sub extract_data {
604    # $datatype, informationnal only here
605    my ($self, $datafile, $datatype, $destfile) = @_;
606    $self->load_archive or return 0;
607    my $ltime = time;
608
609    $self->set_data_value(
610        $datafile,
611        'processtime',
612        $ltime,
613        scalar(localtime($ltime)) .
614        ", $ObsData::VERSION: $ObsData::CVSREV, $ObsData::Repository::CVSREV",
615    );
616    $self->set_data_value(
617        $datafile,
618        'processversion',
619        $ObsData::VERSION,
620    );
621   
622    if (!$destfile) {
623        $self->logging(3,
624            "%s/%s has no destination",
625            $self->{archivefile}, $datafile,
626        );
627        return 0;
628    }
629   
630    if(!$self->do_continue('overwrite', $datafile, $destfile)) {
631        $self->logging(3,
632            "%s extracted from %s/%s already exists, I won't overwrite it",
633            $destfile,
634            $self->{archivefile}, $datafile,
635        );
636        return 0;
637    }
638   
639    $self->logging(0,
640        "Trying to extract %s from %s/%s",
641        $destfile,
642        $self->{archivefile}, $datafile
643    );
644
645    my ($dirname) = $destfile =~ m!(?:(.*)/)?(.*)$!; #! vim syntax sux
646   
647    if ($dirname && ! -d $dirname) {
648        $self->logging(1, "Creating directory %s", $dirname);
649        if ($self->{dry_run}) {
650            $self->logging(1,
651                "Testing mode, assume %s was correctly created",
652                $dirname
653            );
654        } else {
655            if( ! File::Path::mkpath($dirname)) { 
656                $self->logging(
657                    4, "Can't create directory %s", 
658                    $dirname
659                );
660                return 0;
661            }
662        }
663    }
664
665    if ($self->{dry_run}) {
666        $self->logging(2,
667            "Testing mode, %s/%s would be extracted as %s",
668            $self->{archivefile},
669            $datafile,
670            $destfile,
671        );
672    } else {
673        if ($self->{Oarchive}->extract($datafile, $destfile)) {
674            $self->set_data_value($datafile, 'dest', $destfile);
675            $self->logging(
676                2, "Extraction of %s/%s done as %s",
677                $self->{archivefile},
678                $datafile,
679                $destfile,
680            );
681        } else {
682            $self->logging(
683                4, "Can't extract %s/%s to %s: %s",
684                $self->{archivefile}, $datafile, $destfile,
685                $self->{Oarchive}->error,
686            );
687            return 0;
688        }
689    }
690   
691    return 1;
692}
693
694=head3 process
695
696Process all files in the archive
697
698=cut
699
700sub process {
701    my ($self) = @_;
702
703    foreach my $data ($self->list_archive) {
704        if (!$self->do_continue('do_data', $data)) {
705            $self->logging(0,
706                "%s/%s has allready processed, skipping",
707                $self->{archivefile},
708                $data
709            );
710            next;
711        }
712        $self->extract_data($data, $self->get_data_dest($data));
713    }
714   
715    $self->update_status;
716
717    return 1;
718}
719
720=head1 AUTHOR
721
722Olivier Thauvin <olivier.thauvin@aerov.jussieu.fr>
723
724=cut
725
7261;
Note: See TracBrowser for help on using the repository browser.