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

Last change on this file since 184 was 184, checked in by thauvin, 18 years ago
  • add few doc, remove useless old code
  • Property cvs2svn:cvs-rev set to 1.65
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 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($info, %param)
288
289Return a new ObsData::Repository::dir::archive object.
290
291$info is an ObsData::Repository or ObsData::Repository::dir object
292
293%param are:
294   
295    archivefile => the archive file to read
296
297=cut
298
299sub new {
300    my ($class, $infos, %param) = @_;
301
302    my $or = ObsData::Repository->new($infos, %param) or return;
303    # dir directory to parse
304    $or->{archivefile} or die "No archivefile given";
305
306    # $or->{interactive_callback}
307    $or->{_cache_checks} = {};
308   
309    $or->logging(0,
310        "Preparing to parse %s/%s",
311         $or->{dir},
312         $or->{archivefile}
313    );
314
315    bless($or, $class);
316}
317
318=head3 load_archive
319
320Try to read archive, return 1 on success
321
322=cut
323
324sub load_archive {
325    my ($self) = @_;
326    if ($self->{Oarchive}) {
327        return 1;
328    } else {
329        $self->{Oarchive} = ObsData::Archive->new("$self->{dir}/$self->{archivefile}");
330        if ($self->{Oarchive}) {
331            $self->logging(0,
332                "Archive %s/%s properly load",
333                $self->{dir}, $self->{archivefile}
334            );
335            return 1;
336        } else {
337            $self->logging(4, 
338                "Can't handle archive %s/%s: %s, skipping",
339                $self->{dir}, $self->{archivefile},
340                ObsData::Archive->error
341            );
342            return 0;
343        }
344    }
345}
346
347=head2 getvalue($var, $default)
348
349Return a value from status file related to current archive.
350
351=cut
352
353sub getvalue {
354    my ($self, $var, $default) = @_;
355    return $self->{status}->val($self->{archivefile}, $var, $default);
356}
357
358=head2 setvalue
359
360Define a value related to current archive file.
361
362=cut
363
364sub setvalue {
365    my ($self, $var, $val, $comment) = @_;
366    if (!$self->{status}->SectionExists($self->{archivefile})) {
367        $self->{status}->AddSection($self->{archivefile});
368    }
369    $self->{status}->newval($self->{archivefile}, $var, $val) if(defined($val));
370    $self->{status}->SetParameterComment(
371        $self->{archivefile}, $var, $comment
372    ) if(defined($comment));
373}
374
375=head2 get_data_value($datafile, $var, $default)
376
377Return a value from status file related to current archive about $datafile.
378
379=cut
380
381sub get_data_value {
382    my ($self, $datafile, $var, $default) = @_;
383    return $self->getvalue(
384        sprintf('data_%s_%s', $var, $datafile),
385        $var, 
386        $default
387    );
388}
389
390=head2 set_data_value($datafile, $var, $val, $comment)
391
392Set a value and optionnaly a comment for $datafile found in archive file
393
394=cut
395
396sub set_data_value {
397    my ($self, $datafile, $var, $val, $comment) = @_;
398    $self->setvalue(
399        sprintf('data_%s_%s', $var, $datafile),
400        $val,
401        $comment
402    );
403}
404
405=head3 checks
406
407=cut
408
409=head3 default_choice($oevent)
410
411=cut
412
413sub default_choice {
414    my ($self, $oevent) = @_;
415
416    for ($oevent->id) {
417        /^do_archive$/ and do {
418            return($oevent->test_result('archive_exists') ? 0 : 1);
419            last;
420        };
421        /^do_data$/ and do {
422            return 1; # TODO
423            last;
424        };
425        /^overwrite$/ and do {
426            return($oevent->test_result('dest_exists') ? 0 : 1);
427            last;
428        };
429    }
430}
431
432sub ask_user {
433    my ($self, $oevent) = @_;
434
435    $self->logging(0, "Event(%s): %s",
436        $oevent->id,
437        $oevent->message);
438    foreach ($oevent->list_test) {
439        $self->logging(0, "  (%s) %s: %s",
440            $_,
441            $oevent->test_message($_),
442            $oevent->test_result($_),
443        );
444    }
445
446    my $res =
447        $self->{interactive_callback} ?
448        $self->{interactive_callback}->($self, $oevent) :
449        $self->default_choice($oevent);
450
451    $self->logging(0, "Event(%s): Result: %s", $oevent->id, $res);
452    return($res);
453}
454
455=head3 list_archive
456
457Return archive content
458
459=cut
460
461sub list_archive {
462    my ($self) = @_;
463    $self->load_archive or return;
464   
465    return $self->{Oarchive}->ls;
466}
467
468=head3 update_status
469
470Update status information about current archive
471
472=cut
473
474sub update_status {
475    my ($self) = @_;
476    my @filestats = stat("$self->{dir}/$self->{archivefile}");
477   
478    $self->setvalue('mtime', $filestats[9]);
479    $self->setvalue('size', $filestats[7]);
480    $self->setvalue('configtime', $self->{obsdata}->config_mtime);
481}
482
483# Sub data function:
484
485
486
487=head3 get_data_dest($datafile)
488
489Return the datatype and destination for $datafile
490
491=cut
492
493sub get_data_dest {
494    my ($self, $datafile) = @_;
495
496    my ($t, $d) = $self->{obsdata}->build_dest_filename(
497        $datafile,
498        $self->{datatype}
499    );
500    $self->logging(0, "%s/%s file, datatype %s, destination %s",
501        $self->{archivefile},
502        $datafile,
503        $t || '(none)',
504        $d || '(none)',
505    );
506    if ($t) {
507        $self->set_data_value($datafile, 'type', $t);
508    }
509    return($t, $d);
510}
511
512=head3 extract_data($datafile, $datatype, $destfile)
513
514Extract data file into $destfile, $datafile is informationnal only.
515
516$datatype and $destfile are the result of L<get_data_dest>.
517
518A basic call is:
519    $o->extract_data($file, $self->get_data_dest($file));
520
521Return 1 on success
522
523=cut
524
525sub extract_data {
526    # $datatype, informationnal only here
527    my ($self, $datafile, $datatype, $destfile) = @_;
528    $self->load_archive or return 0;
529    my $ltime = time;
530
531    $self->set_data_value(
532        $datafile,
533        'processtime',
534        $ltime,
535        scalar(localtime($ltime)) .
536        ", $ObsData::VERSION: $ObsData::CVSREV, $ObsData::Repository::CVSREV",
537    );
538    $self->set_data_value(
539        $datafile,
540        'processversion',
541        $ObsData::VERSION,
542    );
543   
544    if (!$destfile) {
545        $self->logging(3,
546            "%s/%s has no destination",
547            $self->{archivefile}, $datafile,
548        );
549        return 0;
550    }
551   
552    my $oe = ObsData::Event->new('overwrite',
553        "Should I overwrite $destfile"
554    );
555
556    $oe->add_test(
557        'dest_exists',
558        -e $destfile ? ("$destfile exists", 1) : ("$destfile don't exists", 0)
559    );
560    $self->ask_user($oe) or return 0;
561   
562    $self->logging(0,
563        "Trying to extract %s from %s/%s",
564        $destfile,
565        $self->{archivefile}, $datafile
566    );
567
568    my ($dirname) = $destfile =~ m!(?:(.*)/)?(.*)$!; #! vim syntax sux
569   
570    if ($dirname && ! -d $dirname) {
571        $self->logging(1, "Creating directory %s", $dirname);
572        if ($self->{dry_run}) {
573            $self->logging(1,
574                "Testing mode, assume %s was correctly created",
575                $dirname
576            );
577        } else {
578            if( ! File::Path::mkpath($dirname)) { 
579                $self->logging(
580                    4, "Can't create directory %s", 
581                    $dirname
582                );
583                return 0;
584            }
585        }
586    }
587
588    if ($self->{dry_run}) {
589        $self->logging(2,
590            "Testing mode, %s/%s would be extracted as %s",
591            $self->{archivefile},
592            $datafile,
593            $destfile,
594        );
595    } else {
596        if ($self->{Oarchive}->extract($datafile, $destfile)) {
597            $self->set_data_value($datafile, 'dest', $destfile);
598            $self->logging(
599                2, "Extraction of %s/%s done as %s",
600                $self->{archivefile},
601                $datafile,
602                $destfile,
603            );
604        } else {
605            $self->logging(
606                4, "Can't extract %s/%s to %s: %s",
607                $self->{archivefile}, $datafile, $destfile,
608                $self->{Oarchive}->error,
609            );
610            return 0;
611        }
612    }
613   
614    return 1;
615}
616
617=head3 process
618
619Process all files in the archive
620
621=cut
622
623sub process {
624    my ($self) = @_;
625
626    foreach my $data ($self->list_archive) {
627        my $oe = ObsData::Event->new('do_data',
628            "Should I extract $data from $self->{archivefile}"
629        );
630       
631        $self->ask_user($oe) or next;
632
633        $self->extract_data($data, $self->get_data_dest($data));
634    }
635   
636    $self->update_status;
637
638    return 1;
639}
640
641=head1 AUTHOR
642
643Olivier Thauvin <olivier.thauvin@aerov.jussieu.fr>
644
645=cut
646
6471;
Note: See TracBrowser for help on using the repository browser.