[ic] query2xls UserTag

Carl Bailey carl at endpoint.com
Sat Oct 17 19:26:52 UTC 2009


On Oct 17, 2009, at 2:28 PM, Mike Heins wrote:

> Haven't sent a new usertag skyward in a while....will work both
> with nvend and Interchange. Attached as well as inline.
>
> # Copyright 2009 Perusion <mikeh at perusion.com>
> #
> # 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.  See the LICENSE file for  
> details.
> #
> # $Id: query2xls.tag,v 1.0 2009-10-12 22:02:57 mheins Exp $
>
> UserTag query2xls AddAttr
> UserTag query2xls Version  $Revision: 1.00 $
> UserTag query2xls Documentation <<EOD
> =head1 NAME
>
> query2xls -- Create XLS spreadsheet files from a SQL query
>
> =head1 SYNOPSIS
>
>   [query2xls
>   		query="select field1,field2,field3 from table1"
> 		sheet-name="Sheet 1 of 1"
> 		file-name="file-to-create.xls"
> 		base="tablename"
> 		deliver=1
> 		width=NN
> 		max-width=NNN
> 	]
>
> or
>
>   [query2xls
>   		query.sheetname1="select * from table1"
>   		query.sheetname2="select * from table2"
>   		query.sheetname3="select * from table3"
> 		file-name="file-to-create.xls"
> 		base.sheetname2=table2
> 		width=NN
> 		deliver=1
> 		max-width=NNN
> 	]
>
> or
>
>   [query2xls
>   		query.0="select * from table1"
>   		query.1="select * from table2"
>   		query.1="select * from table3"
> 		file-name="file-to-create.xls"
> 		base.1=table2
> 		width=NN
> 		deliver=1
> 		max-width=NNN
> 	]
>
> =head1 DESCRIPTION
>
> The [query2xls] tag accepts one or more SQL queries and outputs an  
> XLS spreadsheet
> using the perl Spreadsheet::WriteExcel.
>
> Output is the contents of the file created unless the C<hide>  
> parameter is set.
> If there is an error during creation, undef will be returned and the  
> error will
> be logged and set in the error array.
>
> You can set the display width of the columns, and also (to some  
> extent) the max string size
> allowed.
>
> If you set the C<deliver> parameter, the file will be delivered as
> binary content vi the browser. Mime type can be specified in the
> C<type>, parameter. The default is I<application/vnd.ms-excel>.
>
> =head2 OPTIONS
>
> =over 4
>
> =item query
>
> Contains the query or queries. Uses standard Interchange array and  
> hash
> setting if desired. The sheet name will be the name of the hash  
> member --
> if you want capitalization and spaces in the sheet title you should
> format and pass a hash like:
>
>    [query2xls query=`{
>                    "Basic sheet" => "select sku,description as  
> title,price,image from products",
>                    "Full sheet"  => "select * from inventory",
>                    "Partial Sheet" => "select * from products where  
> price > 10",
>                }`
> 		 deliver=1 width=20]
>
> Will honor "as" if header columns are to be set.
>
> descending-brightness colors. The default value will cause
> the selected tab to have a color of #eeeeee, the first unselected
> tab will have #dddddd, the next #cccccc, etc. To create a yellow
> series, use #ffffxx.
>
> =item deliver
>
> Set to 1 if the spreadsheet is to be delivered as binary download.
>
> =item base
>
> The base table to find the table specified in the query. Can match the
> array and hash status of the C<query> object to mix tables.
>
> =item hide
>
> Standard ITL parameter to prevent output. Normally the tag outputs the
> binary spreadsheet suitable for writing to a file.
>
> =item file-name
>
> The name of the file to be written. Defaults to
>
> 	tmp/xls/SID/spreadsheet.xls
>
> where C<tmp> is the catalog ScratchDir and C<SID> is the session id.
>
> =item panel_width
>
> =back
>
> =head1 AUTHOR
>
> Mike Heins, <mikeh at perusion.com>.
>
> =head1 BUGS
>
> The usual number.
>
> =cut
> EOD
>
> UserTag query2xls Routine  <<EOR
> sub {
> 	my $opt = shift;
> 	my $query = $opt->{query};
> 	my $name = $opt->{file_name} || 'spreadsheet.xls';
>
> 	use vars qw/$Tag/;
> 	my $pf0 = $Vend::Cfg->{ProductFiles}[0];
>
> 	my %query;
> 	my %base;
> 	if(! ref $query) {
> #::logDebug("Think query is a scalar");
> 		my $q = $query;
> 		undef $query;
> 		$q =~ s/\s+$//;
> 		$q =~ s/^\s+//;
> 		$opt->{sheet_name} ||= 'Sheet 1';
> 		$query{$opt->{sheet_name}} = $q;
> 		$base{$opt->{sheet_name}} = $opt->{base} || $pf0;
> 	}
>
> 	if(ref $opt->{base} eq 'HASH') {
> 		%base = %{$opt->{base}};
> 	}
> 	elsif(ref $opt->{base} eq 'ARRAY') {
> 		my $i = 0;
> 		for(@{$opt->{base}}) {
> 			$base{$i++} = $_;
> 		}
> 	}
> 	
> 	if(ref $query eq 'HASH' ) {
> #::logDebug("Think query is a hash, of: " . ::uneval($query));
> 		for (sort keys %$query) {
> 			my $k = $_;
> 			my $v = $query->{$k};
> #::logDebug("processing query $k=$v");
> 			$query{$k} = $v;
> 			$base{$k} = $base{$k} || $opt->{base} || $pf0;
> 		}
> 	}
> 	elsif(ref $query eq 'ARRAY') {
> 		my $base_sheet = $opt->{sheet_name} || 'Sheet ';
> 		my $i = 1;
> 		for(@{$query}) {
> 			my $sn = $base_sheet . $i++;
> 			$query{$sn} = $_;
> 			$base{$sn} = $base{$sn} || $base{$i} || $opt->{base} || $pf0;
> 		}
> 	}
>
> #::logDebug("created query hash: " . ::uneval(\%query));
>
> 	use vars qw/$Tag/;
> 	my $dir = "$Vend::Cfg->{ScratchDir}/xls/$Vend::Session->{id}";
> 	$name = "$dir/$name";
> 	use File::Path;
> 	use Spreadsheet::WriteExcel;

May I suggest using Spreadsheet::WriteExcel::Big if available. (See  
usage in backup_database.coretag.)
This will support more rows per sheet for larger tables, bigger  
queries and newer versions of Excel.
Also consider supporting a maxrows setting in $opt.

>
> 	File::Path::mkpath($dir) unless -d $dir;
>
> 	my $Max_xls_string = 255;
>
> 	my $die = sub {
> 		my $msg = errmsg(@_);
> 		$Tag->error({ name => 'tab2xls', set => $msg });
> 		::logError("tab2xls: $msg");
> 		return undef;
> 	};
>
> 	my $xls = Spreadsheet::WriteExcel->new($name)
> 		or return $die->("Unable to create spreadsheet %s", $name);
>
> 	if($opt->{max_xls_string}) {
> 		$Max_xls_string = int($opt->{max_xls_string}) || 255;
> 		$xls->{_xls_strmax} = $Max_xls_string;
> 	}
>
> 	my @errors;
>
> 	my $h = 0;
>    for(sort keys %query) {
> 		my $sn   = $_;
> 		my $q    = $query{$_};
> #::logDebug("creating sheet: " . $sn);
> 		my $sheet = $xls->addworksheet($sn)
> 			or return $die->("Unable to create sheet '%s'", $sn);
> #::logDebug("created sheet object: " . $sheet);
> 		my $tab = $base{$sn} || $opt->{base} || $pf0;
> #::logDebug("referencing table: " . $tab);
> 		my $db = dbref($tab);
> 		$sheet->{_xls_strmax} = $Max_xls_string
> 			if defined $opt->{max_xls_string};
> 		
> 		my ($ary, $fn, $fa) = $db->query($q);
>
> 		if(! $ary) {
> 			my $err = $db->errstr;
> 			return $die->("%s query failed: %s\nerror: %s", 'query2xls', $q,  
> $err);
> 		}
> 	
> #::logDebug("creating header line: " . ::uneval(\@$fa));
>
> 		for(my $j = 0; $j <= @$fa; $j++) {
> 			$sheet->write_string(0, $j, $fa->[$j])
> 				if length $fa->[$j];
> 		}
>
> 		my $i = 1;
> 		for my $f (@$ary) {
> 			chomp;
> 	  #::logDebug("writing row $i: " . ::uneval(\@f));
> 			for(my $j = 0; $j < @$f; $j++) {
> 				$sheet->write_string($i, $j, $f->[$j])
> 					if length $f->[$j];
> 			}
> 			$i++;
> 		}
>  	
> 		if($opt->{width}) {
> 			$sheet->set_column(0, $#$fa, $opt->{width});
> 		}
> 		$h++;
>    }
>
> 	undef $xls;
> 	my $out = $Tag->file($name);
> 	unlink $name;
> 	if($opt->{deliver}) {
> 		$opt->{type} ||= 'application/vnd.ms-excel';
> 		$Tag->deliver({ type => $opt->{type}, body => $out });
> 		return length($out);
> 	}
> 	return $out;
> }
> EOR
> <query2xls.tag>_______________________________________________
> interchange-users mailing list
> interchange-users at icdevgroup.org
> http://www.icdevgroup.org/mailman/listinfo/interchange-users


Very handy tag indeed!  Thanks for this.

Regards,
Carl
. . . . . . . . . . . . . . . . . .
Carl Bailey
End Point Corp.
t: 919-323-8025
. . . . . . . . . . . . . . . . . .






More information about the interchange-users mailing list