Metadata-Version: 2.1
Name: texsurgery
Version: 0.5.0
Summary: Replace some commands and environments within a TeX document by evaluating code inside a jupyter kernel
Home-page: https://framagit.org/pang/texsurgery
Author: Pablo Angulo, Juan Viu Sos, Miguel Angel Marco Buzunariz
Author-email: pablo.angulo@upm.es
License: UNKNOWN
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
Requires-Dist: jupyter-client

# TexSurgery

Replaces some commands and environments within a TeX document by evaluating code inside a jupyter kernel.

Much like [sagetex](https://github.com/sagemath/sagetex), but with the following differences:

 1. `sagetex` collects all the code using LaTeX and only then runs `sage` to get the LaTeX output, which definitely works, but this conflicts with some interesting LaTeX packages and is slower than a direct conversion.
 2. `TexSurgery` works in any language with a jupyter kernel. In particular, you don't need to install `sagemath`.

## Installation

    python3 -m pip install texsurgery

### Installation from source

    git clone https://framagit.org/pang/texsurgery.git
    cd texsurgery
    python3 -m pip install -U .

## Command line usage

You can call it from the command line as follows:

```
texsurgery input_file.tex -o output_file.tex
```

will perform the code substitutions in `input_file.tex` and write the result in `output_file.tex`

The possible parameters are:

- `-h, --help` shows the help message and exits,
- `input_file` the file to read from. If none given, the standard input will be used.
- `--output_file output_file, -o output_file` writes the result in output file. If it is not provided, the result is directed to the standard output
- `-tex` just perform the code substitution in the latex content (this is the default)
- `-pdf` create a pdf file from the resulting modified tex. If the `-o` option is not provided, texsurgery will try to guess the name of the resulting file. This option deppends on `pdflatex`being installed in the system.
- `--pdflatex-options` options to be passed to pdflatex (it requires the `-pdf` option).

Since it can use standard input/output, it is pipe friendly. So, for example:

```
texsurgery input_file.tex | pandoc --from latex -o output_file.html
```
will perform the code substitutions and convert the result to a html (provided you have `pandoc`installed).

Likewise, if you have a markdown file `input.md` with the lines

```
\usepackage[python3]{texsurgery}

The result of multiplying 3 and 5 is \eval{3*5}
```

And run

```
pandoc input.md --from markdown --to latex -s | texsurgery
```

you will get the corresponding latex file whith the `\usepackage` line removed, and the `\eval{3*5}` text substituted by the corresponding result `15`.

In fact, since texsurgery does not require fully correct `.tex` code, you can directly run texsurgery in the markdown file!

## Testing

If you installed from source, the following command will perform some common tests, and specific tests for some of the kernels that are installed:

    python3 -m unittest tests

## Selectors

### `find` and `findall`

`texsurgery` can also gather information using css-style selectors:

.. doctest::

  >>> from texsurgery.texsurgery import TexSurgery
  >>> tex = open('../tests/test_find.tex').read()
  >>> # An environment, which can be question or questionmultx,
  >>> # which contains an environment runsilent, and captures its content
  >>> TexSurgery(tex).findall('question,questionmultx runsilent')
  [('questionmultx', [('runsilent', '\na = randint(1,10)\n')]), ('questionmultx', [('runsilent', '\na = randint(2,10)\n')]), ('question', [('runsilent', '\na = randint(2,10)\nf = sin(a*x)\nfd = f.derivative(x)\n')])]
  >>> # An environment, which can be question or questionmultx,
  >>> # which contains an environment choices
  >>> # which contains a command \correctchoice, and captures its argument
  >>> TexSurgery(tex).findall('question,questionmultx choices \correctchoice{choice}')
  [('question', [('choices', [('\\correctchoice', {'choice': '$\\sage{fd}$'})])])]
  >>> # An environment questionmultx which contains a command
  >>> # \AMCnumericChoices with two arbitrary arguments
  >>> TexSurgery(tex).findall('questionmultx \AMCnumericChoices[_nargs=2]')
  [('questionmultx', [('\\AMCnumericChoices', {'arg0': '\\eval{8+a}', 'arg1': 'digits=2,sign=false,scoreexact=3'})]), ('questionmultx', [('\\AMCnumericChoices', {'arg0': '\\eval{8*a}', 'arg1': 'digits=2,sign=false,scoreexact=3'})])]
  >>> # Environment questionmultx, with first argument exactly equal to basic-multiplication
  >>> TexSurgery(tex).find(r'questionmultx{@basic-multiplication}')
  ('questionmultx', {'@basic-multiplication': 'basic-multiplication'}, '\n\\begin{runsilent}\na = randint(2,10)\n\\end{runsilent}\nWhat is $8*\\eval{a}$?\n\\AMCnumericChoices{\\eval{8*a}}{digits=2,sign=false,scoreexact=3}\n')
  >>> # Command \copygroup, with any first argument, and second argument
  >>> # exactly equal to BigGroupe
  >>> TexSurgery(tex).findall(r'\copygroup{category}{@BigGroupe}')
  [('\\copygroup', {'category': 'cat1', '@BigGroupe': 'BigGroupe'}), ('\\copygroup', {'category': 'cat2', '@BigGroupe': 'BigGroupe'})]
   >>> # Command \subsection, with any title as first argument,
   >>> # with \label{seed}
   >>> TexSurgery(tex).find('\\subsection[label="student id"]{title}')
   ('\\subsection', {'title': 'Student identification'})
  >>> # Command \subsection, with any title as first argument,
  >>> # with \label{seed} and in this subsection (which is ended by a \section)
  >>> # there is a run environment
  >>> TexSurgery(tex).find('\\subsection{title}#seed:next run')
  ('\\subsection', ('run', "\nprint('The random seed is ', seed)\n"))

### `insertAfter` and `replace`

`texsurgery` can perform search-and-replace, and search-and-insert-after using the css-style selectors:

.. doctest::

  >>> from texsurgery.texsurgery import TexSurgery
  >>> tex = r'''\begin{choices}
  ... \wrongchoice{$\sage{fd + a}$}
  ... \correctchoice{$\sage{fd}$}
  ... \wrongchoice{$\sage{fd*a}$}
  ... \end{choices}
  ... '''
  >>> ts = TexSurgery(tex)
  >>> ts = ts.replace(r'\correctchoice{choice}', r'\correctchoice{$\sage{f.derivative(x)}$}')
  >>> ts.src
  '\\begin{choices}\n\\wrongchoice{$\\sage{fd + a}$}\n\\correctchoice{$\\sage{f.derivative(x)}$}\n\\wrongchoice{$\\sage{fd*a}$}\n\\end{choices}\n'

### `shuffle`

`texsurgery` can also shuffle some TexElements nested within a parent using the css-style selectors:

.. doctest::

  >>> from texsurgery.texsurgery import TexSurgery
  >>> tex = r'''\begin{choices}
  ... \wrongchoice{$\sage{fd + a}$}
  ... \correctchoice{$\sage{fd}$}
  ... \wrongchoice{$\sage{fd*a}$}
  ... \end{choices}
  ... '''
  >>> ts = TexSurgery(tex)
  >>> ts = ts.shuffle('choices', r'\correctchoice{choice},\wrongchoice{choice}', randomseed=1)
  >>> ts.src
  '\\begin{choices}\n\\correctchoice{$\\sage{fd}$}\n\\wrongchoice{$\\sage{fd*a}$}\n\\wrongchoice{$\\sage{fd + a}$}\n\\end{choices}\n'


## Kernels

TexSurgery can use several jupyter kernels in the same document. In order to do so, you have to declare them with a line like

~~~~~~~~~~latex
\usepackage[sagemath,python3]{texsurgery}
~~~~~~~~~~

The first one is the default. To use another one, pass the option to the corresponding environment. For example
~~~~~~~~~~latex
\begin{run}
1^1
\end{run}
~~~~~~~~~~
will be transformed into
~~~~~~~~~~latex
1
~~~~~~~~~~
but, because of the different way that `sagemath` and `python` handle the `^` operator, this
~~~~~~~~~~latex
\begin{run}[python3]
1^1
\end{run}
~~~~~~~~~~
will be transformed into
~~~~~~~~~~latex
0
~~~~~~~~~~
instead.

This can be useful for example to include graphics generated by different systems in the same document.

## Example

Start with this LaTeX code:

~~~~~~~~~~latex
% Any jupyter kernel is available
\usepackage[sagemath]{texsurgery}

% Compatible with any other LaTeX package
\usepackage[bloc,completemulti]{automultiplechoice}

% Example of user macros
\providecommand{\abs}[1]{\lvert#1\rvert}
\newcommand{\R}{\mathbb{R}}

% TexSurgery can replace some \commands before pdflatex runs
\begin{minipage}{.85\linewidth}
Student: {\bf \name \;  \surname},  \quad ID:  {\bf \id}
\end{minipage}

\begin{question}{derivative-sin}
\qvariant{1} \qtags{derivative}
% TexSurgery will run code in a jupyter kernel
\begin{runsilent}
set_random_seed(\seed)
a = randint(2,10)
f = sin(a*x)
fd = f.derivative(x)
\end{runsilent}
% TexSurgery will eval code in a jupyter kernel
% and replace \eval{expr} with the output from the kernel
% \sage{expr} is just an alias for \eval{latex(expr)}
What is the first derivative of $\sage{f}$?
\begin{choices}
  \correctchoice{$\sage{fd}$}
  \wrongchoice{$\sage{fd*a}$}
  \wrongchoice{$\sage{fd + a}$}
\end{choices}
\begin{explain}
\begin{run}
# TexSurgery will run code in the jupyter kernel
# and replace this environment with the full output
\end{run}
\end{explain}
\end{question}
~~~~~~~~~~

and run this `python` code:
~~~~~~~~~~python
from texsurgery.texsurgery import TexSurgery
student_vars = dict(name='Fulano', surname='de Tal', seed='1', id='314159')
ts = TexSurgery(tex_source).data_surgery(student_vars).code_surgery()
~~~~~~~~~~

in order to transform it into this:

~~~~~~~~~~latex
% Compatible with any other LaTeX package
\usepackage[bloc,completemulti]{automultiplechoice}

% Example of user macros
\providecommand{\abs}[1]{\lvert#1\rvert}
\newcommand{\R}{\mathbb{R}}

\begin{minipage}{.85\linewidth}
Student: {\bf Fulano \;  de Tal},  \quad ID:  {\bf 314159}
\end{minipage}

\begin{question}{derivative-sin}
\qvariant{1} \qtags{derivative}
What is the first derivative of $\sin\left(7 \, x\right)$?
\begin{choices}
  \correctchoice{$7 \, \cos\left(7 \, x\right)$}
  \wrongchoice{$49 \, \cos\left(7 \, x\right)$}
  \wrongchoice{$7 \, \cos\left(7 \, x\right) + 7$}
\end{choices}
\begin{explain}
\begin{run}
# TexSurgery will run code in the jupyter kernel
# and replace this environment with the full output
\end{run}
\end{explain}
\end{question}
~~~~~~~~~~

## Thanks

To all our colleagues that gave feedback to the early versions, specially Fabricio from ETSIN.UPM and Carlos from ETSIAAB.UPM


