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

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