#!/usr/bin/perl use strict; use utf8::all; use Getopt::Long qw(:config no_auto_abbrev ); use Pod::Usage; use Cwd; use MLDBM qw(DB_File); use DB_File; use Fcntl; my %S=(n=>1); GetOptions( \%S, qw{help|h|? man l s=i o=s n=i srand=i pdf pdf2 nosol nores test showrand nohead no cgi db h h1 dl dm test catalog=s tit=s aut=s outdir=s } ) or die "Specify the --help (or -?) option for usage information.\n"; pod2usage(2) if $S{help}; pod2usage(-exitstatus => 0, -verbose => 2) if $S{man}; if($S{no}){$S{nosol} = $S{nores} = $S{nohead} = 1 } if($S{srand}){srand($S{srand})} use Data::Dumper; use Exercise::Gen::Let; ## use Skel::Data; skimport(); use Parse::DSLUtils; skimport(); my $cwd=cwd(); $S{outdir} //="OUT"; $S{outdir} = "$cwd/$S{outdir}" unless $S{outdir} =~ m!^[./~]!; $S{outdir} =~ s!^\.!$cwd/! if $S{outdir} =~ m!^[.][^.]!; mkdir $S{outdir}; my @catlist = (); push( @catlist, (split(/:/,$S{catalog})) ) if $S{catalog}; push( @catlist, "$ENV{HOME}/.mex_catalogue") if -f "$ENV{HOME}/.mex_catalogue"; my $state = {}; if($S{h}) {print EXE(); exit 0} ### Mex skeleton if($S{h1}){print GEXER(); exit 0} ### Test skeleton my $f = shift or die("usage: $0 [options] file\n"); die("File $f does not exist!\n") unless -e $f; my ($nam1,$nam2)=(".",""); ($f,$nam1,$nam2)=get_mex_filename($f,@catlist); ## print "F=$f,dir=$nam1,file=$nam2"; my $base = $nam2; $base =~ s!\.(.{1,5}$)!!; ## print STDERR "Debug: $nam1/=/$nam2; dir=$S{outdir}\n"; if($S{d}) { Exercise::Gen::Let::parseFile($f); print Dumper(\%Exercise::Gen::Let::sem ); exit 0} if($S{dl}){ Exercise::Gen::Let::lexdebug($f); exit 0} if($f =~ m/(.*)\.test$/){$S{test}=1; $S{o} ||="$S{outdir}/$1.tex"; } else { $S{o} ||="$S{outdir}/$base.tex"; } for my $id( ($S{n} ==1 ? ("") : (map {".$_" } 1 .. $S{n}))){ if($S{dm}){ debugmex ($f,$S{o})} else { mex2tex ({ ($S{nosol}? (nosol=>$S{nosol}) :()), ($S{nores}? (nores=>$S{nores}) :()), ($S{nohead}? (nohead=>$S{nohead}) :()), ($S{s} ? (s=>$S{s}) :()), ($S{tit} ? (title=>$S{tit}) :()), ($S{aut} ? (author=>$S{aut}) :()), },[$f],"$S{o}$id")} } if($S{pdf}){ tex2pdf("$S{outdir}/$base.tex"); system("jpdf $S{outdir}/$base.pdf") == 0 or system("xpdf $S{outdir}/$base.pdf") == 0 or system("open $S{outdir}/$base.pdf") == 0 or die("Error no PDF found\n"); } if($S{pdf2}){ tex2pdf("$S{outdir}/$base.tex"); } sub tex2pdf{ my $file=shift; my $pp = undef; if( -x "/usr/local/bin/ppdflatex" ){ $pp ||='/usr/local/bin/ppdflatex';} if( -x "/usr/bin/ppdflatex" ){ $pp ||='/usr/bin/ppdflatex';} if( -x "/bin/ppdflatex" ){ $pp ||='/bin/ppdflatex';} if( -x "/opt/bin/ppdflatex" ){ $pp ||='/opt/bin/ppdflatex';} if( -x "/opt/local/bin/ppdflatex" ){ $pp ||='/opt/local/bin/ppdflatex';} if($pp){ system("$pp -Q $file")== 0 or die("Error in ppdflatex\n");} else { my $op = ""; if( $file =~ m!(.+)/(.*)!) {$op="--output-directory $1"} system("pdflatex -interaction nonstopmode $op $file")== 0 or die("Error in ppdflatex\n");} } sub debugmex{ ## debug multi-exercise : generates a exe+solu+... tex file my ($f,$o)= @_; Exercise::Gen::Let::parseFile($f); Exercise::Gen::Let::calcsem(); my $t= $Exercise::Gen::Let::sem{texblocks}; my $v= $Exercise::Gen::Let::sem{ts}; my $tab=""; if($S{showrand}){ for (keys %$v ){ $tab.= sprintf("%5s | %20s | %s\n", $_, $v->{$_}{fv}, $v->{$_}{v1})}; } open (F,">:utf8",$o) or die("cant create output file $o\n");; print F LATDEBUG({ ($S{showrand} ?( tab => $tab):()), map {("_$_"=>$t->{$_}{v1} )} qw(sugestion result author title usepackage resolution obs question) }); close F; if($S{cgi}){ my $verify = $Exercise::Gen::Let::sem{verify}; my $vindex = $Exercise::Gen::Let::vindex; if($S{db}){ mex2db($verify, $base, $S{outdir}, $vindex); } else{ mex2cgi($verify, $base, $S{outdir}, $vindex); } } } sub mex2db{ my ($verify, $base, $outdir, $vindex) = @_; my %EXER; my $x= tie %EXER, 'MLDBM' , "MEX/mex.db", O_CREAT|O_RDWR, 0644 or die $! ; my $topid = $EXER{topid}; $EXER{topid} = $topid+1; my $tmp = $EXER{$topid}; $tmp->{enun} = scalar(`ppdflatex -p -Q $outdir/$base.tex | tth -a -e1 -u -r`); my $tmp2 = $tmp->{answer}; for (0..$vindex-1){ $tmp2->[$_] = $verify->[$_]{v1}; } $tmp->{answer} = $tmp2; $tmp->{n} = $vindex; $EXER{$topid} = $tmp; print "New exercise is id: ".$topid."\n"; } sub mex2cgi{ my ($verify, $base, $outdir, $vindex) = @_; open (F,">:utf8", "$outdir/$base.cgi") or die("cant create output file $outdir/$base.cgi\n"); print F CGIHEAD({base => $base, enunciado => scalar( `tth -a -e1 -u -r < $S{outdir}/$base.tex`)}); for (0..$vindex-1){ print F CGIQUESTIF({answer => $verify->[$_]{v1}, number => $_+1}); } print F CGIFOOT1({}); for (0.. $vindex-1){ print F CGIQUESTELSE({number => $_+1}); } print F CGIFOOT2({}); close F; } sub get_mex_filename{ my ($f,$cata)=@_; my $a = `mex_catalogue -meta -cata "$cata" "$f"`; my $b = eval $a; return ("$b->[0]{dir}/$b->[0]{file}",$b->[0]{dir},$b->[0]{file}); } sub _old_get_mex_filename{ my ($f,$cata)=@_; my ($nam1,$nam2)=(".",""); if(-f $f) { if($f =~ m!(.*)/(.*)!){$nam1 = $1; $nam2 = $2; } else { $nam2 = $f; }} elsif(defined $cata->{$f} and $f =~ m{^__}){ ($nam1,$nam2)= split("=",$cata->{$f}); my $aux=$f; $f="$nam1/$nam2"; $nam2=$aux;} elsif(defined $cata->{$f}){ $nam2=$f; $nam1=$cata->{$f}[0]{dir}; $f="$nam1/$nam2";} elsif($f =~ m!(.*)/(.*)! and defined $cata->{$2}){ my ($x1,$x2)=($1,$2); for( @{ $cata->{$x2}} ){ if($_->{dir} =~ m{$x1}){ $nam2=$x2; $nam1=$_->{dir}; $f="$nam1/$nam2"; last} } die "cant find $f\n" unless $nam2} else { die "cant find $f\n";} return ($f,$nam1,$nam2); } sub mex2tex{ ## multi-exercise : generates a exe.tex file my %opt =(n => 1,nores=>0, nosol => 0, s => 0,nohead=>0); if(ref($_[0]) eq "HASH") {%opt = (%opt , %{shift(@_)}) } ; my ($f,$o)= @_; my @files = ref($f)? @$f : ($f); my @q = (); my @r = (); my $rule = "\n\n....................\n\n"; my @resol = (); my ($enu,$sol,$res,$t); for my $f1 (@files){ Exercise::Gen::Let::parseFile($f1); Exercise::Gen::Let::calcsem(); ## Exercise::Gen::Let::calcnewchoice(); $t= $Exercise::Gen::Let::sem{texblocks}; $opt{title} //= $t->{title}{v1}; $opt{subtitle} //= $t->{author}{v1}; $opt{data} //= $t->{data}{v1}; push(@q ,{q=> $t->{question}{v1} }); push(@r ,{q=> $t->{question}{v1} . $rule . $t->{result}{v1} }); push(@resol ,{q=> $t->{question}{v1} . $rule . $t->{resolution}{v1} }); } my %meta = ( title => $opt{title}, subtitle => $opt{author} || '\mbox{}' , data => $opt{data} || '\today' ); if ($opt{s}==0){ $enu= join("\n\n\\hrule\n\n",map {$_->{q}} @q); $sol= join("\n\n\\hrule\n\n",map {$_->{q}} @r); $res= join("\n\n\\hrule\n\n",map {$_->{q}} @resol); } elsif($opt{s}==1){ $enu= question( @q); $sol= question( @r); $res= question( @resol); } elsif($opt{s}==2){ $enu = ENUN({ questions => question( @q), %meta }); $sol = ENUN({ questions => question( @r), %meta }); $res = ENUN({ questions => question( @resol), %meta }); } my $body= join('\newpage ', ( $enu, ($opt{nosol} ? () : ($sol)), ($opt{nores} ? () : ($res)), )); if($opt{nohead}){ open (F,">:utf8",$o) or die("cant create output file $o\n"); print F $enu; close F; open (F,">:utf8","$o-sol") or die("cant create output file $o\n"); print F $res; close F; } else { open (F,">:utf8",$o) or die("cant create output file $o\n"); print F TEX( { _usepackage => $t->{usepackage}{v1}, tex_body => $body, }); close F; } } sub mex{ ## my %opt =(s => 1); if(ref($_[0]) eq "HASH") {%opt = (%opt , %{shift(@_)}) } ; my ($f)= shift; my $q = ""; my $r = ""; my $resol = ""; my ($enu,$sol,$res,$t); my $rule = "\n\n....................\n\n"; Exercise::Gen::Let::parseFile($f); Exercise::Gen::Let::calcsem(); ## Exercise::Gen::Let::calcnewchoice(); $t= $Exercise::Gen::Let::sem{texblocks}; $opt{title} //= $t->{title}{v1}; $opt{subtitle} //= $t->{author}{v1}; $opt{data} //= $t->{data}{v1}; $q = {q=> $t->{question}{v1} }; $r = {q=> $t->{result}{v1} }; $resol = {q=> $t->{resolution}{v1} }; my %meta = ( title => $opt{title}, subtitle => $opt{author} || '\mbox{}' , data => $opt{data} || '\today' ); if ($opt{s}==0){ $enu= $q->{q}; $sol= $q->{q}; $res= $q->{q}; } elsif($opt{s}==1){ $enu= question( $q); $sol= question( $r); $res= question( $resol); } ($enu,$sol,$res,\%meta) } sub mtest2tex_no{ my ($f,$o)= @_; my $txt=`cat $f`; my $aux; #### =mtestfile($txt); my %meta= (title => $aux->{title}, _usepackage => $aux->{usepackage} || '%no extra packages' , subtitle => $aux->{subtitle} || '\mbox{}' , data => $aux->{data} || '\today' ); $state->{enunpage} = ENUN({%meta, questions=> $aux->{txt} }); $state->{resultpage}= ENUN({%meta, questions=> $state->{resultpage_q}}); $state->{resolutionpage}=ENUN({%meta,questions=> $state->{resolutionpage_q}}); open (F,">",$o) or die; print F TSKEL($state); close F; } sub saveq{ my ($q,$resul,$resol)=@_; $state->{resultpage_q} .=ENU2({_question => $resul}); $state->{resolutionpage_q}.=ENU2({_question => $resol}); ENU2({_question => $q}); } sub proc_file{ my $f=shift; print STDERR "Debug: processing '$f'\n"; die("Error: cant find $f\n") unless -f $f; my $e=Exercise::Gen->new($f) or warn("Fail to process $f\n"); my $aux= $e->latex(); saveq($aux->{_question},$aux->{_resolution}, $aux->{_result}); } __DATA__ __jjaulas__ \RequirePackage[a4paper,top=2cm,left=2cm,right=2cm,bottom=1.5cm,nohead,nofoot]{geometry} \parindent 0pt \parskip 3pt \fvset{fontsize=\small, numbers=left, frame=leftline, numberblanklines=false} \newcounter{quest} %\def\theenumi{\alpha{enumi}} \newenvironment{questao}[2][]{ \def\theenumi{\alph{enumi})} \addtocounter{quest}{1}{\bf Quest\~ao \thequest\ (#2)} \marginpar{#1} }{ \addvspace{2mm}\mbox{}\hrule\mbox{} } \newenvironment{exame}[3]{\noindent\fbox{\begin{minipage}{\textwidth}\begin{center} {\Huge #1 \\ \huge #2 \\ \large #3\\} \end{center}\end{minipage}} \mbox{}\\}{} \newcommand{\exametitle}[3]{\noindent\fbox{\begin{minipage}{\textwidth}\begin{center} {\Huge #1 \\ \huge #2 \\ \large #3\\} \end{center}\end{minipage}} \mbox{}\\} __ENU3__ \begin{questao}{} [% _question %] \footnotetext{\thequest:[% _result %]} \end{questao} __ENU2__ \begin{questao}{} [% _question %] \end{questao} __ENU__ \section{Enunciado} [% _question %] __SUG__ \section{Sugestão} [% _sugestion %] __RES__ [% _result %] __LATDEBUG__ [%default:{ _usepackage=>'%no #usepackage provided', _sugestion =>'... no sugestion provided', _resolution=>'... no resolution provided', _author=>'', data=>'\today', _obs=>'', tab=> '', }%] \documentclass[portuges,a4paper]{article} \usepackage{babel} \usepackage[mathletters]{ucs} \usepackage[utf8x]{inputenc} \usepackage[T1]{fontenc} \usepackage{fancyvrb} %\usepackage{t1enc} %\usepackage{jjaulas} \usepackage{graphicx} [% _usepackage %] \newif\iftth [%!jjaulas %] \begin{document} \exametitle{[% _title %]}{[% _author %]\mbox{}}{[%data%]} \subsection*{Question} [% _question %] \subsection*{Sugestion} [% _sugestion %] \subsection*{Resolution} [% _resolution %] \iftth \begin{html}\end{html} \fi \subsection*{Result} \iftth \begin{html}
\end{html} \fi \subsection*{Obs} [% _obs %] \subsection*{Random choices} {\small \begin{verbatim} [% tab %] \end{verbatim} } \end{document} __EXE__ #title: #author: #date: (def=\today) #let: n a =[ ]; a ~ b; #question: \begin{enumerate} \item \end{enumerate} #sugestion: #resolution: #result: #Verify: #usepackage % \usepackage{...} latex preambula __GEXER__ template: ~/svn/gexer/Exercise-Gen/example/jj.tskel problems: ~/svn/gexer/ versions: 4 title: Teste Matemática Jun 2011 subtitle: exemplo inocente =*Introdução Este teste destina-se a preparar os alunos para o prémio nobel (pedir não custa). #q{EqQua2.txt} #q{DerivadaDoProduto.txt} #q[2]{Estatica.txt} #q{DerivadaDaFuncaoComposta.txt} #q{inline{Quanto é $3 \times 9$?}{$9+9+9=27$}{27} } __ENUN__ \exametitle{[% title %]}{[% subtitle %]}{[%data%]} [% questions %] __TSKEL__ [%default:{_usepackage=>'', -author=> '', }%] \documentclass[portuges,a4paper]{article} \usepackage{babel} \usepackage[mathletters]{ucs} \usepackage[utf8x]{inputenc} \usepackage[T1]{fontenc} \usepackage{fancyvrb} %\usepackage{t1enc} %\usepackage{jjaulas} \usepackage{graphicx} [% _usepackage %] [%!jjaulas %] \begin{document} %\fvset{fontsize=\small, frame=single, numberblanklines=false} [% enunpage %] \newpage [% resultpage %] \newpage [% resolutionpage %] \end{document} __question__ \begin{questao}{} [% q %] \end{questao} __TEX__ [%default:{_usepackage=>'', tex_title => '', }%] \documentclass[portuges,a4paper]{article} \usepackage{babel} \usepackage[mathletters]{ucs} \usepackage[utf8x]{inputenc} \usepackage[T1]{fontenc} \usepackage{fancyvrb} %\usepackage{t1enc} %\usepackage{jjaulas} \usepackage{graphicx} [% _usepackage %] [%!jjaulas %] \begin{document} %\fvset{fontsize=\small, frame=single, numberblanklines=false} [% tex_title %] [% tex_body %] \end{document} __CGIHEAD__ #!/usr/bin/perl use CGI q(:all); print header(-charset=>'utf-8'); print start_html("[% base %]"); ###system("cat [% base %].html"); print q{[% enunciado %]}; print start_form; ## gerado pelo verify block if(param()) { __CGIQUESTIF__ ## pergunta [% number %] $answer = param('p[% number %]'); print "Pergunta 1: ", textfield('p[% number %]'); if ($answer eq [% answer %]) { print 'Correcto!'; } else{ print 'Errado!'; } print "