Excuse me for sending unsolecitated kb of mail.

I'm developing a quiz-managing tool, based on a prototype already in use
at the Engineering Faculty of the University of Florence.

My users and myself use LaTeX + some target to set up a database of
questions, a perl script for selecting randomly from the database and a
template for printing the results and various reports.

I started re-writing the tool in a clearer way.

My fundamental goal is: let people use their preferred tool, either LaTeX
or Microsoft Word or Wiki (TWiki).

So I started enhancing (I hope) tpage to allow variables (eventually
interpolated into a template) to be specified as xml, perl dump file or
(in a near future version) tagged text file.

In these days I'm writing the "combinatorial machine" for extracting
random quizzes and computing marks.

The documentation is included in the attached script.

Questions:

1) Am I completely wrong? (is there a simpler approach?)
2) Has it already been done? (there are several quizz-making scripts, but
all force people to adopt a particular style, and few (none) allow LaTeX
and/or templates..)
3) (more techical): How can I share a stash between templates (as when
there is a [% PROCESS %] directive) and how can i cleanly extract
variables (includeing structured variables, like loh) defined in the
templates from a stash ?
4) I would like to use my tool as a CGI script, but I need to [% INCLUDE
%] or evaluate perl. I would like to change the way INCLUDE directives are
processed, to avoid arbitrary acceesses to filesystem, and wrap perl
inside a Safe module. Which is the suggested way of proceding?
I can write a plugin, but I would like a syntax similar to
Template keywords:

[% MYINCLUDE file %] istead of [% myinclude('file') %]

Is it possible?

Thanks.


-- 
Franco Bagnoli (franchino) <[EMAIL PROTECTED]>
virtual location: Dipartimento di Energetica "S. Stecco"
real location: Dip. Matematica Applicata "G. Sansone", Universita' Firenze,
Via S. Marta, 3 I-50139 Firenze, Italy. Tel. +39 0554796422, fax: +39 055471787
GPG Key fingerprint = 169D 9EA5 8FD3 7EDA E43A  9830 255F BCEC 0D63 3728
#!/usr/bin/perl -w

# itpage is an evolution (I hope) of tpage, from the tt2 toolkit.
# it does essentially the same things, with the 
# possibility of loading an xml or per file to be interpolated
# (whence the "i" in the name) in the template. 

# description is at the end

# to do: 
# -  add tagged text (wiki text) format : grammar definition file? 
#    (this could allow other fomats, like tex or rtf) 
# -  allow passing of stash between templates and saving,
#    so that templates can be used for defining variables 
# -  possibility of dynamically loading plugins to elaborate variables
#    before interpolation

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell


use strict;
use Template;
use Getopt::Long;
use XML::Simple; 
use Storable qw/nstore_fd/;
use Data::Dumper; 
use Safe;
use Pod::Text;
use IO::Handle;

use vars qw /$vars/;

#definition of parameter handling 

my $DEBUG = 1;

$vars = {}; #this is the hash ref interpolated in the template
my $define = {};
my $output = '';
my $outputhandle = \*STDOUT;
my $save = '';
my $forcearray=1;
my $forcecontent=1;
my $allowperl = 1;

GetOptions (
        'help|h|?' => \&help,
        'define|d=s' => $define,
        'load|l=s' => \&load,
        'save|s=s' => \&save,
        'output|o=s' => $output,
        'forcearray|a!' => \$forcearray,
        'forcecontent|c!' => \$forcecontent,
        'eval_perl|p!' => \$allowperl,
); 

addkeys($vars, $define);

# read from STDIN if no files specified
push(@ARGV, '-') unless (@ARGV);


# if just one file and no --output and it is named something 
# like try.tt2.tex or try.tex.tt2, then the ".tt2" 
# is striped off and output is try.tex
#

unless ($output) {
        if (@ARGV == 1 and $ARGV[0] =~ /\.tt2/) {
                ($output = $ARGV[0]) =~ s/\.tt2//;
        }
}

print "output: $output\n" if $DEBUG;

# open output file

if ($output) {
        open($outputhandle, ">$output") || die ("cannot open $output for writing"); 
}

# create a template processor 
my $template = Template->new({
    ABSOLUTE => 1,
    RELATIVE => 1,
    PRE_CHOMP=>1,
    POST_CHOMP=>0,
                OUTPUT_PATH=>".",
                EVAL_PERL=>$allowperl,
                PLUGIN_BASE => "",
});

# process each input file 
foreach my $file (@ARGV) {
                $vars->{INPUTFILE} = $ARGV[0];
                ($vars->{INPUTBASE} = $ARGV[0]) =~ s/\..*?$//;
    $file = \*STDIN if $file eq '-';
    $template->process($file, $vars, $output)
        || die $template->error();
}

# subroutines

# loads a structure into %{$vars}. 
# extensions: perl (.pl)  xml (.xml)
# storable (.stor)

# to do :
#
# others considered tagged text: 
# there should be somewhere a grammar file for parsing
# named as the extension (say something like tex.itp for 
# tagged tex file or wk.itp for wiki files


sub load {
        my $compartment = new Safe 'Load'; 
        my $load = $_[1];
        
        $Load::vars = {}; # refresh $Load::vars
        unless (-e $load) {
                die "Error: --load requires a valid file (you wrote \"$load\")";
        }
        
        if ($load =~ /\.pl$/) { # perl file
                print "load: found perl file $load\n" if $DEBUG;
                $compartment->rdo($load) or die "load error: $@\n";
        } elsif ($load =~ /\.xml$/) { #xml file
                if ($DEBUG) {
                        print "load: found xml file $load\n
                        forcearray: $forcearray\n
                        forcecontent: $forcecontent\n";
                } 
                
                $Load::vars=XMLin(
                        $load,
                        searchpath=>'.',
                        forcearray=>$forcearray,
                        forcecontent=>$forcecontent, 
                        cache=>'storable',
                        parseropts => [ProtocolEncoding => 'ISO-8859-1'],
                );
        } elsif ($load =~ /\.stor$/) { # storable
                if ($DEBUG) {
                        print "load: found storable file $load\n";
                }
                $Load::vars = retrieve ($load);
        } else { # tagged text  
                if ($DEBUG) {
                        print "load: found tagged text $load\n";
                }
                die "error: tagged text non jet implemented";
        }               
        addkeys($vars, $Load::vars);
        print "load: ended\n" if $DEBUG;
}


# saves %{$vars} on disk. 

sub save {
        $save = $_[1];
                
        open OUT, ">$save" or die ("cannot open $save");
        if ($save =~ /\.pl$/) { # perl file
                print "save: producing perl file $save\n" if $DEBUG;
                $Data::Dumper::Indent=1;
                print OUT Data::Dumper->Dump([$vars], ['vars']);                
        } elsif ($save =~ /\.xml$/) { #xml file
                print "save: producing xml file $save\n" if $DEBUG;
                print OUT XMLout($vars);
        } elsif ($save =~ /\.stor$/) { #storable file
                print "save: producing storable file $save\n" if $DEBUG;
                nstore_fd($vars,\*OUT);
        } else {
                print "save: producing tagged text file file $save\n" if $DEBUG;
                die "tagged file saving not yet implemented";
        }
        close OUT;
        print "save: ended\n" if $DEBUG;
}


sub addkeys {
        my ($dest, $src) = @_;
        my ($key, $value);
        while ( ($key,$value) = each(%{$src}) ) {
    $dest->{$key} = $value;
        }
}

sub help { 
        my $parser = Pod::Text->new (sentence => 0, width => 78);
        $parser->parse_from_filehandle(*DATA);
        exit; 
}



__END__


=head1 NAME

    itpage - Load definition files and process templates from command line 
    version 1.0.20020124
                
=head1 USAGE

    itpage [options] [file(s)]

=head1 DESCRIPTION

The B<itpage> script is a simple extension of 
B<tpage>, from the Template Toolkit processor (L<www.tt2.org>). 

It is used (almost) like the B<tpage> script, for processing 
template files, but variables to be interpolated can be defined into 
"definition files" in a variety of formats (see DEFINITION FILES). 

It can also be used for merging and changing format of definition files.

but see the TODO section.....

=head1 OPTIONS

  -h           (--help)             Print this page
  -d key=value (--define)           Define a variable on command line
  -l FILE      (--load)             Read a definition file. See DEFINITION FILES.
  -s FILE      (--save)             Save a definition file. See DEFINITION FILES.
  -o FILE      (--output)           Output file. See OUTPUT FILES
        -[no]a       (--[no]forcearray)   Option for XML files, see DEFINITION FILES
        -[no]c       (--[no]forcecontent) Option for XML files, see DEFINITION FILES
        -p           (--eval_perl)        Evaluate [% PERL %] ... [% END %] code 
blocks

=head1 DEFINITION FILES

Definition files are loaded using the --load option 
and interpolated into templates.

If more than one definition file is loaded, their contents are merged. 

Variables can also be defined on command line using the --define option.

Definition files (eventually merged) can be dumped to 
disk using the --save option.

There are several possible formats:

  xml: read and saved using XML::Simple. One can define variable to be 
             interpolated using an xml file:
                         
                         variables.xml: 
                         
                           <head> this is the first line </head>
                           <foot> this is the last line </foot>
                         
                         try.tt2.txt:
                          
                           [% head %]
          this is the body 
                                 
                                 [% foot %]
                                 
       and using itpage -noc -noa   -l variables.xml try.tt2.txt
                         
                         produces try.txt:
                         
                   this is the first line 
       this is the body 
       this is the last line 

                        WARNING: if you do not use the -noc -noa options, you have to 
                        write a template like 
                        
                           [% head.0.content %]
          this is the body 
                                 
                                 [% foot.0.content %]
                        
                        see the XML::Simple man pages.
                        
                                 
                        Variables can also be defined in a perl file 
                        (same format ad Data::Dumper) using the hash ref "$vars":
                        
                        example: variables.pl
                        
                        $vars = $vars = {
                          'head' => ' this is the first line ',
                        'foot' => ' this is the last line '
                        };

                        or as storable file (see Storable). 
                        
                        Tagged text format based on grammar will be implemented in 
                        a future version (se TODO).
                        
                        In a future version, variables defined inside a template will 
be merged
                        in the %{$vars} hash, so that also tt2 files will be used as 
definition files
                        (see TODO).
                        
                        By combining --load and --save one can merge definition 
                        files and/or convert from one format to another.               
         
                        
=head1 OUTPUT FILES

More than one file can be specified on command line.

Output is on STDOUT unless the --output option is passed or, 
if there is just one file on command line, it contains the ".tt2" ssubstring.
In this latter case, the ".tt2" string is stripped, definig the output file.

Example: B<itpage try.tt2.txt> processes try.tt2.txt and writes on try.txt.

=head1 TODO
        
                1) Debugging 
                
                2) Tagged text format: 
                
                        for instance: 

                        HEAD: this is the first line
                        FOOT: and this is the last one 

                        VECTOR: 
                                * first component VECTOR.0
                                * second component VECTOR.1
                                * third component VECTOR.2

                        HASH: 
                                first: this is the component HASH.first
                                second: this is the component HASH.second

                        or: 

                        \Q This is a question for a quizz file in \LaTeX
                        \R right answer
                        \W wrong answer
                        \Q And this as another question....

                        The tags are defined in a grammar file.

                        Why this? I developed itpage as a first step toward a script 
for managing
                        multiple-choiches quiz. In this way people can define 
databases of questions 
                        using their preferred tool, LaTeX in particolar, and validate 
the syntax
                        (the LaTeX syntax, not the tag one) simply by compiling the 
file.  
                        The first example instead is inspired by the idea of wiki 
syntax: write 
                        like you use doing in e-mails an let the computer do the work.
                        But modernity calls for xml files..... 

                        Then, a plugin (next todo) can elaborate the variables (for 
instance, 
                        build the quiz or compute marks. The elaborated variables can 
be 
                        saved on disk by using the --save option.

                        All "reports" are produced by means of a suitable template. 
 
                3) Plugins. One should be able to elaborate variables by loading a 
                        small perl fragment. In this way all input processing is done 
by the
                        script, and output by templates.
                
                4) Allow templates to export variables. Templates themself can be used 
to 
                        export variables to other templates (similar to using the [% 
PROCESS %] 
                        statement). I have found no "clean" way of extracting 
variables from stash 
                        except custom filtering internal from external variables. 
Maybe it is 
                        for templates to share the stash.             
                        
                5) Relax syntax. --load can be eliminated, I suppose. 
                
                6) Configuration files (i.e. something similar to ttree)
                
                7) Possibility of a graphics interface for editing variables (XML 
editors?)
                        and choosing template files. Needed for Windows users....
                        Maybe using a miniserver http?  
                        
=head1 AUTHOR

Franco (franchino) Bagnoli E<lt>[EMAIL PROTECTED]<gt>


=head1 VERSION

1.0.20020124 (24/01/2002)

=head1 COPYRIGHT

  Copyright (C) 1996-2001 Andy Wardley.  All Rights Reserved.
  Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
        
  Copyright (C) 2001 Franco Bagnoli.

This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=head1 SEE ALSO

L<tpage|Template::Tools::ttpage>
L<ttree|Template::Tools::ttree>
L<XML::Simple>
L<Data::Dumper>

Reply via email to