root/main/trunk/greenstone2/perllib/oaiinfo.pm @ 31723

Revision 31723, 23.3 KB (checked in by ak19, 2 years ago)

Minor changes to comments.

Line 
1# This class based on arcinfo.pm
2package oaiinfo;
3
4use constant INFO_STATUS_INDEX  => 0;
5use constant INFO_TIMESTAMP_INDEX => 1;
6use constant INFO_DATESTAMP_INDEX => 2;
7
8use strict;
9
10use arcinfo;
11use dbutil;
12
13# Store timestamp in 2 formats: internal and external (same as oailastmodified and oailastmodifieddate)
14# These times indicate the last modified date for that document. In the case of the doc being deleted,
15# it's the time the doc was deleted.
16
17# File format read in: OID <tab> (Deletion-)Status <tab> Timestamp <tab> Datestamp
18
19# Deletion status can be:
20#  E = Doc with OID exists (has not been deleted from collection). Timestamp indicates last time of build
21#  D = Doc with OID has been deleted. Timestamp indicates time of deletion
22#  PD = Provisionally Deleted. The associated timestamps are momentarily unaltered.
23
24# oaidb is "always incremental": always reflects the I/B/R/D status of archive info db,
25# before the indexing step of the build phase that alters the I/B/R/D contents of archive info db.
26# (I=index, B=been indexed, R=reindex; D=delete)
27
28sub new {
29    my $class = shift(@_);
30    my ($config_filename, $infodbtype, $verbosity) = @_;
31 
32    my $self = {
33    'verbosity' => $verbosity || 0,
34    'verbosity_threshold' => 5, # start printing debugging info from verbosity >= threshold
35    'info'=>{} # map of {OID, array[deletion-status,timestamp,datestamp]} pairs
36    };
37   
38    if(!defined $infodbtype) {
39    $infodbtype = &dbutil::get_default_infodb_type();
40    }
41    $infodbtype = "gdbm" if ($infodbtype eq "gdbm-txtgz");
42    $self->{'infodbtype'} = $infodbtype;
43
44    # Create and store the db filenames we'll be working with (tmp and livedb)
45    my $etc_dir = &util::get_parent_folder($config_filename);
46
47    my $perform_firsttime_init = 0;
48    $self->{'oaidb_live_filepath'} = &dbutil::get_infodb_file_path($infodbtype, "oai-inf", $etc_dir, $perform_firsttime_init);
49    $self->{'oaidb_tmp_filepath'} = &dbutil::get_infodb_file_path($infodbtype, "oai-inf-tmp", $etc_dir, $perform_firsttime_init);
50    $self->{'etc_dir'} = $etc_dir;
51#    print STDERR "############ LIVE DB: $self->{'oaidb_live_filepath'}\n" if $self->{'verbosity'} >= $self->{'verbosity_threshold'};
52#    print STDERR "############ TMP DB: $self->{'oaidb_tmp_filepath'}\n" if $self->{'verbosity'} >= $self->{'verbosity_threshold'};
53
54    $self->{'oaidb_file_path'} = $self->{'oaidb_tmp_filepath'}; # db file we're working with
55
56    return bless $self, $class;
57}
58
59# this subroutine will work out the starting contents of the tmp-db (temporary oai db):
60# whether it should start off empty, or with the contents of any existing live-db,
61# or with the contents of any existing tmp-db.
62sub init_tmpdb {
63    my $self = shift(@_);
64    my ($removeold, $have_manifest) = @_;
65
66    # if we have a manifest file, then we pretend we are fully incremental for oaiinfo db.
67    # removeold implies proper full-rebuild, whereas keepold or incremental means incremental
68    if($have_manifest) { # if we have a manifest file, we're not doing removeold/full-rebuild either
69    $removeold = 0;
70    }
71
72    my $do_pd_step = ($removeold) ? 1 : 0;
73       # if $removeold, then proper full rebuild, will carry out step where all E will be marked as PD
74       # else some kind of incremental build, won't do the extra PD pass
75       # which is the step marking existing OIDs (E) as PD (provisionally deleted) 
76   
77    my $oaidb_live_filepath = $self->{'oaidb_live_filepath'};
78    my $oaidb_tmp_filepath = $self->{'oaidb_tmp_filepath'};
79    my $infodbtype = $self->{'infodbtype'};
80    # Note: the live db can only exist if the collection has been activated at least once before
81    my $livedb_exists = &FileUtils::fileExists($oaidb_live_filepath);
82    my $tmpdb_exists = &FileUtils::fileExists($oaidb_tmp_filepath);   
83
84    my $initdb = 0;
85   
86    # work out what operation we need to do
87    #    work with empty tmpdb
88    #    copy_livedb_to_tmpdb
89    #    work with existing tmpdb (so existing tmpdb will be topped up)
90
91    # make_contents_of_tmpdb_empty
92    # make_contents_of_tmpdb_that_of_livedb
93    # continue_working_with_tmpdb ("contents_of_tmpdb_is_tmpdb")
94
95    # We're going to prepare the starting state of tmpdb next.
96    # It can start off empty, start off with the contents of livedb, or it can start off with the contents
97    # of the existing tmp db. Which of these three it is depends on the 3 factors: whether livedb exists,
98    # whether tmpdb exists and whether or not removeold is true.
99    # i.o.w. which of the 3 outcomes it is depends on the truth table built on the following 3 variables:
100    #   LDB = LiveDB exists
101    #   TDB = TmpDB exists
102    #   RO = Removeold
103    # OUTCOMES:
104    #   clean slate (create an empty tmpdb/make tmpdb empty)
105    #   top up tmpDB (work with existing tmpdb)
106    #   copy LiveDB to TmpDB (liveDB's contents become the contents of TmpDB, and we'll work with that)
107    #
108    # TRUTH TABLE:
109    # ---------------------------------------
110    # LDB TDB  RO | Outcome
111    # ---------------------------------------
112    #  0   0   0  | clean-slate
113    #  0   0   1  | clean-slate
114    #  0   1   0  | top-up-tmpdb
115    #  0   1   1  | erase tmpdb, clean-slate
116    #  1   0   0  | copy livedb to tmpdb
117    #  1   0   1  | copy livedb to tmpdb
118    #  1   1   0  | top-up-tmpdb
119    #  1   1   1  | copy livedb to tmpd
120    # ---------------------------------------
121    #
122    # Dr Bainbridge worked out using Karnaugh maps that, from the above truth table:
123    # => clean-slate/empty-tmpdb = !LDB && (RO || !TDB)
124    # => top-up-tmpdb/work-with-existing-tmpdb = !RO && TDB
125    # => copy-livedb-to-tmpdb = LDB && (!TDB || RO)
126    # I had most of these tests, except that I hadn't (yet) merged the two clean slate instances
127    # of first-build-ever and make-contents-of-tmpdb-empty
128
129    #my $first_build_ever = (!$livedb_exists && !$tmpdb_exists);
130    #my $make_contents_of_tmpdb_empty = (!$livedb_exists && $tmpdb_exists && $removeold);
131    # Karnaugh map allows merging $first_build_ever and $make_contents_of_tmpdb_empty above
132    # into: my $work_with_empty_tmpdb = (!$livedb_exists && (!$tmpdb_exists || $removeold));
133    my $work_with_empty_tmpdb = (!$livedb_exists && (!$tmpdb_exists || $removeold));
134    my $make_contents_of_tmpdb_that_of_livedb = ($livedb_exists && (!$tmpdb_exists || $removeold));
135    my $work_with_existing_tmpdb = ($tmpdb_exists && !$removeold);
136
137    if($work_with_empty_tmpdb) { # we'll use an empty tmpdb
138
139    # If importing the collection for the very first time, neither db exists,
140    # so create an empty tmpdb.
141    #
142    # We also create an empty tmpdb when livedb doesn't exist and $removeold is true.
143    # This can happen if we've never run activate (so no livedb),
144    # yet had done some import (and perhaps building) followed by a full re-import now.
145    # Since there was no activate and we're doing a removeold/full-rebuild now, can just
146    # work with a new tmpdb, even though one already existed, its contents can be wiped out.
147        # In such a scenario, we'll be deleting tmpdb. Then there  will be no livedb nor any tmpdb
148    # any more, so same situation as if importing the very first time when no oaidb exists either.
149
150    &dbutil::remove_db_file($self->{'infodbtype'}, $oaidb_tmp_filepath) if $tmpdb_exists; # remove the db file and any assoc files
151    $initdb = 1; # new tmpdb
152   
153    # if the oai db is created the first time, it's like incremental and
154    # "keepold" (keepold means "only add, don't reprocess existing"). So
155    # no need to do the special passes dealing with "provisional deletes".
156    $do_pd_step = 0;
157   
158    } elsif ($make_contents_of_tmpdb_that_of_livedb) {
159
160    # If the livedb exists and we're doing a full rebuild ($removeold is true),
161    # copy livedb to tmp regardless of if tmpdb already exists.
162    # Or if the livedb exists and tmpdb doesn't exist, it doesn't matter
163    # if we're incremental or not: also copy live to tmp and work with tmp.
164   
165    # copy livedb to tmpdb
166    &dbutil::remove_db_file($self->{'infodbtype'}, $oaidb_tmp_filepath) if $tmpdb_exists; # remove the db file and any assoc files
167    &FileUtils::copyFiles($oaidb_live_filepath, $oaidb_tmp_filepath);
168   
169    $initdb = 0; # tmpdb exists, since we just copied livedb to tmpdb, so no need to init existing tmpdb
170
171    } else { # $work_with_existing_tmpdb, so we'll build on top of what's presently already in tmpdb
172         # (we'll be topping up the current tmpdb)
173
174    # !$removeold, meaning incremental
175    # If incremental and have a tmpdb already, regardless of whether livedb exists,
176    # then work with the existing tmpdb file, as this means we've been
177    # importing (perhaps followed by building) repeatedly without activating the
178    # last time but want to maintain the (incremental) changes in tmpdb.       
179     
180    $initdb = 0;
181
182    } # Dr Bainbridge drew up Karnaugh maps on the truth table, which proved that all cases
183                    # are indeed covered above, so don't need any other catch-all else here
184
185    $self->{'oaidb_file_path'} = &dbutil::get_infodb_file_path($infodbtype, "oai-inf-tmp", $self->{'etc_dir'}, $initdb);
186                                 # final param follows jmt's $perform_firsttime_init in inexport.pm
187
188#    print STDERR "@@@@@ oaidb: $self->{'oaidb_file_path'}\n" if $self->{'verbosity'} >= $self->{'verbosity_threshold'};
189   
190    return $do_pd_step;
191}
192
193sub get_filepath {
194    my $self = shift (@_);
195    return $self->{'oaidb_file_path'};
196}
197
198sub import_stage {
199    my $self = shift (@_);
200    my ($removeold, $have_manifest) = @_;
201   
202    my $do_pd_step = $self->init_tmpdb($removeold, $have_manifest);
203      # returns 1 if the step to mark oaidb entries as PD is required
204      # if we're doing full rebuilding and it's NOT the first time creating the oai_inf db,
205      # then the tasks to do with PD (provisionally deleted) OAI OIDs should be carried out
206
207    $self->load_info();
208    $self->print_info(); # DEBUGGING
209
210    if ($do_pd_step) {
211    $self->mark_all_existing_as_provisionallydeleted();
212    $self->print_info(); # DEBUGGING
213
214    # save to db file now that we're done
215    $self->save_info();
216    }
217
218}
219
220sub building_stage_before_indexing() {
221    my $self = shift (@_);   
222    my ($archivedir) = @_;
223
224    # load archive info db into memory
225    my $arcinfo_doc_filename = &dbutil::get_infodb_file_path($self->{'infodbtype'}, "archiveinf-doc", $archivedir);
226    my $arcinfo_src_filename = &dbutil::get_infodb_file_path($self->{'infodbtype'}, "archiveinf-src", $archivedir);
227    my $archive_info = new arcinfo ($self->{'infodbtype'});
228    $archive_info->load_info ($arcinfo_doc_filename);
229
230    #my $started_from_scratch = &FileUtils::fileTest($self->{'oaidb_tmp_filepath'}, '-z'); # 1 if tmpdb is empty
231        # -z test for file is empty http://www.perlmonks.org/?node_id=927447
232   
233    # load the oaidb file's contents into memory.
234    $self->load_info();
235    $self->print_info(); # DEBUGGING
236
237    # process all the index, reindex and delete operations as indicated in arcinfo,
238    # all the while ensuring all PDs are changed back to E for OIDs that exist in both arcinfo and oaiinfo db. 
239
240    my $arcinfo_map = $archive_info->{'info'};
241
242    foreach my $OID (keys %$arcinfo_map) {
243    my $arcinf_tuple = $archive_info->{'info'}->{$OID};
244    my $indexing_status = $arcinf_tuple->[arcinfo::INFO_STATUS_INDEX];
245                 # use packageName::constant to refer to constants declared in another package,
246                 # see http://perldoc.perl.org/constant.html
247
248    print STDERR "######## OID: $OID - status: $indexing_status\n" if $self->{'verbosity'} >= $self->{'verbosity_threshold'};
249
250    if($indexing_status eq "I") {
251        $self->index($OID); # add new as E with current timestamp/or set existing as E with orig timestamp
252    } elsif($indexing_status eq "R") {
253        $self->reindex($OID); # update timestamp and ensure marked as E (if oid doesn't exist, add new)
254    } elsif($indexing_status eq "D") {
255        $self->delete($OID); # set as D with current timestamp
256    } elsif($indexing_status eq "B") { # B for "been indexed"
257        $self->been_indexed($OID); # will flip any PD to E if oid exists, else will add new entry for oid
258        # A new entry may be required if the collection had been built prior to turning this into
259        # an oaicollection. But what if we always maintain an oaidb? Still call $self->index() here.
260    } else {
261        if ($self->{'verbosity'} >= $self->{'verbosity_threshold'}) {
262            print STDERR "### oaiinfo::building_stage_before_indexing(): Unrecognised indexing status $indexing_status\n";
263        }
264    }
265    }
266
267    # once all docs processed, go through oaiiinfo db changing any PDs to D along with current timestamp
268    # to indicate that they're deleted
269    $self->mark_all_provisionallydeleted_as_deleted();
270    $self->print_info();
271   
272    # let's save to db file now that we're done
273    $self->save_info();
274   
275}
276
277sub activate_collection { # move tmp db to live db
278    my $self = shift (@_);
279
280    my $oaidb_live_filepath =  $self->{'oaidb_live_filepath'};
281    my $oaidb_tmp_filepath = $self->{'oaidb_tmp_filepath'};
282
283    my $livedb_exists = &FileUtils::fileExists($oaidb_live_filepath);
284    my $tmpdb_exists = &FileUtils::fileExists($oaidb_tmp_filepath);
285
286    if($tmpdb_exists) {
287    if($livedb_exists) {
288        #&dbutil::remove_db_file($self->{'infodbtype'}, $oaidb_live_filepath); # remove the db file and any assoc files
289        &dbutil::rename_db_file_to($self->{'infodbtype'}, $oaidb_live_filepath, $oaidb_live_filepath.".bak"); # rename the db file and any assoc files
290    }
291    #&FileUtils::moveFiles($oaidb_tmp_filepath, $oaidb_live_filepath);
292    &dbutil::rename_db_file_to($self->{'infodbtype'}, $oaidb_tmp_filepath, $oaidb_live_filepath); # rename the db file and any assoc files
293
294    if ($self->{'verbosity'} >= $self->{'verbosity_threshold'}) {
295        print STDERR "#### Should now have MOVED $self->{'oaidb_tmp_filepath'} to $self->{'oaidb_live_filepath'}\n";
296    }
297   
298    } else {
299        if ($self->{'verbosity'} >= $self->{'verbosity_threshold'}) {
300            print STDERR "@@@@@ In oaiinfo::activate_collection():\n";
301            print STDERR "@@@@@   No tmpdb at $self->{'oaidb_tmp_filepath'}\n";
302            print STDERR "@@@@@   to make 'live' by moving to $self->{'oaidb_live_filepath'}.\n";
303        }
304    }
305}
306
307##################### SPECIFIC TO PD-STEP ####################
308
309
310# mark all existing, E (non-deleted) OIDs as Provisionally Deleted (PD)
311# this subroutine doesn't save to oai-inf.DB
312# the caller should call save_info when they want to save to the db
313sub mark_all_existing_as_provisionallydeleted {
314    my $self = shift (@_);
315   
316    print STDERR "@@@@@ oaiinfo::mark_all_E_as_PD(): Marking the E entries as PD\n" if $self->{'verbosity'} >= $self->{'verbosity_threshold'};
317
318    my $infomap = $self->{'info'};
319
320    foreach my $OID (keys %$infomap) { # Mac Mountain Lion wants %$map, won't accept %$self->{'info'}
321    my $OID_info = $self->{'info'}->{$OID};
322    my $curr_status = $OID_info->[INFO_STATUS_INDEX];
323    if($curr_status eq "E") {       
324        $OID_info->[INFO_STATUS_INDEX] = "PD";
325    }
326    }
327}
328
329# mark all OIDs that are Provisionally Deleted (PD) as deleted, and set to current timestamp
330# To be called at end of build. Again, the caller should save to DB by calling save_info.
331sub mark_all_provisionallydeleted_as_deleted {
332    my $self = shift (@_);
333   
334    print STDERR "@@@@@ oaiinfo::mark_all_PD_as_D(): Marking the PD entries as D\n" if $self->{'verbosity'} >= $self->{'verbosity_threshold'};
335
336    my $infomap = $self->{'info'};
337
338    foreach my $OID (keys %$infomap) {
339    my $OID_info = $self->{'info'}->{$OID};
340    my $curr_status = $OID_info->[INFO_STATUS_INDEX];
341    if($curr_status eq "PD") {
342        $self->set_info($OID, "D", $self->get_current_time());
343    }
344    }
345}
346
347
348##################### GENERAL, NOT SPECIFIC TO PD-STEP ####################
349
350sub print_info {
351    my $self = shift (@_);
352
353    if ($self->{'verbosity'} < $self->{'verbosity_threshold'}) {
354        return;
355    }   
356   
357    print STDERR "###########################################################\n";
358    print STDERR "@@@@@ oaiinfo::print_info(): oaidb in memory contains: \n";
359   
360    my $infomap = $self->{'info'};
361
362    foreach my $OID (keys %$infomap) {
363    print STDERR "OID: $OID";
364    print STDERR " status: " . $self->{'info'}->{$OID}->[INFO_STATUS_INDEX];
365    print STDERR " time: " . $self->{'info'}->{$OID}->[INFO_TIMESTAMP_INDEX];
366    print STDERR " date: " . $self->{'info'}->{$OID}->[INFO_DATESTAMP_INDEX];
367    print STDERR "\n";
368    }
369
370    print STDERR "###########################################################\n";
371}
372
373
374# Find the OID, if it exists, make its status=E for existing. Leave its timestamp alone.
375# If the OID doesn't yet exist, add it as a new entry with status=E and with current timestamp.
376sub index { # Add a new oid with current time and E. If the oid was already present, mark as E
377    my $self = shift (@_);
378    my ($OID) = @_;
379   
380    my $OID_info = $self->{'info'}->{$OID};
381   
382    if (defined $OID_info) { # if OID is present, this will change status back to E, timestamp unchanged
383    $OID_info->[INFO_STATUS_INDEX] = "E";
384   
385    } else { # if OID is not present, then it's now added as existing from current time on
386    $self->set_info($OID, "E", $self->get_current_time());
387    }
388}
389
390# Upon reindexing a document with identifier OID, change its timestamp to current time
391# if a new OID, then add as new entry with status=E and current timestamp
392sub reindex { # update timestamp if oid is already present, if not (unlikely), add as new
393    my $self = shift (@_);
394    my ($OID) = @_;
395
396    my $OID_info = $self->{'info'}->{$OID};   
397    $self->set_info($OID, "E", $self->get_current_time()); # Takes care of 3 things:
398       # if OID exists, updates modified time to indicate the doc has been reindexed
399       # if OID exists, ensures any status=PD is flipped back to E for this OID doc (as we know it exists);
400       # if the OID doesn't yet exist, adds a new OID entry with status=E and current timestamp.
401
402}
403
404# Does the same as index():
405# OIDs that have been indexed upon rebuild may still be new to the oaidb: GS2 collections
406# are not OAI collections by default, unlike GS3 collections. Imagine rebuilding a (GS2) collection
407# 5 times and then setting them to be an OAI collection. In that case, the doc OIDs in the collection
408# may not be in the oaidb yet. Unless, we decide (as is the present case) to always maintain an oaidb
409# (always creating an oaidb regardless of whether the collection has OAI support turned on or not).
410sub been_indexed {
411    my $self = shift (@_);
412    my ($OID) = @_;
413
414    $self->index($OID);
415}
416
417# Upon deleting a document with identifier OID,
418# set status to deleted and change its timestamp to current time
419sub delete {
420    my $self = shift (@_);
421    my ($OID) = @_;
422
423    # the following method will set to current time if no timestamp provided,
424    # But by explicit here, the code is easier to follow
425    $self->set_info($OID, "D", $self->get_current_time());
426
427}
428
429#############################################################
430sub get_current_time {
431    my $self = shift (@_);
432    return time; # current time
433
434    # localtime(time) returns an array of values (day, month, year, hour, min, seconds) or singular string
435    # return localtime; # same as localtime(time); # http://perldoc.perl.org/functions/localtime.html
436   
437}
438
439sub get_datestamp {
440    my $self = shift (@_);
441    my ($timestamp) = @_;
442
443    my ($seconds, $minutes, $hours, $day_of_month, $month, $year,
444        $wday, $yday, $isdst) = localtime($timestamp);
445
446    my $datestamp = sprintf("%d%02d%02d",1900+$year,$month+1,$day_of_month);
447
448    return $datestamp;
449}
450
451sub _load_info_txt
452{
453    my $self = shift (@_);
454    my ($filename) = @_;
455
456    if (defined $filename && &FileUtils::fileExists($filename)) {
457    open (INFILE, $filename) ||
458        die "oaiinfo::load_info couldn't read $filename\n";
459
460    my ($line, @lineparts);
461    while (defined ($line = <INFILE>)) {
462        $line =~ s/\cM|\cJ//g; # remove end-of-line characters
463        @lineparts = split ("\t", $line);
464        if (scalar(@lineparts) >= 2) {
465        $self->set_info (@lineparts);
466        }
467    }
468    close (INFILE);
469    }
470
471}
472
473sub _load_info_db
474{
475    my $self = shift (@_);
476    my ($filename) = @_;
477
478    my $infodb_map = {};
479
480    &dbutil::read_infodb_file($self->{'infodbtype'}, $filename, $infodb_map);
481
482    foreach my $oid ( keys %$infodb_map ) {
483    my $vals = $infodb_map->{$oid};
484    # interested in oid, timestamp, deletion status
485
486    my ($deletion_status) = ($vals=~/^<status>(.*)$/m);
487    my ($timestamp) = ($vals=~/^<timestamp>(.*)$/m);
488    my ($datestamp) = ($vals=~/^<datestamp>(.*)$/m);
489   
490    $self->add_info ($oid, $deletion_status, $timestamp, $datestamp);
491    }
492}
493
494# if no filename is passed in (and you don't generally want to), then
495# it tries to load in <collection>/etc/oai-inf.<db> if it exists
496sub load_info {
497    my $self = shift (@_);
498    my ($filename) = @_;
499
500    $self->{'info'} = {};
501
502    $filename = $self->{'oaidb_file_path'} unless defined $filename;
503
504    if (&FileUtils::fileExists($filename)) {
505    if ($filename =~ m/\.inf$/) {
506        $self->_load_info_txt($filename);
507    }
508    else {
509        $self->_load_info_db($filename);
510    }
511    }
512
513}
514
515sub _save_info_txt {
516    my $self = shift (@_);
517    my ($filename) = @_;
518
519    my ($OID, $info);
520
521    open (OUTFILE, ">$filename") ||
522    die "oaiinfo::save_info couldn't write $filename\n";
523 
524    foreach $info (@{$self->get_OID_list()}) {
525    if (defined $info) {
526        print OUTFILE join("\t", @$info), "\n";
527    }
528    }
529    close (OUTFILE);
530}
531
532# if no filename is passed in (and you don't generally want to), then
533# this subroutine tries to write to <collection>/etc/oai-inf.<db>.
534sub _save_info_db {
535    my $self = shift (@_);
536    my ($filename) = @_;
537
538    $filename = $self->{'oaidb_file_path'} unless defined $filename;
539    my $infodbtype = $self->{'infodbtype'};
540
541    # write out again. Open file for overwriting, not appending.
542    # Then write out data structure $self->{'info'} that's been maintaining the data in-memory.
543    my $infodb_handle = &dbutil::open_infodb_write_handle($infodbtype, $filename);
544
545    my $infomap = $self->{'info'};
546    foreach my $oid ( keys %$infomap ) {
547    my $OID_info = $self->{'info'}->{$oid};
548    my $val = "<status>".$OID_info->[INFO_STATUS_INDEX];
549    $val .= "\n<timestamp>".$OID_info->[INFO_TIMESTAMP_INDEX];
550    $val .= "\n<datestamp>".$OID_info->[INFO_DATESTAMP_INDEX]."\n";
551    &dbutil::write_infodb_rawentry($infodbtype,$infodb_handle,$oid,$val);
552    }
553    &dbutil::close_infodb_write_handle($infodbtype, $infodb_handle);
554}
555
556sub save_info {
557    my $self = shift (@_);
558    my ($filename) = @_;
559
560    if(defined $filename) {
561    if ($filename =~ m/(contents)|(\.inf)$/) {
562        $self->_save_info_txt($filename);
563    }
564    else {
565        $self->_save_info_db($filename);
566    }
567    } else {
568    $self->_save_info_db();
569    }
570}
571
572
573sub set_info { # sets existing or appends
574    my $self = shift (@_);
575    my ($OID, $del_status, $timestamp) = @_;
576
577    if(!defined $timestamp) { # get current date timestamp
578    $timestamp = $self->get_current_time();
579    }
580    my $datestamp = $self->get_datestamp($timestamp);
581
582    $self->{'info'}->{$OID} = [$del_status, $timestamp, $datestamp];
583
584}
585
586sub add_info { # called to load a single record from file into memory, so it should be provided all 4 fields
587    my $self = shift (@_);
588    my ($OID, $del_status, $timestamp, $datestamp) = @_;
589
590    $self->{'info'}->{$OID} = [$del_status, $timestamp, $datestamp];
591}
592
593
594# returns a list of the form [[OID, deletion_status, timestamp, datestamp], ...]
595sub get_OID_list
596{
597    my $self = shift (@_);
598
599    my @list = ();
600   
601    my $infomap = $self->{'info'};
602    foreach my $OID (keys %$infomap) { 
603    my $OID_info = $self->{'info'}->{$OID};
604
605    push (@list, [$OID, $OID_info->[INFO_STATUS_INDEX],
606              $OID_info->[INFO_TIMESTAMP_INDEX],
607              $OID_info->[INFO_DATESTAMP_INDEX]
608          ]);
609    }
610
611    return \@list;
612}
613
614
615# returns the number of entries so far, including deleted ones
616# http://stackoverflow.com/questions/1109095/how-can-i-find-the-number-of-keys-in-a-hash-in-perl
617sub size {
618    my $self = shift (@_);
619    my $infomap = $self->{'info'};
620    return (scalar keys %$infomap);
621}
622
6231;
Note: See TracBrowser for help on using the browser.