Thanks, Karl-Dieter. I've made a little more progress: 1. Supports solutions now, including different instances of same problem. 2. No more \begin{sageprocesstext}{Problem}, just \begin{sagetext} now; less clutter. 3. It's a single \include that you put right after \begin{document}, but you still have to make the change to sagetex.sty.
I don't think I'm ready to submit a pull request until I know what I'm doing better. I think I'll email Dan and we can discuss this offline. I also don't feel the need to try to pressure him to add stuff to SageTeX just for my pet project...it's up to him. I'll post here if he and I get any important hurdles cleared. I'll attach the files as they currently stand, in case you want to play with this at all. It's been...extremely minimally tested. But there is a working example! That's all anyone needs, right? :) Nathan On Friday, March 8, 2013 1:41:26 PM UTC-5, kcrisman wrote: > > Very impressive, Nathan. > > >> So, here's some types of advice I'd love to receive on this: >> >> 1. Should I actually be suggesting changes to sagetex.sty? Or is there a >> way to do that in my own .sty file? I couldn't call Dan's ST@stuff >> commands from my own document, because I think TeX @ things are local or >> something...*mumbles cluelessly* >> >> 2. Not sure if I can run sagesilent blocks when not inside a document, >> which I'd need to do to move the class definition into a .sty file. Or >> should I use something like \AtBeginDocument? Not sure if that will work >> either. TeX is so touchy, and I'm rather new at this. >> >> 3. The goal is, of course, to get this nicely packaged into a single .sty >> file that includes sagetex.sty, and then add the features mentioned above, >> and then contribute it...someplace. When the time comes, what's the >> appropriate "someplace"? >> >>> >>>> > > Maybe a pull request at https://bitbucket.org/ddrake/sagetex/ ? > > I think you and Dan should talk a little about whether a new .sty file or > adding a new block type is better - and what the name of the block should > be, etc. > -- You received this message because you are subscribed to the Google Groups "sage-edu" group. To unsubscribe from this group and stop receiving emails from it, send an email to sage-edu+unsubscr...@googlegroups.com. To post to this group, send email to sage-edu@googlegroups.com. Visit this group at http://groups.google.com/group/sage-edu?hl=en. For more options, visit https://groups.google.com/groups/opt_out.
%!TEX TS-program = sage \documentclass{article} \usepackage{sagetex} \begin{document} \include{sagetex_exam_include} \begin{sagetext} First Problem a = randint(1,20) b = randint(1,20) What is $\sage{a}+\sage{b}$? $\sage{a+b}$ \end{sagetext} Problem 1: \insertproblem[one]{First Problem} Problem 2: \insertproblem[two]{First Problem} \hfill Answer 2: \insertsolution[two] Answer 1: \insertsolution[one] \end{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Include this file in any document that wishes to use Sage to create exams. % Then define problems like this: % %%%%%%%%%%%%%%%%%%%%%%%%%%%% % % \begin{sagetext} % Problem type name % % initialization code % % Text of problem % % Text of solution % \end{sagetext} % %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Example: % % \begin{sagetext} % Simple addition % % a = randint(1,10) % b = randint(1,10) % % What is the sum of $\sage{a}$ and $\sage{b}$? % % $\sage{a+b}$ % \end{sagetext} % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Then to insert a problem, use \insertproblem{type name}, or \insertproblem[instance name]{type name}. % Then to insert a solution, use \insertsolution (which inserts the next solution in the order in which the % problems were defined) or \insertsolution[instance name] (to insert the solution that goes with a problem % to which you've given a particular name). % % Examples: % % Problem 1: \insertproblem{Simple addition} % Problem 2: \insertproblem{Simple addition} % Answer 1: \insertsolution % Answer 2: \insertsolution % % or % % Problem 1: \insertproblem[one]{Simple addition} % Problem 2: \insertproblem[two]{Simple addition} % The second problem's answer is \insertsolution[two], but the first's is \insertsolution[one]. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{sagesilent} from string import * def safepop ( ary ): return ary.pop( 0 ) if len( ary ) > 0 else '' class Problem: allProblems = {} allInstances = {} lastSolutionIndex = -1 def __init__ ( self, text ): text = text.split( '\n\n' ) self._name = safepop( text ) self._setup = safepop( text ) self._text = safepop( text ) self._soln = safepop( text ) Problem.allProblems[self._name] = self def instantiate ( self ): vars = {} sage_eval( 'None', cmds=self._setup, locals=vars ) copy = self._text while copy.find( '\\sage{' ) > -1: start = copy.find( '\\sage{' ) stop = copy.find( '}', start ) inside = copy[start+6:stop] copy = copy[:start] + str( sage_eval( inside, locals=vars ) ) + copy[stop+1:] result = { 'text' : copy } copy = self._soln while copy.find( '\\sage{' ) > -1: start = copy.find( '\\sage{' ) stop = copy.find( '}', start ) inside = copy[start+6:stop] copy = copy[:start] + str( sage_eval( inside, locals=vars ) ) + copy[stop+1:] result['soln'] = copy return result @staticmethod def insert ( name, key ): newprob = Problem.allProblems[name].instantiate() if key in Problem.allInstances: Problem.allInstances[key] += [ newprob ] else: Problem.allInstances[key] = [ newprob ] return newprob['text'] @staticmethod def insertSolution ( key ): if ( key in Problem.allInstances ) and ( len( Problem.allInstances[key] ) > 0 ): return Problem.allInstances[key].pop( 0 )['soln'] else: return '' \end{sagesilent} \newcommand\insertproblem[2][ ]{\sagestr{Problem.insert(r"""#2""",r"""#1""")}} \newcommand\insertsolution[1][ ]{\sagestr{Problem.insertSolution(r"""#1""")}} \sagetextprocessor{Problem}
%% %% This is file `sagetex.sty', %% generated with the docstrip utility. %% %% The original source files were: %% %% sagetex.dtx (with options: `latex') %% py-and-sty.dtx (with options: `latex') %% %% This is a generated file. It is part of the SageTeX package. %% %% Copyright (C) 2008--2012 by Dan Drake <ddr...@member.ams.org> %% %% 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, see <http://www.gnu.org/licenses/>. %% \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{sagetex} [2012/01/16 v2.3.3-69dcb0eb93de embedding Sage into LaTeX documents] \newcommand{\ST@ver}{2012/01/16 v2.3.3-69dcb0eb93de} \RequirePackage{verbatim} \RequirePackage{fancyvrb} \RequirePackage{listings} \RequirePackage{color} \lstdefinelanguage{Sage}[]{Python} {morekeywords={False,sage,True},sensitive=true} \lstdefinelanguage{SageOutput}[]{} {morekeywords={False,True},sensitive=true} \lstdefinestyle{DefaultSageInputOutput}{ nolol, identifierstyle=, name=sagecommandline, xleftmargin=5pt, numbersep=5pt, aboveskip=0pt, belowskip=0pt, breaklines=true, numberstyle=\footnotesize, numbers=right } \lstdefinestyle{DefaultSageInput}{ language=Sage, style=DefaultSageInputOutput, basicstyle={\ttfamily\bfseries}, commentstyle={\ttfamily\color{dgreencolor}}, keywordstyle={\ttfamily\color{dbluecolor}\bfseries}, stringstyle={\ttfamily\color{dgraycolor}\bfseries}, } \lstdefinestyle{DefaultSageOutput}{ language=SageOutput, style=DefaultSageInputOutput, basicstyle={\ttfamily}, commentstyle={\ttfamily\color{dgreencolor}}, keywordstyle={\ttfamily\color{dbluecolor}}, stringstyle={\ttfamily\color{dgraycolor}}, } \lstdefinestyle{SageInput}{ style=DefaultSageInput, } \lstdefinestyle{SageOutput}{ style=DefaultSageOutput, } \definecolor{dbluecolor}{rgb}{0.01,0.02,0.7} \definecolor{dgreencolor}{rgb}{0.2,0.4,0.0} \definecolor{dgraycolor}{rgb}{0.30,0.3,0.30} \RequirePackage{graphicx} \RequirePackage{makecmds} \RequirePackage{ifpdf} \RequirePackage{ifthen} \IfFileExists{ifxetex.sty}{ \RequirePackage{ifxetex} }{ \newboolean{xetex} \setboolean{xetex}{false}} \newcounter{ST@inline} \newcounter{ST@plot} \newcounter{ST@cmdline} \setcounter{ST@inline}{0} \setcounter{ST@plot}{0} \setcounter{ST@cmdline}{0} \newlength{\sagetexindent} \setlength{\sagetexindent}{5ex} \newif\ifST@paused \ST@pausedfalse \AtBeginDocument{\@ifundefined{ST@final}{% \newwrite\ST@sf% \immediate\openout\ST@sf=\jobname.sagetex.sage% \newcommand{\ST@wsf}[1]{\immediate\write\ST@sf{#1}}% \ST@wsf{% # -*- encoding: utf-8 -*-^^J% # This file (\jobname.sagetex.sage) was *autogenerated* from \jobname.tex with sagetex.sty version \ST@ver.^^J% import sagetex^^J% _st_ = sagetex.SageTeXProcessor('\jobname', version='\ST@ver', version_check=\ST@versioncheck)}}% {\newcommand{\ST@wsf}[1]{\relax}}} \newcommand{\ST@dodfsetup}{% \@ifundefined{ST@diddfsetup}{% \newwrite\ST@df% \immediate\openout\ST@df=\jobname_doctest.sage% \immediate\write\ST@df{r"""^^J% This file was *autogenerated* from \jobname.tex with sagetex.sty^^J% version \ST@ver. It contains the contents of all the^^J% sageexample environments from \jobname.tex. You should be able to^^J% doctest this file with "sage -t \jobname_doctest.sage".^^J% ^^J% It is always safe to delete this file; it is not used in typesetting your^^J% document.^^J}% \AtEndDocument{\immediate\write\ST@df{"""}}% \gdef\ST@diddfsetup{x}}% {\relax}} \newcommand{\ST@wdf}[1]{\immediate\write\ST@df{#1}} \DeclareOption{final}{% \newcommand{\ST@final}{x}% \IfFileExists{\jobname.sagetex.sout}{}{\AtEndDocument{\PackageWarningNoLine{sagetex}% {`final' option provided, but \jobname.sagetex.sout^^Jdoesn't exist! No Sage input will appear in your document. Remove the `final'^^Joption and rerun LaTeX on your document}}}} \DeclareOption{imagemagick}{% \newcommand{\ST@useimagemagick}{x}% \AtBeginDocument{% \@ifundefined{ST@final}{% \ST@wsf{_st_.useimagemagick = True}}{}}} \DeclareOption{epstopdf}{% \AtBeginDocument{% \@ifundefined{ST@final}{% \ST@wsf{_st_.useepstopdf = True}}{}}} \newcommand{\ST@versioncheck}{True} \DeclareOption{noversioncheck}{% \renewcommand{\ST@versioncheck}{False}} \ProcessOptions\relax \InputIfFileExists{\jobname.sagetex.sout}{} {\typeout{No file \jobname.sagetex.sout.}} \AtBeginDocument{\provideenvironment{NoHyper}{}{}} \newcommand{\ST@sage}[1]{\ST@wsf{% try:^^J _st_.inline(\theST@inline, #1)^^J% except:^^J _st_.goboom(\the\inputlineno)}% \ifST@paused \mbox{(Sage\TeX{} is paused)}% \else \begin{NoHyper}\ref{@sageinline\theST@inline}\end{NoHyper}% \@ifundefined{r@@sageinline\theST@inline}{\gdef\ST@rerun{x}}{}% \fi \stepcounter{ST@inline}} \newcommand{\sage}[1]{\ST@sage{latex(#1)}} \newcommand{\sagestr}[1]{\ST@sage{#1}} \catcode`\%=12 \newcommand{\percent}{%} \catcode`\%=14 \newcommand{\ST@plotdir}{sage-plots-for-\jobname.tex} \newcommand{\ST@missingfilebox}{\framebox[2cm]{\rule[-1cm]{0cm}{2cm}\textbf{??}}} \newcommand{\sageplot}[1][]{% \@ifnextchar[{\ST@sageplot[#1]}{\ST@sageplot[#1][notprovided]}} \def\ST@sageplot[#1][#2]#3{\ST@wsf{try:^^J _st_.plot(\theST@plot, format='#2', _p_=#3)^^Jexcept:^^J _st_.goboom(\the\inputlineno)}% \ifthenelse{\boolean{pdf} \or \boolean{xetex}}{ \ifthenelse{\equal{#2}{notprovided}}% {\ST@inclgrfx{#1}{pdf}}% {\ST@inclgrfx{#1}{#2}}} { \ifthenelse{\equal{#2}{notprovided}}% {\ST@inclgrfx{#1}{eps}}% {\@ifundefined{ST@useimagemagick}% {\IfFileExists{\ST@plotdir/plot-\theST@plot.#2}% {\ST@missingfilebox% \PackageWarning{sagetex}{Graphics file \ST@plotdir/plot-\theST@plot.#2\space on page \thepage\space cannot be used with DVI output. Use pdflatex or create an EPS file. Plot command is}}% {\ST@missingfilebox% \PackageWarning{sagetex}{Graphics file \ST@plotdir/plot-\theST@plot.#2\space on page \thepage\space does not exist. Plot command is}% \gdef\ST@rerun{x}}}% {\ST@inclgrfx{#1}{eps}}}} \stepcounter{ST@plot}} \newcommand{\ST@inclgrfx}[2]{\ifST@paused \fbox{\rule[-1cm]{0cm}{2cm}Sage\TeX{} is paused; no graphic} \else \IfFileExists{\ST@plotdir/plot-\theST@plot.#2}% {\includegraphics[#1]{\ST@plotdir/plot-\theST@plot.#2}}% {\IfFileExists{\ST@plotdir/plot-\th...@plot.png}% {\ifpdf \ST@inclgrfx{#1}{png} \else \PackageWarning{sagetex}{Graphics file \ST@plotdir/plot-\th...@plot.png on page \thepage\space not supported; try using pdflatex. Plot command is}% \fi}% {\ST@missingfilebox% \PackageWarning{sagetex}{Graphics file \ST@plotdir/plot-\theST@plot.#2\space on page \thepage\space does not exist. Plot command is}% \gdef\ST@rerun{x}}} \fi} \newcommand{\ST@beginsfbl}{% \@bsphack\ST@wsf{% _st_.blockbegin()^^Jtry:}% \let\do\@makeother\dospecials\catcode`\^^M\active} \newcommand{\ST@endsfbl}{% \ST@wsf{except:^^J _st_.goboom(\the\inputlineno)^^J_st_.blockend()}} \newenvironment{sageblock}{\ST@beginsfbl% \def\verbatim@processline{\ST@wsf{ \the\verbatim@line}% \hspace{\sagetexindent}\the\verbatim@line\par}% \verbatim}% {\ST@endsfbl\endverbatim} \newenvironment{sagesilent}{\ST@beginsfbl% \def\verbatim@processline{\ST@wsf{ \the\verbatim@line}}% \verbatim@start}% {\ST@endsfbl\@esphack} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Begin modifications made by Nathan Carter in May 2013 \newcommand\sagetextprocessor[1]{\ST@wsf{try:^^J _st_._processor = #1^^J% except:^^J _st_.goboom(\the\inputlineno)}} \newenvironment{sagetext}{\ST@beginsfbl% \ST@wsf{ _st_._processor(r"""}% \def\verbatim@processline{\ST@wsf{\the\verbatim@line}}% \verbatim@start}% {\ST@wsf{""".strip())}\ST@endsfbl\@esphack} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% End modifications \newenvironment{sageverbatim}{% \def\verbatim@processline{\hspace{\sagetexindent}\the\verbatim@line\par}% \verbatim}% {\endverbatim} \newcommand{\sageexampleincludetextoutput}{False} \newenvironment{sageexample}{% \ST@wsf{% try:^^J _st_.doctest(\theST@inline, r"""}% \ST@dodfsetup% \ST@wdf{Sage example, line \the\inputlineno::^^J}% \begingroup% \@bsphack% \let\do\@makeother\dospecials% \catcode`\^^M\active% \def\verbatim@processline{% \ST@wsf{\the\verbatim@line}% \ST@wdf{\the\verbatim@line}% }% \verbatim@start% } { \@esphack% \endgroup% \ST@wsf{% """, globals(), locals(), \sageexampleincludetextoutput)^^Jexcept:^^J _st_.goboom(\the\inputlineno)}% \ifST@paused% \mbox{(Sage\TeX{} is paused)}% \else% \begin{NoHyper}\ref{@sageinline\theST@inline}\end{NoHyper}% \@ifundefined{r@@sageinline\theST@inline}{\gdef\ST@rerun{x}}{}% \fi% \ST@wdf{}% \stepcounter{ST@inline}} \newcommand{\sagecommandlinetextoutput}{True} \newlength{\sagecommandlineskip} \setlength{\sagecommandlineskip}{8pt} \newenvironment{sagecommandline}{% \ST@wsf{% try:^^J _st_.commandline(\theST@cmdline, r"""}% \ST@dodfsetup% \ST@wdf{Sage commandline, line \the\inputlineno::^^J}% \begingroup% \@bsphack% \let\do\@makeother\dospecials% \catcode`\^^M\active% \def\verbatim@processline{% \ST@wsf{\the\verbatim@line}% \ST@wdf{\the\verbatim@line}% }% \verbatim@start% } { \@esphack% \endgroup% \ST@wsf{% """, globals(), locals(), \sagecommandlinetextoutput)^^Jexcept:^^J _st_.goboom(\the\inputlineno)}% \ifST@paused% \mbox{(Sage\TeX{} is paused)}% \else% \begin{NoHyper}\ref{@sagecmdline\theST@cmdline}\end{NoHyper}% \@ifundefined{r@@sagecmdline\theST@cmdline}{\gdef\ST@rerun{x}}{}% \fi% \ST@wdf{}% \stepcounter{ST@cmdline}} \newcommand{\sagetexpause}{\ifST@paused\relax\else \ST@wsf{print 'SageTeX paused on \jobname.tex line \the\inputlineno'^^J"""} \ST@pausedtrue \fi} \newcommand{\sagetexunpause}{\ifST@paused \ST@wsf{"""^^Jprint 'SageTeX unpaused on \jobname.tex line \the\inputlineno'} \ST@pausedfalse \fi} \AtEndDocument{\ifST@paused \ST@wsf{"""^^Jprint 'SageTeX unpaused at end of \jobname.tex'} \fi \ST@wsf{_st_.endofdoc()}% \@ifundefined{ST@rerun}{}% {\typeout{*********************************************************************} \PackageWarningNoLine{sagetex}{there were undefined Sage formulas and/or plots.^^JRun Sage on \jobname.sagetex.sage, and then run LaTeX on \jobname.tex again}} \typeout{*********************************************************************}} \endinput %% %% End of file `sagetex.sty'.