X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=reports%2Fcatalogue_stats.pl;h=f3b92b3029123f27bb2a686fac90c6d242104a2b;hb=2bdff1822add1273760cef70fed6e39ef16d1924;hp=b1e6d5fbc472f759f2ac03fd0d1d735ddef50303;hpb=fc1342f73df868410e0ab670981f25ba2e1acd74;p=koha.git diff --git a/reports/catalogue_stats.pl b/reports/catalogue_stats.pl index b1e6d5fbc4..f3b92b3029 100755 --- a/reports/catalogue_stats.pl +++ b/reports/catalogue_stats.pl @@ -1,6 +1,5 @@ #!/usr/bin/perl -# $Id$ # Copyright 2000-2002 Katipo Communications # @@ -20,14 +19,15 @@ # Suite 330, Boston, MA 02111-1307 USA use strict; +# use warnings; # FIXME use C4::Auth; use CGI; use C4::Context; use C4::Branch; # GetBranches use C4::Output; use C4::Koha; -use C4::Interface::CGI::Output; -use C4::Circulation::Circ2; +use C4::Reports; +use C4::Circulation; =head1 NAME @@ -35,24 +35,31 @@ plugin that shows a stats on borrowers =head1 DESCRIPTION - -=over2 +=over 2 =cut +our $debug = 0; my $input = new CGI; -my $do_it=$input->param('do_it'); my $fullreportname = "reports/catalogue_stats.tmpl"; -my $line = $input->param("Line"); -my $column = $input->param("Column"); -my @filters = $input->param("Filter"); +my $do_it = $input->param('do_it'); +my $line = $input->param("Line"); +my $column = $input->param("Column"); +my @filters = $input->param("Filter"); my $deweydigits = $input->param("deweydigits"); -my $lccndigits = $input->param("lccndigits"); -my $cotedigits = $input->param("cotedigits"); -my $output = $input->param("output"); -my $basename = $input->param("basename"); -my $mime = $input->param("MIME"); -my $del = $input->param("sep"); +my $lccndigits = $input->param("lccndigits"); +my $cotedigits = $input->param("cotedigits"); +my $output = $input->param("output"); +my $basename = $input->param("basename"); +my $mime = $input->param("MIME"); +our $sep = $input->param("sep"); +$sep = "\t" if ($sep eq 'tabulation'); +my $item_itype; +if(C4::Context->preference('item-level_itypes')) { + $item_itype = "items\.itype" +} else { + $item_itype = "itemtype"; +} my ($template, $borrowernumber, $cookie) = get_template_and_user({template_name => $fullreportname, @@ -74,10 +81,8 @@ if ($do_it) { -encoding => 'utf-8', -attachment=>"$basename.csv", -name=>"$basename.csv" ); - my $cols = @$results[0]->{loopcol}; + my $cols = @$results[0]->{loopcol}; my $lines = @$results[0]->{looprow}; - my $sep; - $sep =C4::Context->preference("delimiter"); print @$results[0]->{line} ."/". @$results[0]->{column} .$sep; foreach my $col ( @$cols ) { print $col->{coltitle}.$sep; @@ -106,175 +111,72 @@ if ($do_it) { my %labels; my $count=0; my $req; - $req = $dbh->prepare("select count(dewey) from biblioitems "); - $req->execute; - my $hasdewey; my @select; -# push @select,""; - while (my ($value) =$req->fetchrow) { - $hasdewey =1 if (($value>2) and (! $hasdewey)); - $count++ if (($value>2) and (! $hasdewey)); -# push @select, $value; - } -# my $CGIdewey=CGI::scrolling_list( -name => 'Filter', -# -id => 'Filter', -# -values => \@select, -# -size => 1, -# -multiple => 0 ); - - $req = $dbh->prepare( "select count(lccn) from biblioitems "); - $req->execute; -# undef @select; -# push @select,""; - my $haslccn; - my $hlghtlccn; - while (my ($value) =$req->fetchrow) { - $hlghtlccn = !($hasdewey); - $haslccn =1 if (($value>2) and (! $haslccn)); - $count++ if (($value) and (! $haslccn)); + # FIXME: no such field "dewey" + # $req = $dbh->prepare("select count(dewey) from biblioitems "); + # $req->execute; + my $hasdewey = 0; + +# (rch) biblioitems.lccn is mapped to lccn MARC21 010$a in default framework. +# This is not the LC Classification. It's the Control Number. +# So I'm just going to remove this bit. Call Number is handled in itemcallnumber. +# + my $haslccn = 0; +# $req = $dbh->prepare( "select count(lccn) from biblioitems "); +# $req->execute; +# my $hlghtlccn; +# while (my ($value) =$req->fetchrow) { +# $hlghtlccn = !($hasdewey); +# $haslccn =1 if (($value>2) and (! $haslccn)); +# $count++ if (($value) and (! $haslccn)); # push @select, $value; - } +# } # my $CGIlccn=CGI::scrolling_list( -name => 'Filter', # -id => 'Filter', # -values => \@select, # -size => 1, # -multiple => 0 ); - - $req = $dbh->prepare("select count(itemcallnumber) from items"); - $req->execute; -# undef @select; -# push @select,""; - my $hascote; - my $hlghtcote; - while (my ($value) =$req->fetchrow) { - $hascote =1 if (($value>2) and (! $hascote)); - $count++ if (($value) and (! $hascote)); - $hlghtcote = (($hasdewey) and ($haslccn)) or (!($hasdewey) and !($haslccn)); -# push @select, $value; - } -# my $CGIcote=CGI::scrolling_list( -name => 'Filter', -# -id => 'Filter', -# -values => \@select, -# -size => 1, -# -multiple => 0 ); - $count++; - my $hglghtDT =$count % 2; -# warn "highlightDT ".$hglghtDT; - $count++; - my $hglghtPub =$count % 2; -# warn "highlightPub ".$hglghtPub; - $count++; - my $hglghtPY =$count % 2; -# warn "highlightPY ".$hglghtPY; - $count++; - my $hglghtHB =$count % 2; -# warn "highlightHB ".$hglghtHB; - $count++; - my $hglghtLOC =$count % 2; -# warn "highlightLOC ".$hglghtLOC; - - - $req = $dbh->prepare("select distinctrow itemtype from biblioitems order by itemtype"); + +# No need to test for data here. If you don't have itemcallnumbers, you probably know it. +# FIXME: Hardcoding to 5 chars on itemcallnum. +# + my $hascote = 1; + my $highcote = 5; + + $req = $dbh->prepare("select itemtype, description from itemtypes order by description"); $req->execute; - undef @select; - push @select,""; - while (my ($value) =$req->fetchrow) { - push @select, $value; - } - my $CGIitemtype=CGI::scrolling_list( -name => 'Filter', - -id => 'itemtype', - -values => \@select, - -size => 1, - -multiple => 0 ); - -# $req = $dbh->prepare("select distinctrow left(publishercode,75) from biblioitems order by publishercode"); -# $req->execute; -# undef @select; -# push @select,""; -# while (my ($value) =$req->fetchrow) { -# push @select, $value; -# } -# my $CGIpublisher=CGI::scrolling_list( -name => 'Filter', -# -id => 'Filter', -# -values => \@select, -# -size => 1, -# -multiple => 0 ); + my $CGIitemtype = $req->fetchall_arrayref({}); - undef @select; - push @select,""; - my $branches=GetBranches(); - my %select_branches; - $select_branches{""} = ""; - foreach my $branch (keys %$branches) { - push @select, $branch; - $select_branches{$branch} = $branches->{$branch}->{'branchname'}; + my $authvals = GetKohaAuthorisedValues("items.ccode"); + my @authvals; + foreach (sort {$authvals->{$a} cmp $authvals->{$b} || $a cmp $b} keys %$authvals) { + push @authvals, { code => $_, description => $authvals->{$_} }; } - my $CGIbranch=CGI::scrolling_list( -name => 'Filter', - -id => 'branch', - -values => \@select, - -labels => \%select_branches, - -size => 1, - -multiple => 0 ); - - $req = $dbh->prepare("select distinctrow holdingbranch from items order by holdingbranch"); - $req->execute; - undef @select; - push @select,""; - my $CGIlocation=CGI::scrolling_list( -name => 'Filter', - -id => 'holdingbranch', - -values => \@select, - -size => 1, - -multiple => 0 ); - my @mime = ( C4::Context->preference("MIME") ); - foreach my $mime (@mime){ -# warn "".$mime; + my $locations = GetKohaAuthorisedValues("items.location"); + my @locations; + foreach (sort keys %$locations) { + push @locations, { code => $_, description => "$_ - " . $locations->{$_} }; } - my $CGIextChoice=CGI::scrolling_list( - -name => 'MIME', - -id => 'MIME', - -values => \@mime, - -size => 1, - -multiple => 0 ); - - my @dels = ( C4::Context->preference("delimiter") ); - my $CGIsepChoice=CGI::scrolling_list( - -name => 'sep', - -id => 'sep', - -values => \@dels, - -size => 1, - -multiple => 0 ); + my @mime = ( map { +{type =>$_} } (split /[;:]/,C4::Context->preference("MIME")) ); $template->param(hasdewey=>$hasdewey, -# CGIFromDeweyClass => $CGIdewey, -# CGIToDeweyClass => $CGIdewey, - haslccn=> $haslccn, - hlghtlccn => $hlghtlccn, -# CGIFromLoCClass => $CGIlccn, -# CGIToLoCClass => $CGIlccn, - hascote=> $hascote, - hlghtcote => $hlghtcote, - hglghtDT => $hglghtDT, - hglghtPub => $hglghtPub, - hglghtPY => $hglghtPY, - hglghtHB => $hglghtHB, - hglghtLOC => $hglghtLOC, -# CGIFromCoteClass => $CGIcote, -# CGIToCoteClass => $CGIcote, + haslccn => $haslccn, + hascote => $hascote, CGIItemType => $CGIitemtype, -# CGIFromPublicationYear => $CGIpublicationyear, -# CGIToPublicationYear => $CGIpublicationyear, -# CGIPublisher => $CGIpublisher, - CGIBranch => $CGIbranch, - CGILocation => $CGIlocation, - CGIextChoice => $CGIextChoice, - CGIsepChoice => $CGIsepChoice + CGIBranch => GetBranchesLoop(C4::Context->userenv->{'branch'}), + locationloop => \@locations, + authvals => \@authvals, + CGIextChoice => \@mime, + CGIsepChoice => GetDelimiterChoices, + item_itype => $item_itype ); } output_html_with_http_headers $input, $cookie, $template->output; +## End of Main Body sub calculate { @@ -286,68 +188,93 @@ sub calculate { my @looprow; my %globalline; my $grantotal =0; + my $barcodelike = @$filters[13]; + my $barcodefilter = @$filters[14]; + my $not; + # extract parameters my $dbh = C4::Context->dbh; +# if barcodefilter is empty set as % +if($barcodefilter){ + # Check if barcodefilter is "like" or "not like" + if(!$barcodelike){ + $not = "not"; + } + # Change * to % + $barcodefilter =~ s/\*/%/g; +}else{ + $barcodefilter = "%"; +} + # Filters # Checking filters # my @loopfilter; - for (my $i=0;$i<=11;$i++) { + for (my $i=0;$i<=12;$i++) { my %cell; if ( @$filters[$i] ) { if ((($i==1) or ($i==3) or ($i==5) or ($i==9)) and (@$filters[$i-1])) { $cell{err} = 1 if (@$filters[$i]<@$filters[$i-1]) ; } $cell{filter} .= @$filters[$i]; - $cell{crit} .="Dewey Classification From" if ($i==0); - $cell{crit} .="Dewey Classification To" if ($i==1); - $cell{crit} .="Lccn Classification From" if ($i==2); - $cell{crit} .="Lccn Classification To" if ($i==3); - $cell{crit} .="Cote Classification From" if ($i==4); - $cell{crit} .="Cote Classification To" if ($i==5); - $cell{crit} .="Document type" if ($i==6); - $cell{crit} .="Publisher" if ($i==7); - $cell{crit} .="Publication year From" if ($i==8); - $cell{crit} .="Publication year To" if ($i==9); - $cell{crit} .="Branch :" if ($i==10); - $cell{crit} .="Location:" if ($i==11); + $cell{crit} .= + ($i== 0) ? "Dewey Classification From" : + ($i== 1) ? "Dewey Classification To" : + ($i== 2) ? "Lccn Classification From" : + ($i== 3) ? "Lccn Classification To" : + ($i== 4) ? "Item CallNumber From" : + ($i== 5) ? "Item CallNumber To" : + ($i== 6) ? "Item type" : + ($i== 7) ? "Publisher" : + ($i== 8) ? "Publication year From" : + ($i== 9) ? "Publication year To" : + ($i==10) ? "Library :" : + ($i==11) ? "Shelving Location :" : + ($i==12) ? "Collection Code :" : ''; push @loopfilter, \%cell; } } +# warn map {"filtres $_\n"} @filters[0..3]; + my @linefilter; -# warn "filtres ".@filters[0]; -# warn "filtres ".@filters[1]; -# warn "filtres ".@filters[2]; -# warn "filtres ".@filters[3]; - $linefilter[0] = @$filters[0] if ($line =~ /dewey/ ) ; $linefilter[1] = @$filters[1] if ($line =~ /dewey/ ) ; $linefilter[0] = @$filters[2] if ($line =~ /lccn/ ) ; $linefilter[1] = @$filters[3] if ($line =~ /lccn/ ) ; - $linefilter[0] = @$filters[4] if ($line =~ /items.itemcallnumber/ ) ; - $linefilter[1] = @$filters[5] if ($line =~ /items.itemcallnumber/ ) ; - $linefilter[0] = @$filters[6] if ($line =~ /itemtype/ ) ; + $linefilter[0] = @$filters[4] if ($line =~ /items\.itemcallnumber/ ) ; + $linefilter[1] = @$filters[5] if ($line =~ /items\.itemcallnumber/ ) ; + if (C4::Context->preference('item-level_itypes')) { + $linefilter[0] = @$filters[6] if ($line =~ /items\.itype/ ) ; + } else { + $linefilter[0] = @$filters[6] if ($line =~ /itemtype/ ) ; + } $linefilter[0] = @$filters[7] if ($line =~ /publishercode/ ) ; $linefilter[0] = @$filters[8] if ($line =~ /publicationyear/ ) ; $linefilter[1] = @$filters[9] if ($line =~ /publicationyear/ ) ; - @linefilter[0] = @$filters[10] if ($line =~ /items.homebranch/ ) ; - @linefilter[0] = @$filters[11] if ($line =~ /items.holdingbranch/ ) ; -# + $linefilter[0] = @$filters[10] if ($line =~ /items\.homebranch/ ) ; + $linefilter[0] = @$filters[11] if ($line =~ /items\.location/ ) ; + $linefilter[0] = @$filters[12] if ($line =~ /items\.ccode/ ) ; + my @colfilter ; $colfilter[0] = @$filters[0] if ($column =~ /dewey/ ) ; $colfilter[1] = @$filters[1] if ($column =~ /dewey/ ) ; $colfilter[0] = @$filters[2] if ($column =~ /lccn/ ) ; $colfilter[1] = @$filters[3] if ($column =~ /lccn/ ) ; - $colfilter[0] = @$filters[4] if ($column =~ /itemcallnumber/ ) ; - $colfilter[1] = @$filters[5] if ($column =~ /itemcallnumber/ ) ; - $colfilter[0] = @$filters[6] if ($column =~ /itemtype/ ) ; + $colfilter[0] = @$filters[4] if ($column =~ /items\.itemcallnumber/ ) ; + $colfilter[1] = @$filters[5] if ($column =~ /items\.itemcallnumber/ ) ; + if (C4::Context->preference('item-level_itypes')) { + $colfilter[0] = @$filters[6] if ($column =~ /items\.itype/ ) ; + } else { + $colfilter[0] = @$filters[6] if ($column =~ /itemtype/ ) ; + } $colfilter[0] = @$filters[7] if ($column =~ /publishercode/ ) ; $colfilter[0] = @$filters[8] if ($column =~ /publicationyear/ ) ; $colfilter[1] = @$filters[9] if ($column =~ /publicationyear/ ) ; - @colfilter[0] = @$filters[10] if ($column =~ /items.homebranch/ ) ; - @colfilter[0] = @$filters[11] if ($column =~ /items.holdingbranch/ ) ; + $colfilter[0] = @$filters[10] if ($column =~ /items\.homebranch/ ) ; + $colfilter[0] = @$filters[11] if ($column =~ /items\.location/ ) ; + $colfilter[0] = @$filters[12] if ($column =~ /items\.ccode/ ) ; # 1st, loop rows. my $linefield; @@ -360,10 +287,9 @@ sub calculate { }else { $linefield .= $line; } - - + my $strsth; - $strsth .= "select distinctrow $linefield from biblioitems left join items on (items.biblioitemnumber = biblioitems.biblioitemnumber) where $line is not null "; + $strsth .= "select distinctrow $linefield from biblioitems left join items on (items.biblioitemnumber = biblioitems.biblioitemnumber) where barcode $not LIKE ? AND $line is not null "; if ( @linefilter ) { if ($linefilter[1]){ $strsth .= " and $line >= ? " ; @@ -374,15 +300,15 @@ sub calculate { } } $strsth .=" order by $linefield"; - warn "". $strsth; + $debug and print STDERR "catalogue_stats SQL: $strsth\n"; my $sth = $dbh->prepare( $strsth ); if (( @linefilter ) and ($linefilter[1])){ - $sth->execute($linefilter[0],$linefilter[1]); - } elsif ($linefilter[0]) { - $sth->execute($linefilter[0]); + $sth->execute($barcodefilter,$linefilter[0],$linefilter[1]); + } elsif ($barcodefilter,$linefilter[0]) { + $sth->execute($barcodefilter,$linefilter[0]); } else { - $sth->execute; + $sth->execute($barcodefilter); } while ( my ($celvalue) = $sth->fetchrow) { my %cell; @@ -398,17 +324,21 @@ sub calculate { # 2nd, loop cols. my $colfield; if (($column =~/dewey/) and ($deweydigits)) { - $colfield .="left($column,$deweydigits)"; + $colfield = "left($column,$deweydigits)"; }elsif (($column=~/lccn/) and ($lccndigits)) { - $colfield .="left($column,$lccndigits)"; + $colfield = "left($column,$lccndigits)"; }elsif (($column=~/itemcallnumber/) and ($cotedigits)) { - $colfield .="left($column,$cotedigits)"; + $colfield = "left($column,$cotedigits)"; }else { - $colfield .= $column; + $colfield = $column; } - my $strsth2; - $strsth2 .= "select distinctrow $colfield from biblioitems left join items on (items.biblioitemnumber = biblioitems.biblioitemnumber) where $column is not null "; + my $strsth2 = " + SELECT distinctrow $colfield + FROM biblioitems + LEFT JOIN items + ON (items.biblioitemnumber = biblioitems.biblioitemnumber) + WHERE barcode $not LIKE ? AND $column IS NOT NULL "; if (( @colfilter ) and ($colfilter[1])) { $strsth2 .= " and $column> ? and $column< ?"; }elsif ($colfilter[0]){ @@ -416,14 +346,14 @@ sub calculate { $strsth2 .= " and $column LIKE ? "; } $strsth2 .= " order by $colfield"; - warn "". $strsth2; + $debug and print STDERR "SQL: $strsth2"; my $sth2 = $dbh->prepare( $strsth2 ); if ((@colfilter) and ($colfilter[1])) { - $sth2->execute($colfilter[0],$colfilter[1]); + $sth2->execute($barcodefilter,$colfilter[0],$colfilter[1]); } elsif ($colfilter[0]){ - $sth2->execute($colfilter[0]); + $sth2->execute($barcodefilter,$colfilter[0]); } else { - $sth2->execute; + $sth2->execute($barcodefilter); } while (my ($celvalue) = $sth2->fetchrow) { my %cell; @@ -437,7 +367,6 @@ sub calculate { push @loopcol, \%cell; } - my $i=0; my @totalcol; my $hilighted=-1; @@ -454,73 +383,78 @@ sub calculate { } # preparing calculation - my $strcalc .= "SELECT $linefield, $colfield, count( * ) FROM biblioitems LEFT JOIN items ON (items.biblioitemnumber = biblioitems.biblioitemnumber) WHERE 1"; + my $strcalc .= "SELECT $linefield, $colfield, count(*) FROM biblioitems LEFT JOIN items ON (items.biblioitemnumber = biblioitems.biblioitemnumber) WHERE 1 AND barcode $not like ? "; if (@$filters[0]){ @$filters[0]=~ s/\*/%/g; - $strcalc .= " AND dewey >" . @$filters[0] .""; + $strcalc .= " AND dewey >" . @$filters[0]; } if (@$filters[1]){ @$filters[1]=~ s/\*/%/g ; - $strcalc .= " AND dewey <" . @$filters[1] .""; - + $strcalc .= " AND dewey <" . @$filters[1]; } if (@$filters[2]){ @$filters[2]=~ s/\*/%/g ; - $strcalc .= " AND lccn >" . @$filters[2] ."" ; + $strcalc .= " AND lccn >" . @$filters[2]; } if (@$filters[3]){ @$filters[3]=~ s/\*/%/g; - $strcalc .= " AND lccn <" . @$filters[3] ."" ; + $strcalc .= " AND lccn <" . @$filters[3]; } if (@$filters[4]){ @$filters[4]=~ s/\*/%/g ; - $strcalc .= " AND items.itemcallnumber >=" . $dbh->quote(@$filters[4]) ."" ; + $strcalc .= " AND items.itemcallnumber >=" . $dbh->quote(@$filters[4]); } if (@$filters[5]){ @$filters[5]=~ s/\*/%/g; - $strcalc .= " AND items.itemcallnumber <=" . $dbh->quote(@$filters[5]) ."" ; + $strcalc .= " AND items.itemcallnumber <=" . $dbh->quote(@$filters[5]); } if (@$filters[6]){ @$filters[6]=~ s/\*/%/g; - $strcalc .= " AND biblioitems.itemtype like '" . @$filters[6] ."'"; + $strcalc .= " AND " . + (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype') + . " LIKE '" . @$filters[6] ."'"; } if (@$filters[7]){ @$filters[7]=~ s/\*/%/g; @$filters[7].="%" unless @$filters[7]=~/%/; - $strcalc .= " AND biblioitems.publishercode like \"" . @$filters[7] ."\""; + $strcalc .= " AND biblioitems.publishercode LIKE \"" . @$filters[7] ."\""; } if (@$filters[8]){ @$filters[8]=~ s/\*/%/g; - $strcalc .= " AND publicationyear >" . @$filters[8] ."" ; + $strcalc .= " AND publicationyear >" . @$filters[8]; } if (@$filters[9]){ @$filters[9]=~ s/\*/%/g; - $strcalc .= " AND publicationyear <" . @$filters[9] .""; + $strcalc .= " AND publicationyear <" . @$filters[9]; } if (@$filters[10]){ @$filters[10]=~ s/\*/%/g; - $strcalc .= " AND items.homebranch like '" . @$filters[10] ."'"; + $strcalc .= " AND items.homebranch LIKE '" . @$filters[10] ."'"; } if (@$filters[11]){ @$filters[11]=~ s/\*/%/g; - $strcalc .= " AND items.holdingbranch like '" . @$filters[11] ."'" if ( @$filters[11] ); + $strcalc .= " AND items.location LIKE '" . @$filters[11] ."'"; + } + if (@$filters[12]){ + @$filters[12]=~ s/\*/%/g; + $strcalc .= " AND items.ccode LIKE '" . @$filters[12] ."'"; } $strcalc .= " group by $linefield, $colfield order by $linefield,$colfield"; - warn "". $strcalc; + $debug and warn "SQL: $strcalc"; my $dbcalc = $dbh->prepare($strcalc); - $dbcalc->execute; + $dbcalc->execute($barcodefilter); # warn "filling table"; my $emptycol; while (my ($row, $col, $value) = $dbcalc->fetchrow) { # warn "filling table $row / $col / $value "; - $emptycol = 1 if ($col eq undef); - $col = "zzEMPTY" if ($col eq undef); - $row = "zzEMPTY" if ($row eq undef); + $emptycol = 1 if (!defined($col)); + $col = "zzEMPTY" if (!defined($col)); + $row = "zzEMPTY" if (!defined($row)); $table{$row}->{$col}+=$value; $table{$row}->{totalrow}+=$value; @@ -544,10 +478,9 @@ sub calculate { } push @looprow,{ 'rowtitle' => ($row eq "zzEMPTY")?"NULL":$row, 'loopcell' => \@loopcell, - 'hilighted' => ($hilighted >0), + 'hilighted' => ($hilighted *= -1 > 0), 'totalrow' => $table{$row}->{totalrow} }; - $hilighted = -$hilighted; } # warn "footer processing";