Re: [greenstone-devel] Horizontal sort by title - Vertical sort by date

From Michael Dewsnip
DateFri, 22 Aug 2003 16:57:40 +1200
Subject Re: [greenstone-devel] Horizontal sort by title - Vertical sort by date
In-Reply-To (001401c367ba$d1e416b0$7bb88c96-inferno)
Hello Dimitris,

I don't think any of the standard classifiers will do this - they only use one metadata field.

However you should be able to do what you want with a new classifier that I hacked up quickly a couple of weeks ago (mainly to learn about classifiers). Note that it hasn't had much testing, some things are pretty hokey (especially the partitioning into HLists, but you may not need this) and the output looks a bit ugly (fixing this requires changes to the receptionist).

However, if you're willing to give it a go, put the attached file in your perllib/classify directory, and add something like:

classify    MultiAZCompactList -metadata Title,Year,Month -groupsize 20,20,20

to the collect.cfg file of your collection. This assumes you have three separate metadata fields: Title, Year and Month. The groupsize specifies the number of child items allowed (for the corresponding metadata element) before an hlist partition is added (you'll see what I mean if you try it).

This is hardly even beta, so don't expect it to work perfectly! Hopefully I'll get some time to polish it up soon.

Please let me know if you have any problems, or if you can think of new options it should have, etc.

Regards,

Michael
 
 

Dimitris Gavrilis wrote:

Dear greenstone developers, Is it possible to build a collection of journals where in the titles a-zmenu the journals are displayed by title (horizontal) and at the second level(vertical) the volumes of each title are sorted by date ? This is an example of what i mean: Title 1Title 2Title 3  |------> 1997  |------> 1998               |------> Jan               |------> Feb  |-------> 1999Title 4 Thank you,Dimitris Gavrilis (gavrilis@george.wcl2.ee.upatras.gr)

_______________________________________________ greenstone-devel mailing list greenstone-devel@list.scms.waikato.ac.nz https://list.scms.waikato.ac.nz/mailman/listinfo/greenstone-devel


<<attachment>>
Type: text/plain
Filename: MultiAZCompactList.pm

###########################################################################
#
# MultiAZCompactList.pm --
# A component of the Greenstone digital library software
# from the New Zealand Digital Library Project at the
# University of Waikato, New Zealand.
#
# Copyright (C) 1999 New Zealand Digital Library Project
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
###########################################################################

package MultiAZCompactList;


use BasClas;


sub BEGIN {
@ISA = ('BasClas');
}


my $arguments =
[ { 'name' => "metadata",
'desc' => "Metadata fields used for classification, comma separated.",
'type' => "metalist",
'reqd' => "yes" } ,
{ 'name' => "buttonname",
'desc' => "Button name for this classifier.",
'type' => "string",
'deft' => "First metadata field specified with -metadata",
'reqd' => "no" },
{ 'name' => "alwaysgroup",
'desc' => "Create a bookshelf icon even if there is only one item in the group.",
'type' => "string",
'deft' => "True for all metadata fields except the last",
'reqd' => "no" },
{ 'name' => "groupsize",
'desc' => "The number of items in each hlist group.",
'type' => "string"} ];

my $options = { 'name' => "MultiAZCompactList",
'desc' => "",
'inherits' => "Yes",
'args' => $arguments };


sub new
{
my $class = shift(@_);
my $self = new BasClas($class, @_);

# To allow for proper inheritance of arguments
local $option_list = $self->{'option_list'};
push(@{$option_list}, $options);

local $metadata;
local $buttonname;
local $alwaysgroup;
local $groupsize;
if (!parsargv::parse(\@_,
q^metadata/.*/^, \$metadata,
q^buttonname/.*/^, \$buttonname,
q^alwaysgroup/.*/^, \$alwaysgroup,
q^groupsize/.*/^, \$groupsize,
"allow_extra_options")) {
print STDERR "\nIncorrect options passed to $class, check your collect.cfg file\n";
$self->print_txt_usage();
die "\n";
}

# The metadata elements to use (required)
if (!$metadata) {
die "Error: No metadata fields specified for MultiAZCompactList.\n";
}
local @metalist = split(/\,/, $metadata);
$self->{'metalist'} = \@metalist;

# Create an empty list for the OID values
$self->{'OIDlist'} = [];

# Create an empty hash for the metadata values of each metadata element
foreach $metaelem (@metalist) {
$self->{$metaelem . ".list"} = {};
}

# The classifier button name
if (!$buttonname) {
# Default: the first metadata field specified
$buttonname = $metalist[0];
}
$self->{'title'} = $buttonname;

# Whether to group single items into a bookshelf
if (!$alwaysgroup) {
# Default: true for all metadata fields except the last
foreach $metaelem (@metalist) {
$self->{$metaelem . ".alwaysgroup"} = "t";
}
local $lastelem = $metalist[$#metalist];
$self->{$lastelem . ".alwaysgroup"} = "f";
}
else {
local @alwaysgrouplist = split(/\,/, $alwaysgroup);

# Assign values based on the always group parameter
foreach $metaelem (@metalist) {
local $alwaysgroupelem = shift(@alwaysgrouplist);
if (defined($alwaysgroupelem)) {
$self->{$metaelem . ".alwaysgroup"} = $alwaysgroupelem;
}
else {
if ($metaelem ne $metalist[$#metalist]) {
$self->{$metaelem . ".alwaysgroup"} = "t";
}
else {
$self->{$metaelem . ".alwaysgroup"} = "f";
}
}
}
}

# The number of items in each group
if (!$groupsize) {
# Default: 20 in first level, 19 in second level, ... etc.
local $thisgroupsize = 20;
foreach $metaelem (@metalist) {
$self->{$metaelem . ".groupsize"} = $thisgroupsize;
$thisgroupsize--;
}
}
else {
local @groupsizelist = split(/\,/, $groupsize);

# Assign values based on the groupsize parameter
foreach $metaelem (@metalist) {
local $groupsizeelem = shift(@groupsizelist);
if (defined($groupsizeelem)) {
$self->{$metaelem . ".groupsize"} = $groupsizeelem;
}
else {
$self->{$metaelem . ".groupsize"} = $self->{$metalist[0] . ".groupsize"};
}
}
}

return bless $self, $class;
}


sub init
{
# Nothing to do...
local $self = shift(@_);
}


sub classify
{
local $self = shift(@_);
local $doc_obj = shift(@_);

local $doc_OID = $doc_obj->get_OID();
local $doc_top = $doc_obj->get_top_section();

local @metalist = @{$self->{'metalist'}};

# Only classify the document if it has a value for the first metadata element
local $firstelem = $metalist[0];
if (defined($doc_obj->get_metadata_element($doc_top, $firstelem))) {
push(@{$self->{'OIDlist'}}, $doc_OID);

# Get the value of each metadata element for this document
foreach $metaelem (@metalist) {
local $metavalue = $doc_obj->get_metadata_element($doc_top, $metaelem);

# If there is no value for this metadata element, use "Unknown"
if (!defined($metavalue)) {
$metavalue = "Unknown";
}

# Make the value title case
substr($metavalue, 0, 1) =~ tr/a-z/A-Z/;
# print "Metaelem: $metaelem, Value: $metavalue\n";
$self->{$metaelem . ".list"}->{$doc_OID} = $metavalue;
}
}
}


sub get_classify_info
{
local $self = shift(@_);

# The metadata elements to classify by
local @metalist = @{$self->{'metalist'}};

# The OID values of the documents to include in the classification
local @OIDlist = @{$self->{'OIDlist'}};
# print "Number of OIDs to include in classification: " . @OIDlist . "\n";

# The root node of the classification hierarchy
local %classifyinfo = ( 'thistype' => "Invisible",
'Title' => $self->{'title'},
'contains' => [] );

# Recursively create the classification hierarchy, one level for each metadata element
&add_az_list($self, \@metalist, \@OIDlist, \%classifyinfo);
return \%classifyinfo;
}


sub add_az_list
{
local $self = shift(@_);
local @metalist = @{shift(@_)};
local @OIDlist = @{shift(@_)};
local $classifyinfo = shift(@_);
# print "\nAdding AZ list for " . $classifyinfo->{'Title'} . "\n";

local $metaelem = $metalist[0];
# print "Processing metadata element: " . $metaelem . "\n";
# print "Number of OID values: " . @OIDlist . "\n";

local %OIDtometavaluehash = %{$self->{$metaelem . ".list"}};

# Create a mapping from metadata value to OID
local %metavaluetoOIDhash = ();
foreach $OID (@OIDlist) {
local $metavalue = $OIDtometavaluehash{$OID};
push(@{$metavaluetoOIDhash{$metavalue}}, $OID);
}
# print "Number of distinct values: " . scalar(keys %metavaluetoOIDhash) . "\n";

# Partition the values (if necessary)
local $groupsize = $self->{$metaelem . ".groupsize"};
if (scalar(keys %metavaluetoOIDhash) > $groupsize) {
local @sortedmetavalues = sort(keys %metavaluetoOIDhash);
local $itemsdone = 0;
local %metavaluetoOIDsubhash = ();
local $lastpartitionend = "";
local $partitionstart;
foreach $metavalue (@sortedmetavalues) {
# print "Metavalue: $metavalue\n";
$metavaluetoOIDsubhash{$metavalue} = $metavaluetoOIDhash{$metavalue};
$itemsdone++;
local $itemsinpartition = scalar(keys %metavaluetoOIDsubhash);

# Is this the start of a new partition?
if ($itemsinpartition == 1) {
$partitionstart = &generate_partition_start($metavalue, $lastpartitionend);
}

# Is this the end of the partition?
if ($itemsinpartition == $groupsize || $itemsdone == @sortedmetavalues) {
local $partitionend = &generate_partition_end($metavalue, $partitionstart);

local $partitionname = $partitionstart;
if ($partitionend ne $partitionstart) {
$partitionname = $partitionname . "-" . $partitionend;
}
# print "Partition: $partitionname\n";
&add_hlist_partition($self, \@metalist, $classifyinfo, $partitionname, \%metavaluetoOIDsubhash);
%metavaluetoOIDsubhash = ();
$lastpartitionend = $partitionend;
}
}

# The partitions are stored in an HList
$classifyinfo->{'childtype'} = "HList";
}

# Otherwise just add all the values to a VList
else {
&add_vlist($self, \@metalist, $classifyinfo, \%metavaluetoOIDhash);
$classifyinfo->{'childtype'} = "VList";
}
}


sub generate_partition_start
{
local $metavalue = shift(@_);
local $lastpartitionend = shift(@_);

local $partitionstart = substr($metavalue, 0, 1);
if ($partitionstart le $lastpartitionend) {
$partitionstart = substr($metavalue, 0, 2);
# Give up after three characters
if ($partitionstart le $lastpartitionend) {
$partitionstart = substr($metavalue, 0, 3);
}
}

return $partitionstart;
}


sub generate_partition_end
{
local $metavalue = shift(@_);
local $partitionstart = shift(@_);

local $partitionend = substr($metavalue, 0, length($partitionstart));
if ($partitionend gt $partitionstart) {
$partitionend = substr($metavalue, 0, 1);
if ($partitionend le $partitionstart) {
$partitionend = substr($metavalue, 0, 2);
# Give up after three characters
if ($partitionend le $partitionstart) {
$partitionend = substr($metavalue, 0, 3);
}
}
}

return $partitionend;
}


sub add_hlist_partition
{
local $self = shift(@_);
local @metalist = @{shift(@_)};
local $classifyinfo = shift(@_);
local $partitionname = shift(@_);
local $metavaluetoOIDhash = shift(@_);

# Create an hlist partition
local %subclassifyinfo = ( 'Title' => $partitionname,
'childtype' => "VList",
'contains' => [] );

# Add the children to the hlist partition
&add_vlist($self, \@metalist, \%subclassifyinfo, \%metavaluetoOIDsubhash);
push(@{$classifyinfo->{'contains'}}, \%subclassifyinfo);
}


sub add_vlist
{
local $self = shift(@_);
local @metalist = @{shift(@_)};
local $classifyinfo = shift(@_);
local $metavaluetoOIDhash = shift(@_);

local $metaelem = shift(@metalist);

# Create an entry in the vlist for each value
foreach $metavalue (sort(keys %{$metavaluetoOIDhash})) {
local @OIDlist = @{$metavaluetoOIDhash->{$metavalue}};

# If there is only one item and 'alwaysgroup' is false, add the item to the list
if (@OIDlist == 1 && $self->{$metaelem . ".alwaysgroup"} eq "f") {
push(@{$classifyinfo->{'contains'}}, { 'OID' => $OIDlist[0] });
}

# Otherwise create a sublist (bookshelf) for the metadata value
else {
local %subclassifyinfo = ( 'Title' => $metavalue,
'childtype' => "VList",
'contains' => [] );

# If there are metadata elements remaining, recursively apply the process
if (@metalist > 0) {
&add_az_list($self, \@metalist, \@OIDlist, \%subclassifyinfo);
}
# Otherwise just add the documents as children of this list
else {
foreach $OID (@OIDlist) {
push(@{$subclassifyinfo{'contains'}}, { 'OID' => $OID });
}
}

# Add the sublist to the list
push(@{$classifyinfo->{'contains'}}, \%subclassifyinfo);
}
}
}


1;