source: gsdl/trunk/perllib/classify/DateList.pm@ 17209

Last change on this file since 17209 was 17209, checked in by kjdon, 16 years ago

BasClas renamed to BaseClassifier, tidied up constructors

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1###########################################################################
2#
3# DateList.pm --
4# A component of the Greenstone digital library software
5# from the New Zealand Digital Library Project at the
6# University of Waikato, New Zealand.
7#
8# Copyright (C) 1999 New Zealand Digital Library Project
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24###########################################################################
25
26# classifier plugin for sorting by date
27
28# date is assumed to be in the form yyyymmdd
29
30# at present dates are split by year - this should change
31# jrm21 - added option "bymonth", which splits by year and month.
32
33# 23/09/03 Added some more options -kjdon.
34# these include:
35# -nogroup, which makes each year (or year+month) an individual entry in
36# the horizontal list and prevents compaction
37# -metadata, use a different metadata for the date (instead of Date), still expects yyyymmdd format. this affects display cos greenstone displays Date metadata as dd month yyyy, whereas any other date metadata is displayed as yyyymmdd - this needs fixing
38# -sort specifies an additional metadata to use in sorting, will take affect when two docs have the same date.
39
40package DateList;
41
42use BaseClassifier;
43use sorttools;
44
45use strict;
46no strict 'refs'; # allow filehandles to be variables and viceversa
47
48sub BEGIN {
49 @DateList::ISA = ('BaseClassifier');
50}
51
52my $arguments =
53 [ { 'name' => "metadata",
54 'desc' => "{DateList.metadata}",
55 'type' => "metadata",
56 'deft' => "Date",
57 'reqd' => "yes" } ,
58 { 'name' => "sort",
59 'desc' => "{DateList.sort}",
60 'type' => "metadata",
61 'reqd' => "no" } ,
62 { 'name' => "reverse_sort",
63 'desc' => "{DateList.reverse_sort}",
64 'type' => "flag",
65 'reqd' => "no" },
66 { 'name' => "bymonth",
67 'desc' => "{DateList.bymonth}",
68 'type' => "flag",
69 'reqd' => "no" },
70 { 'name' => "nogroup",
71 'desc' => "{DateList.nogroup}",
72 'type' => "flag",
73 'reqd' => "no" },
74 { 'name' => "no_special_formatting",
75 'desc' => "{DateList.no_special_formatting}",
76 'type' => "flag",
77 'reqd' => "no" }
78
79 ];
80
81my $options = { 'name' => "DateList",
82 'desc' => "{DateList.desc}",
83 'abstract' => "no",
84 'inherits' => "yes",
85 'args' => $arguments };
86
87
88sub new {
89 my ($class) = shift (@_);
90 my ($classifierslist,$inputargs,$hashArgOptLists) = @_;
91 push(@$classifierslist, $class);
92
93 push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
94 push(@{$hashArgOptLists->{"OptList"}},$options);
95
96 my $self = new BaseClassifier($classifierslist, $inputargs, $hashArgOptLists);
97
98 if ($self->{'info_only'}) {
99 # don't worry about any options etc
100 return bless $self, $class;
101 }
102
103 # Manually set $self parameters.
104 $self->{'list'} = {};
105
106 if (!defined $self->{"metadata"} || $self->{"metadata"} eq "") {
107 $self->{'metadata'} = "Date";
108 }
109 # now can have comma separated list of Dates - we just use the first one (for now)
110 my @meta_list = split(/,/, $self->{"metadata"});
111 $self->{'meta_list'} = \@meta_list;
112
113 $self->{'buttonname'} = $self->generate_title_from_metadata($self->{'metadata'}) unless ($self->{'buttonname'});
114
115 $self->{'childtype'} = "DateList";
116 if ($self->{'no_special_formatting'}) {
117 $self->{'childtype'} = "VList";
118 }
119
120 return bless $self, $class;
121}
122
123sub init {
124 my $self = shift (@_);
125
126 $self->{'list'} = {};
127}
128
129sub classify {
130 my $self = shift (@_);
131 my ($doc_obj) = @_;
132
133 my $doc_OID = $doc_obj->get_OID();
134
135 # find the first available metadata
136 my $date;
137 foreach my $m (@{$self->{'meta_list'}}) {
138 $date = $doc_obj->get_metadata_element($doc_obj->get_top_section(), $m);
139 last if defined $date;
140 }
141
142 #my $date = $doc_obj->get_metadata_element ($doc_obj->get_top_section(), $self->{'metadata'});
143 if (!defined $date || $date eq "") {
144 # if this document doesn't contain Date element we won't
145 # include it in this classification
146 return;
147 }
148
149 my $sort_other = "";
150 if (defined $self->{'sort'} && $self->{'sort'} ne "") {
151 $sort_other = $doc_obj->get_metadata_element ($doc_obj->get_top_section(), $self->{'sort'});
152 $sort_other = &sorttools::format_metadata_for_sorting($self->{'sort'}, $sort_other, $doc_obj) unless $self->{'no_metadata_formatting'};
153 }
154
155 if (defined $self->{'list'}->{$doc_OID}) {
156 my $outhandle = $self->{'outhandle'};
157 print $outhandle "WARNING: DateList::classify called multiple times for $doc_OID\n";
158 }
159
160
161 $self->{'list'}->{$doc_OID} = "$date$sort_other";
162
163}
164
165
166sub get_classify_info {
167 my $self = shift (@_);
168
169 my @classlist = sort {$self->{'list'}->{$a} cmp $self->{'list'}->{$b};} keys %{$self->{'list'}};
170
171 if ($self->{'reverse_sort'}) {
172 @classlist = reverse @classlist;
173 }
174
175
176 return $self->splitlist (\@classlist);
177}
178
179
180sub get_entry {
181 my $self = shift (@_);
182 my ($title, $childtype, $thistype) = @_;
183
184 # organise into classification structure
185 my %classifyinfo = ('childtype'=>$childtype,
186 'Title'=>$title,
187 'contains'=>[],
188 'mdtype'=>$self->{'metadata'});
189 $classifyinfo{'thistype'} = $thistype
190 if defined $thistype && $thistype =~ /\w/;
191
192 return \%classifyinfo;
193}
194
195# splitlist takes an ordered list of classifications (@$classlistref) and
196# splits it up into sub-sections by date
197sub splitlist {
198 my $self = shift (@_);
199 my ($classlistref) = @_;
200 my $classhash = {};
201
202 # top level
203 my $childtype = "HList";
204
205 if (scalar (@$classlistref) <= 39 &&
206 !$self->{'nogroup'}) {$childtype = $self->{'childtype'};}
207
208 my $classifyinfo = $self->get_entry ($self->{'buttonname'}, $childtype, "Invisible");
209 # don't need to do any splitting if there are less than 39 (max + min -1)
210 # classifications, unless nogroup is specified
211 if ((scalar @$classlistref) <= 39 && !$self->{'nogroup'}) {
212 foreach my $subOID (@$classlistref) {
213 push (@{$classifyinfo->{'contains'}}, {'OID'=>$subOID});
214 }
215 return $classifyinfo;
216 }
217
218
219 if ($self->{'bymonth'}) {
220 # first split up the list into separate year+month classifications
221
222 if (!$self->{'nogroup'}) { # hlist of year+month pairs
223 # single level of classifications
224 foreach my $classification (@$classlistref) {
225 my $date = $self->{'list'}->{$classification};
226 $date =~ s/^(\d\d\d\d)-?(\d\d).*$/$1&nbsp;_textmonth$2_/;
227 # sanity check if month is zero
228 if ($date =~ /00_$/) {
229 $date =~ s/^(\d\d\d\d).*$/$1/g;
230 }
231 $classhash->{$date} = [] unless defined $classhash->{$date};
232 push (@{$classhash->{$date}}, $classification);
233 }
234
235 } else { # don't group - individual years and months
236 foreach my $classification (@$classlistref) {
237 my $date = $self->{'list'}->{$classification};
238 $date =~ s/^(\d\d\d\d)-?(\d\d).*$/$1&nbsp;_textmonth$2_/;
239 my ($year, $month)=($1,$2);
240 # sanity check if month is zero
241 if ($date =~ /00_$/) {
242 $date =~ s/^(\d\d\d\d).*$/$1/g;
243 }
244 # create subclass if it doesn't already exist
245 $classhash->{$year} = () unless defined $classhash->{$year};
246
247 $classhash->{$year}->{$month} = []
248 unless defined $classhash->{$year}->{$month};
249 push (@{$classhash->{$year}->{$month}}, $classification);
250
251 }
252 # create hlist of years containing hlists of months
253
254
255 if ($self->{'reverse_sort'}){
256 foreach my $subclass (sort {$b <=> $a}
257 (keys %$classhash)){
258 my $yearclassify = $self->get_entry($subclass, "HList");
259 foreach my $subsubclass (sort {$b <=> $a}
260 (keys %{$classhash->{$subclass}})) {
261 my $monthname=$subsubclass;
262 if ($monthname >= 1 && $monthname <= 12) {
263 $monthname="_textmonth" . $monthname . "_";
264 }
265 my $monthclassify=$self->get_entry($monthname, $self->{'childtype'});
266 push (@{$yearclassify->{'contains'}}, $monthclassify);
267
268 foreach my $subsubOID
269 (@{$classhash->{$subclass}->{$subsubclass}}) {
270 push (@{$monthclassify->{'contains'}},
271 {'OID'=>$subsubOID});
272 }
273 }
274 push (@{$classifyinfo->{'contains'}}, $yearclassify);
275 }
276 }
277 else{
278 foreach my $subclass (sort {$a <=> $b}
279 (keys %$classhash)){
280 my $yearclassify = $self->get_entry($subclass, "HList");
281 foreach my $subsubclass (sort {$a <=> $b}
282 (keys %{$classhash->{$subclass}})) {
283 my $monthname=$subsubclass;
284 if ($monthname >= 1 && $monthname <= 12) {
285 $monthname="_textmonth" . $monthname . "_";
286 }
287 my $monthclassify=$self->get_entry($monthname, $self->{'childtype'});
288 push (@{$yearclassify->{'contains'}}, $monthclassify);
289
290 foreach my $subsubOID
291 (@{$classhash->{$subclass}->{$subsubclass}}) {
292 push (@{$monthclassify->{'contains'}},
293 {'OID'=>$subsubOID});
294 }
295 }
296 push (@{$classifyinfo->{'contains'}}, $yearclassify);
297 }
298
299
300 }
301
302 return $classifyinfo;
303 } # nogroup
304 }else {
305 # not by month
306 # first split up the list into separate year classifications
307 foreach my $classification (@$classlistref) {
308 my $date = $self->{'list'}->{$classification};
309 $date =~ s/^(\d\d\d\d).*$/$1/;
310 $classhash->{$date} = [] unless defined $classhash->{$date};
311 push (@{$classhash->{$date}}, $classification);
312 }
313
314 }
315
316 # only compact the list if nogroup not specified
317 if (!$self->{'nogroup'}) {
318 $classhash = $self->compactlist ($classhash);
319 }
320
321 if ($self->{'reverse_sort'} && $self->{'nogroup'} ) {
322 foreach my $subclass (reverse sort keys %$classhash) {
323 my $tempclassify = $self->get_entry($subclass, $self->{'childtype'});
324 foreach my $subsubOID (@{$classhash->{$subclass}}) {
325 push (@{$tempclassify->{'contains'}}, {'OID'=>$subsubOID});
326 }
327 push (@{$classifyinfo->{'contains'}}, $tempclassify);
328 }
329 }
330 else{
331 foreach my $subclass (sort keys %$classhash) {
332 my $tempclassify = $self->get_entry($subclass, $self->{'childtype'});
333 foreach my $subsubOID (@{$classhash->{$subclass}}) {
334 push (@{$tempclassify->{'contains'}}, {'OID'=>$subsubOID});
335 }
336 push (@{$classifyinfo->{'contains'}}, $tempclassify);
337 }
338
339 }
340
341
342 return $classifyinfo;
343}
344
345sub compactlist {
346 my $self = shift (@_);
347 my ($classhashref) = @_;
348 my $compactedhash = {};
349 my @currentOIDs = ();
350 my $currentfirstdate = "";
351 my $currentlastdate = "";
352 my $lastkey = "";
353
354 # minimum and maximum documents to be displayed per page.
355 # the actual maximum will be max + (min-1).
356 # the smallest sub-section is a single letter at present
357 # so in this case there may be many times max documents
358 # displayed on a page.
359 my $min = 10;
360 my $max = 30;
361 foreach my $subsection (sort keys %$classhashref) {
362 $currentfirstdate = $subsection if $currentfirstdate eq "";
363 if ((scalar (@currentOIDs) < $min) ||
364 ((scalar (@currentOIDs) + scalar (@{$classhashref->{$subsection}})) <= $max)) {
365 push (@currentOIDs, @{$classhashref->{$subsection}});
366 $currentlastdate = $subsection;
367 } else {
368
369 if ($currentfirstdate eq $currentlastdate) {
370 @{$compactedhash->{$currentfirstdate}} = @currentOIDs;
371 $lastkey = $currentfirstdate;
372 } else {
373 @{$compactedhash->{"$currentfirstdate-$currentlastdate"}} = @currentOIDs;
374 $lastkey = "$currentfirstdate-$currentlastdate";
375 }
376 if (scalar (@{$classhashref->{$subsection}}) >= $max) {
377 $compactedhash->{$subsection} = $classhashref->{$subsection};
378 @currentOIDs = ();
379 $currentfirstdate = "";
380 $lastkey = $subsection;
381 } else {
382 @currentOIDs = @{$classhashref->{$subsection}};
383 $currentfirstdate = $subsection;
384 $currentlastdate = $subsection;
385 }
386 }
387 }
388
389 # add final OIDs to last sub-classification if there aren't many otherwise
390 # add final sub-classification
391 if ((scalar (@currentOIDs) < $min) && (scalar (@currentOIDs) > 0)) {
392 # want every thing in previous up to the dash
393 my ($newkey) = $lastkey =~ /^([^\-]+)/;
394 @currentOIDs = (@{$compactedhash->{$lastkey}}, @currentOIDs);
395 delete $compactedhash->{$lastkey};
396 @{$compactedhash->{"$newkey-$currentlastdate"}} = @currentOIDs;
397 } else {
398 if ($currentfirstdate eq $currentlastdate) {
399 @{$compactedhash->{$currentfirstdate}} = @currentOIDs;
400 } else {
401 @{$compactedhash->{"$currentfirstdate-$currentlastdate"}} = @currentOIDs;
402 }
403 }
404
405 return $compactedhash;
406}
407
4081;
Note: See TracBrowser for help on using the repository browser.