summaryrefslogtreecommitdiff
path: root/Master
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2019-03-17 21:54:02 +0000
committerKarl Berry <karl@freefriends.org>2019-03-17 21:54:02 +0000
commita79da32935834f42057e258d22bb90e8ed7e65aa (patch)
tree5a572359444a579b30b73e8a3844b01afe47ff22 /Master
parent5698059f8aa4109c8f98d166ef398f04c515b022 (diff)
webquiz deref/remove symlinks
git-svn-id: svn://tug.org/texlive/trunk@50431 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
l---------Master/texmf-dist/doc/latex/webquiz/www/doc/examples1
-rw-r--r--[l---------]Master/texmf-dist/doc/latex/webquiz/www/doc/webquiz-online-manual.tex625
-rwxr-xr-x[l---------]Master/texmf-dist/scripts/webquiz/webquiz1096
-rwxr-xr-xMaster/tlpkg/bin/ctan2tl3
-rwxr-xr-xMaster/tlpkg/bin/deref-symlinks22
-rwxr-xr-xMaster/tlpkg/bin/tl-update-messages8
-rwxr-xr-xMaster/tlpkg/libexec/ctan2tds6
7 files changed, 1757 insertions, 4 deletions
diff --git a/Master/texmf-dist/doc/latex/webquiz/www/doc/examples b/Master/texmf-dist/doc/latex/webquiz/www/doc/examples
deleted file mode 120000
index d15735c1dfa..00000000000
--- a/Master/texmf-dist/doc/latex/webquiz/www/doc/examples
+++ /dev/null
@@ -1 +0,0 @@
-../../examples \ No newline at end of file
diff --git a/Master/texmf-dist/doc/latex/webquiz/www/doc/webquiz-online-manual.tex b/Master/texmf-dist/doc/latex/webquiz/www/doc/webquiz-online-manual.tex
index dcdc896493e..ab86e3a90c6 120000..100644
--- a/Master/texmf-dist/doc/latex/webquiz/www/doc/webquiz-online-manual.tex
+++ b/Master/texmf-dist/doc/latex/webquiz/www/doc/webquiz-online-manual.tex
@@ -1 +1,624 @@
-../../webquiz-online-manual.tex \ No newline at end of file
+%-----------------------------------------------------------------------------
+% Copyright (C) 2004-2019 Andrew Mathas, University of Sydney
+%
+% Distributed under the terms of the GNU General Public License (GPL)
+% http://www.gnu.org/licenses/
+%
+% This file is part of the WebQuiz system.
+%
+% <Andrew.Mathas@sydney.edu.au>
+%-----------------------------------------------------------------------------
+
+%% This file is part of the WebQuiz class distribution
+\PassOptionsToClass{svgnames}{xcolor}
+\documentclass[svgnames]{webquiz}
+
+% load webquiz-doc code -- the class file loads webquiz-ini.code.tex is first
+\input{webquiz-doc.code}
+
+% using \textcolor or \color in \WebQuiz conflicts with the code to make
+% listings respect color in webquiz.cfg
+\newcommand\WebQuiz{WebQuiz\xspace}
+
+\usepackage{pst-plot}
+\usepackage{hyperref}
+
+\usepackage{ifpdf}
+\ifpdf
+ \PackageError{WebQuiz}{This file must be compiled using latex not pdflatex}
+\fi
+\newcommand{\C}{\mathbb C}
+\newcommand*{\vect}[1]{\mathbf{#1}}
+
+\hypersetup{pdftitle={WebQuiz online manual}}
+
+\title{WebQuiz: Web Quizzes using LaTeX}
+\begin{document}
+
+\begin{discussion}[Introduction]
+ \WebQuiz is a \LaTeX{} package for creating \textit{interactive}
+ web quizzes. The idea is that you write the quiz using \LaTeX{} and that
+ \WebQuiz creates the web page from this file. Anything that you can
+ write using \LaTeX{} will be converted to \HTML by \WebQuiz. This
+ includes, for example, mathematics and graphics written using
+ \ctan{pstricks}. \WebQuiz supports three different types of quiz
+ questions:
+
+ \begin{enumerate}
+ \item Multiple choice questions with a \emph{unique} correct answer.
+ (See \qref{question1})
+ \item Multiple choice questions with \emph{several} (or no)
+ correct answers.
+ (See \qref{question2})
+ \item Questions that require the student to enter an answer, which
+ can then be compared with the correct answer in several different
+ ways. (See \qref{question3})
+ \end{enumerate}
+The use of \WebQuiz is described in the next section. Later
+sections describe how each of the \WebQuiz environments are used.
+
+The \LatexCode|discussion| environment in \WebQuiz can also be used to
+write Web Pages like this one (The pages you are reading here were
+written using \WebQuiz.)
+\end{discussion}
+\begin{discussion}[Basic Usage]
+Once you have a \WebQuiz file, you can run it through \LaTeX, in
+the usual way, to produce a readable version of your quiz. When you
+are happy with the quiz, use \WebQuiz to create the HTML
+version. Note that the printable version of the quiz does \emph{not}
+look like the web page; rather, it contains all of the information in
+an easily readable layout.
+
+ If, for example, your quiz file is called \emph{quiz1.tex} then you
+ can use the following commands:
+\begin{latexcode}
+ > latex quiz1 % latex a quiz file
+ > pdflatex quiz1 % a PDF versio of the quiz
+ > xdvi quiz1 % view the quiz using xdvi
+ > dvips quiz1 % print the quiz
+ > webquiz quiz1 % converts the quiz to html
+\end{latexcode}
+ Converting the quiz to html can take quite a long time, particularly
+ if a large number of images need to be created.
+
+\end{discussion}
+\begin{discussion}[WebQuiz files]
+
+ The basic structure of a \WebQuiz file is as follows:
+\begin{latexcode}
+ \documentclass{webquiz}
+
+ \title{Quiz 1: Complex numbers}
+
+ \UnitCode{MATH1001}
+ \UnitName{Differential Calculus}
+ \UnitURL{/u/UG/JM/MATH1001/}
+ \QuizzesURL{/u/UG/JM/MATH1001/Quizzes/}
+
+ \begin{document}
+
+ \begin{discussion}[short heading][optional heading]
+ . . . % optional discussion
+ \end{discussion}
+
+ \begin{question} % question 1
+ . . .
+ \end{question}
+
+ \begin{question} % question 2
+ . . .
+ \end{question}
+ .
+ .
+ \end{document}
+\end{latexcode}
+ In the preamble of the \LaTeX{} file you can specify the unit code, the
+ name of the unit of study, the location of the homepage for the
+ unit and the index file for the quizzes for this unit; this is
+ done using the commands
+ \LatexCode|\UnitCode|,
+ \LatexCode|\UnitName|,
+ \LatexCode|\UnitURL| and
+ \LatexCode|\QuizzesURL| respectively. If the
+ command \LatexCode|\QuizzesURL| is omitted then the URL for the quiz
+ index file is set to \LatexCode|\UnitURL/|Quizzes.
+
+ The title of the quiz can be set in the preamble using the
+ \LatexCode|\title| command. Note that the \LatexCode|\title|
+ command \emph{must} appear before the \LatexCode|\begin{document}| command.
+ As in any \LaTeX{} document, the preamble can define macros and load
+ other \LaTeX{} packages the usual way .
+
+ By using the \LatexCode|discussion| environment you can summarise the
+ material for the students or add introductory material for the quiz.
+ For example, \LatexCode|discussion| environments can be used to recall
+ that main concepts being covered by the quiz or to give references to
+ the lecture notes for the unit. The syntax for the
+ \LatexCode|discussion| environment is as follows:
+\begin{latexcode}
+ \begin{discussion}[optional short heading][optional heading]
+ . . .
+ \end{discussion}
+\end{latexcode}
+ Anything you like (text, mathematics, \ldots) can go inside
+ discussion environments. The \emph{optional heading}, which defaults
+ to Discussion'', is used both as
+ the section heading on the web page and as the heading in the
+ side-menu on left hand side of the page. If a \textit{short heading}
+ is also given then it is used in the side-menu. The quiz can contain
+ zero or more discussion items ( and zero or more quiz questions).
+
+ Questions are set inside a \LatexCode|question| environment. The text is
+ followed by the answers.
+
+ \WebQuiz supports three types of questions:
+ \begin{itemize}
+ \item Multiple choice questions with \emph{precisely one} correct
+ answer;
+ \item Multiple choice questions with \emph{zero or more } correct
+ answers;
+ \item Questions that require the students to enter an answer. Five
+ difference comparison methods are available.
+ \end{itemize}
+ With each of these types of questions you can (and should) provide
+ feedback to the students depending on whether their answer is
+ correct or incorrect. Below we describe how to produce these
+ different types of questions.
+\end{discussion}
+\begin{discussion}[Question 1]
+ The syntax for a multiple choice question having \emph{precisely
+ one} correct answer is as follows:
+\begin{latexcode}
+ \begin{question}
+ . . .question text
+ \begin{choice}
+ \(in)correct . . . text for (in)correct option
+ \feedback . . . feedback on response
+
+ \(in)correct . . . text for (in)correct option
+ \feedback . . . feedback on response
+ .
+ .
+ .
+ \end{choice}
+ \end{question}
+\end{latexcode}
+ The different choices in a multiple choice question must be inside a
+ \LatexCode|choice| environment. This environment behaves like a
+ standard \LaTeX{} list environment except that instead of using
+ \LatexCode|\item| for list item you use:
+ \begin{itemize}
+ \item \LatexCode|\correct| for a correct choice
+ \item \LatexCode|\incorrect| for an incorrect choice
+ \item \LatexCode|\feedback| to give feedback to the student when
+ they select this choice
+ \end{itemize}
+ At most one \LatexCode|\feedback| response should be given for each
+ \LatexCode|\correct| and each \LatexCode|\incorrect| response.
+ The \LatexCode|\feedback| commands are optional; however, it is
+ recommended that you use them because targeted feedback to the
+ students based on their responses can be beneficial.
+ \par
+ For example, the code below, when run through \WebQuiz, produces \qref{question1}
+ in the online manual quiz.
+\begin{latexcode}
+ \begin{question}
+ The shaded region in the graph
+ \begin{center}
+ \begin{pspicture}(-3,-1.5)(3,4)
+ \pscircle[linewidth=1pt,linestyle=dashed,%
+ fillcolor=SkyBlue,fillstyle=solid](1,1){2}
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {->}(0,0)(-1.5,-1.5)(3.5,3.5)
+ \rput(3.75,0){$x$}
+ \rput(0,3.85){$iy$}
+ \rput(3,-0.4){3}
+ \rput(-0.4,3){3$i$}
+ \psdots(1,1)
+ \end{pspicture}
+ \end{center}
+ is equal to which of the following sets of complex numbers?
+ \begin{choice}
+ \incorrect $\{z \in \C : (z-1)^{2}+(z-(i+1))^{2}<2\}$
+ \feedback The equation of a circle in the complex plane with
+ centre $a+ib$ and radius $r$ is
+ \begin{displaymath}
+ \{z\in\C : |z-(a+ib)|<r \}.
+ \end{displaymath}
+
+ \incorrect $\{z \in \C : z+(i+1)<2\}$
+ \feedback You want the set of points whose \textit{distance}
+ from $1+i$ is less than $2$.
+
+ \correct $\{z \in \C : |z-(i+1)|<2\}$
+ \feedback The graph shows the set of complex numbers whose
+ distance from $1+i$ is less than $2$.
+
+ \incorrect $\{z \in \C : |z-2|<|i+1-2|\}$
+ \feedback As $|i+1-2|=\sqrt 2$, this is the set of complex
+ numbers whose distance from $2$ is less than
+ $\sqrt 2$.
+
+ \incorrect None of the above.
+ \feedback The graph shows the set of complex numbers whose
+ distance from the centre of the circle is less than $2$.
+ \end{choice}
+ \end{question}
+\end{latexcode}
+\end{discussion}
+\begin{discussion}[Question 2]
+ To allow multiple (or no) correct answer we add \LatexCode|multiple| as an
+ optional argument to the \LatexCode|choice| environment:
+\begin{latexcode}
+ \begin{question}
+ . . .question text. . .
+ \begin{choice}[multiple]
+ \(in)correct . . . text for (in)correct option
+ \feedback . . . feedback on response
+
+ \(in)correct . . . text for (in)correct option
+ \feedback . . . feedback on response
+ .
+ .
+ .
+ \end{choice}
+ \end{question}
+\end{latexcode}
+ The only difference to the previous case is that zero or more
+ \LatexCode|\correct| commands can appear.
+ \par
+ For example, \qref{question2} below was typed into \WebQuiz
+ using the following commands:
+\begin{latexcode}
+ \begin{question}
+ Which of the following numbers are prime?
+ \begin{choice}[multiple]
+ \incorrect 1
+ \feedback By definition, a prime is a number greater than 1
+ whose only factors are 1 and itself.
+
+ \correct 19
+ \feedback The only factors of 19 are 1 and itself.
+
+ \incorrect 6
+ \feedback 2 is a factor of 6
+
+ \correct 23
+ \feedback The only factors of 23 are 1 and itself.
+
+ \correct 191
+ \feedback The only factors of 191 are 1 and itself.
+ \end{choice}
+ \end{question}
+\end{latexcode}
+\end{discussion}
+\begin{discussion}[Question 3]
+
+ By default, the \LatexCode|choice| environments puts the multiple
+ choice options into one column format. Sometimes the options look
+ better when listed in two or more columns, however, this should be
+ used sparingly as multiple columns do not always display well if the
+ quiz is viewed on a mobile device. By using the \LatexCode|columns|
+ key word in a \LatexCode|choice| environment you can specify the
+ number of columns in the HTML version of the quiz.
+
+\begin{latexcode}
+ \begin{question}
+ . . .question text. . .
+ \begin{choice}[multiple, columns=n] . . . n columns
+ \(in)correct . . . text for (in)correct option
+ \feedback . . . feedback on response
+
+ \(in)correct . . . text for (in)correct option
+ \feedback . . . feedback on response
+ .
+ .
+ .
+ \end{choice}
+ \end{question}
+\end{latexcode}
+ If the optional argument \LatexCode|[multiple]| is not present, then the
+ question admits precisely one correct answer.
+ \par
+ For example, \qref{question3} below was typed into \WebQuiz
+ using the following commands:
+\begin{latexcode}
+ \begin{question}
+ What are suitable parametric equations for this plane curve?
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-2.5,-0.5)(5,5.5)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {->}(0,0)(-2.5,-1.5)(5,5)
+ \psellipse[linecolor=SkyBlue,linewidth=2pt](1,2)(3,2)
+ \end{pspicture}
+ \end{center}
+
+ \begin{choice}[columns=1]
+ \incorrect $x=2\cos t + 1$, $y=3\sin t + 2$
+ \feedback This is an ellipse with centre $(1,2)$ and with axes of
+ length $4$ in the $x$-direction and $6$ in the $y$-direction.
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-2.5,-0.5)(5,5.5)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {->}(0,0)(-2.5,-1.5)(5,5)
+ \parametricplot[linecolor=SkyBlue,linewidth=2pt]{0}{360}%
+ {t cos 2 mul 1 add t sin 3 mul 2 add}
+ \end{pspicture}
+ \end{center}
+
+ \correct $x=3\cos t + 1$, $y=2\sin t + 2$
+ \feedback The curve is an ellipse centre (1,2) with axes length 6
+ in the $x$ direction and 4 in the $y$ direction.
+
+ \incorrect $x=3\cos t - 1$, $y=2\sin t - 2$
+ \feedback This is an ellipse with centre $(-1,-2)$ and with axes
+ of length $6$ in the $x$-direction and $4$ in the $y$-direction.
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-5,-4)(1,2)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {<-}(0,0)(-4.5,-5.5)(1,2)
+ \parametricplot[linecolor=SkyBlue,linewidth=2pt]{0}{360}%
+ {t cos 3 mul 1 sub t sin 2 mul 2 sub}
+ \end{pspicture}
+ \end{center}
+
+ \incorrect $x=2\cos t - 1$, $y=3\sin t - 2$
+ \feedback This is an ellipse with centre $(-1,-2)$ and with axes
+ of length $4$ in the $x$-direction and $6$ in the $y$-direction.
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-4,-5)(1,2)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {<-}(0,0)(-4.5,-5.5)(1,2)
+ \parametricplot[linecolor=SkyBlue,linewidth=2pt]{0}{360}%
+ { t cos 2 mul 1 sub t sin 3 mul 2 sub}
+ \end{pspicture}
+ \end{center}
+ \end{choice}
+ \end{question}
+\end{latexcode}
+\end{discussion}
+\begin{discussion}[Question 4]
+ The final type of question that \WebQuiz supports is a question that
+ requires an answer, which can be a number or a string. The answer is
+ typeset using the \LatexCode|\answer| macro. The \LatexCode|\answer|
+ macro takes two arguments: an optional comparison method, which
+ defaults to \LatexCode|string|, and the correct answer for the
+ question:
+ \begin{latexcode}
+ \answer[comparison method]{correct answer}
+ \end{latexcode}
+ Feedback for correct and incorrect answers is given
+ using the macros \LatexCode|\whenRight| and \LatexCode|\whenWrong|,
+ respectively. The structure of questions with \LatexCode|\answer|'s is as follows:
+ \begin{latexcode}
+ \begin{question}
+ . . .question text. . .
+ \answer[*][complex|integer|lowercase|number|string]{actual answer}
+ \whenRight . . . feedback when right (optional)
+ \whenWrong . . . feedback when wrong (optional)
+ \end{question}
+ \end{latexcode}
+ See the \WebQuiz manual for details of the different
+ comparison types. For example, \qref{question4} below was typed into
+ \WebQuiz using the following commands:
+\begin{latexcode}
+ \begin{question}
+ If the vectors $\vect{a}$ (of magnitude 8 units) and $\vect{b}$
+ (of magnitude 3 units) are perpendicular, what is the value
+ of
+ \begin{displaymath}
+ |\vect{a} -2\vect{b}|~?
+ \end{displaymath}
+ (Hint: Draw a diagram!)
+
+ \answer[number]{10}
+ \whenRight The vectors $\vect{a}$, \(-2\vect{b}\), and
+ $\vect{a} - 2\vect{b}$ form the sides of a right-angled
+ triangle, with sides of length $8$ and $6$ and
+ hypotenuse of length $|\vect{a} -2\vect{b}|$. Therefore
+ by Pythagoras' Theorem,
+ \(|\vect{a} -2\vect{b}|=\sqrt{8^2+6^2}=10\).
+
+ \whenWrong Draw a diagram and then use Pythagoras' theorem.
+ \end{question}
+\end{latexcode}
+\end{discussion}
+\begin{discussion}[Index Files]
+ \WebQuiz also provides a mechanism for creating a web page
+ containing an index of all quizzes for a given Unit of Study.
+ This is done with a \WebQuiz file that contains a \LatexCode|quizindex|
+ environment. The syntax for this environment is as follows:
+\begin{latexcode}
+ \begin{quizindex}
+ \quiz[url1]{title for quiz 1}
+ \quiz[url2]{title for quiz 2}
+ . . .
+ \end{quizindex}
+\end{latexcode}
+If no \textit{URL} is given as an optional argument to
+\LatexCode|\quiz| then
+ \WebQuiz sets the url(s) to \BashCode|quiz1.html|, \BashCode|quiz2.html|
+ and so on.
+\end{discussion}
+
+\begin{discussion}[Credits]
+ \WebQuiz was written and developed in the
+ \href{http://www.maths.usyd.edu.au/}{School of Mathematics and
+ Statistics} at the \href{http://www.usyd.edu.au/}{University of
+ Sydney}. The system is built on \LaTeX{} with the conversion from
+ \LaTeX{} to HTML using Eitan Gurari's
+ \href{http://www.cis.ohio-state.edu/~gurari/TeX4ht/mn.html}{TeX4ht},
+ and Michal Hoftich's
+ \href{https://github.com/michal-h21/make4ht}{make4ht}.
+
+ To write quizzes using \WebQuiz it is only necessary to know
+ \LaTeX, however, the \WebQuiz system has three components:
+ \begin{itemize}
+ \item A \LaTeX{} document class file, \LatexCode{webquiz.cls}, and
+ a \TeXfht configuration file, \LatexCode{webquitexz.cfg}, that enable the
+ quiz files to be processed by \LaTeX{} and \TeXfht, respectively.
+ \item A python program, \BashCode{webquiz}, that translates the \XML
+ file that is produced by \TeXfht into \HTML.
+ \item Some \Javascript and \CSS that controls the quiz web page.
+ \end{itemize}
+
+ The \LaTeX{} component of \WebQuiz was written by Andrew Mathas and
+ the python, \CSS and \Javascript code was written by Andrew Mathas (and
+ Don Taylor), based on an initial prototype of Don Taylor's from 2001.
+ Since 2004 the program has been maintained and developed by Andrew
+ Mathas. Although the program has changed substantially since 2004,
+ Don's idea of using \TeXfht, and some of his code, is still in use.
+
+ Thanks are due to Bob Howlett for general help with CSS and, for
+ Version~5, to Michal Hoftich for technical advice.
+\end{discussion}
+
+\begin{question}
+ \label{question1}
+ The shaded region in the graph
+ \begin{center}
+ \begin{pspicture}(-3,-1.5)(3,4)
+ \pscircle[linewidth=1pt,linestyle=dashed,fillcolor=SkyBlue,fillstyle=solid](1,1){2}
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {->}(0,0)(-1.5,-1.5)(3.5,3.5)
+ \rput(3.75,0){$x$}
+ \rput(0,3.85){$iy$}
+ \rput(3,-0.4){3}
+ \rput(-0.4,3){3$i$}
+ \psdots(1,1)
+ \end{pspicture}
+ \end{center}
+ is equal to which of the following sets of complex numbers?
+ \begin{choice}
+ \incorrect $\{z \in \C : (z-1)^{2}+(z-(i+1))^{2}<2\}$
+ \feedback The equation of a circle in the complex plane with
+ centre $a+ib$ and radius $r$ is
+ \begin{displaymath}
+ \{z\in\C : |z-(a+ib)|<r \}.
+ \end{displaymath}
+
+ \incorrect $\{z \in \C : z+(i+1)<2\}$
+ \feedback You want the set of points whose \textit{distance}
+ from $1+i$ is less than $2$.
+
+ \correct $\{z \in \C : |z-(i+1)|<2\}$
+ \feedback The graph shows the set of complex numbers whose
+ distance from $1+i$ is less than $2$.
+
+ \incorrect $\{z \in \C : |z-2|<|i+1-2|\}$
+ \feedback As $|i+1-2|=\sqrt 2$, this is the set of complex
+ numbers whose distance from $2$ is less than
+ $\sqrt 2$.
+
+ \incorrect None of the above.
+ \feedback The graph shows the set of complex numbers whose
+ distance from the centre of the circle is less than $2$.
+ \end{choice}
+\end{question}
+
+\begin{question}
+ \label{question2}
+ Which of the following numbers are prime?
+ \begin{choice}[multiple]
+ \incorrect 1
+ \feedback By definition, a prime is a number greater than 1
+ whose only factors are 1 and itself.
+
+ \correct 19
+ \feedback The only factors of 19 are 1 and itself.
+
+ \incorrect 6
+ \feedback 2 is a factor of 6
+
+ \correct 23
+ \feedback The only factors of 23 are 1 and itself.
+
+ \correct 191
+ \feedback The only factors of 191 are 1 and itself.
+ \end{choice}
+\end{question}
+
+\begin{question}
+ \label{question3}
+ What are suitable parametric equations for this plane curve?
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-2.5,-0.5)(5,5.5)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {->}(0,0)(-2.5,-1.5)(5,5)
+ \psellipse[linecolor=SkyBlue,linewidth=2pt](1,2)(3,2)
+ \end{pspicture}
+ \end{center}
+
+ \begin{choice}[columns=1]
+ \incorrect $x=2\cos t + 1$, $y=3\sin t + 2$
+ \feedback This is an ellipse with centre $(1,2)$ and with axes of
+ length $4$ in the $x$-direction and $6$ in the $y$-direction.
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-2.5,-0.5)(5,5.5)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {->}(0,0)(-2.5,-1.5)(5,5)
+ \parametricplot[linecolor=SkyBlue,linewidth=2pt]{0}{360}%
+ {t cos 2 mul 1 add t sin 3 mul 2 add}
+ \end{pspicture}
+ \end{center}
+
+ \correct $x=3\cos t + 1$, $y=2\sin t + 2$
+ \feedback The curve is an ellipse centre (1,2) with axes length 6
+ in the $x$ direction and 4 in the $y$ direction.
+
+ \incorrect $x=3\cos t - 1$, $y=2\sin t - 2$
+ \feedback This is an ellipse with centre $(-1,-2)$ and with axes
+ of length $6$ in the $x$-direction and $4$ in the $y$-direction.
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-5,-4)(1,2)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {<-}(0,0)(-4.5,-5.5)(1,2)
+ \parametricplot[linecolor=SkyBlue,linewidth=2pt]{0}{360}%
+ {t cos 3 mul 1 sub t sin 2 mul 2 sub}
+ \end{pspicture}
+ \end{center}
+
+ \incorrect $x=2\cos t - 1$, $y=3\sin t - 2$
+ \feedback This is an ellipse with centre $(-1,-2)$ and with axes
+ of length $4$ in the $x$-direction and $6$ in the $y$-direction.
+ \begin{center}
+ \psset{unit=.6cm}
+ \begin{pspicture}(-4,-5)(1,2)
+ \psaxes[linecolor=red,linewidth=1pt,labels=none]%
+ {<-}(0,0)(-4.5,-5.5)(1,2)
+ \parametricplot[linecolor=SkyBlue,linewidth=2pt]{0}{360}%
+ { t cos 2 mul 1 sub t sin 3 mul 2 sub}
+ \end{pspicture}
+ \end{center}
+ \end{choice}
+\end{question}
+
+\begin{question}
+ \label{question4}
+ If the vectors $\vect{a}$ (of magnitude 8 units) and $\vect{b}$
+ (of magnitude 3 units) are perpendicular, what is the value
+ of
+ \(|\vect{a} -2\vect{b}|\)~?
+ (Hint: Draw a diagram!)
+
+ \answer[number]{10} units
+ \whenRight The vectors $\vect{a}$, \(-2\vect{b}\), and
+ $\vect{a} - 2\vect{b}$ form the sides of a right-angled
+ triangle, with sides of length $8$ and $6$ and
+ hypotenuse of length $|\vect{a} -2\vect{b}|$. Therefore
+ by Pythagoras' Theorem,
+ \(|\vect{a} -2\vect{b}|=\sqrt{8^2+6^2}=10\).
+
+ \whenWrong Draw a diagram and then use Pythagoras' theorem.
+\end{question}
+\end{document}
+\endinput
+
+%% End of file `webquiz-manual.tex'.
diff --git a/Master/texmf-dist/scripts/webquiz/webquiz b/Master/texmf-dist/scripts/webquiz/webquiz
index 8d8dccbc1db..283f21d1321 120000..100755
--- a/Master/texmf-dist/scripts/webquiz/webquiz
+++ b/Master/texmf-dist/scripts/webquiz/webquiz
@@ -1 +1,1095 @@
-webquiz.py \ No newline at end of file
+#!/usr/bin/env python3
+r'''
+------------------------------------------------------------------------------
+ webquiz | Online quizzes generated from LaTeX using python and TeX4ht
+ | This module mainly deals with command-line options and
+ | settings and then calls MakeWebQuiz to build the quiz
+------------------------------------------------------------------------------
+ Copyright (C) Andrew Mathas and Donald Taylor, University of Sydney
+
+ Distributed under the terms of the GNU General Public License (GPL)
+ http://www.gnu.org/licenses/
+
+ This file is part of the WebQuiz system.
+
+ <Andrew.Mathas@sydney.edu.au>
+------------------------------------------------------------------------------
+'''
+
+import argparse
+import codecs
+import errno
+import glob
+import os
+import re
+import shutil
+import signal
+import subprocess
+import sys
+
+# imports of webquiz code
+import webquiz_makequiz
+import webquiz_templates
+import webquiz_util
+
+#################################################################################
+# read in basic meta data such as author, version, ... and set debugging=False
+try:
+ metadata = webquiz_util.MetaData(webquiz_util.kpsewhich('webquiz.ini'), debugging=False)
+except subprocess.CalledProcessError:
+ # check to see if we are running from the zip file
+ ini_file = os.path.join(webquiz_util.webquiz_file(''), '..', 'latex', 'webquiz.ini')
+ try:
+ metadata = webquiz_util.MetaData(ini_file, debugging=False)
+ except (FileNotFoundError, subprocess.CalledProcessError):
+ print('webquiz installation error: unable to find webquiz.ini -> {}'.format(ini_file))
+ sys.exit(1)
+
+# ---------------------------------------------------------------------------------------
+def graceful_exit(sig, frame):
+ ''' exit gracefully on SIGINT and SIGTERM'''
+ if metadata:
+ webquiz_util.webquiz_error(False, 'program terminated (signal {}\n {})'.format(sig, frame))
+ else:
+ webquiz_util.webquiz_error(False, 'program terminated (signal {})'.format(sig))
+
+signal.signal(signal.SIGINT, graceful_exit)
+signal.signal(signal.SIGTERM, graceful_exit)
+
+
+#################################################################################
+def preprocess_with_pst2pdf(options, quiz_file):
+ r'''
+ Preprocess the latex file using pst2pdf. As we are preprocessing the file it
+ is not enough to have latex pass us a flag that tells us to use pst2pdf.
+ Instead, we have to extract the class file option from the tex file
+
+ INPUT: quiz_file should be the name of the quiz file, WITHOUT the .tex extension
+ '''
+ options.talk('Preprocessing {} with pst2pdsf'.format(quiz_file))
+ try:
+ # pst2pdf converts pspicture environments to svg images and makes a
+ # new latex file quiz_file+'-pdf' that includes these
+ cmd = 'pst2pdf --svg --imgdir={q_file} {q_file}.tex'.format(q_file=quiz_file)
+ options.run(cmd)
+ except OSError as err:
+ if err.errno == errno.ENOENT:
+ webquiz_util.webquiz_error(options.debugging, 'pst2pdf not found. You need to install pst2pdf to use the pst2pdf option', err)
+ else:
+ webquiz_util.webquiz_error(options.debugging, 'error running pst2pdf on {}'.format(quiz_file), err)
+
+ # match \includegraphics commands
+ fix_svg = re.compile(r'(\\includegraphics\[scale=1\])\{('+quiz_file+r'-fig-[0-9]*)\}')
+ # the svg images are in the quiz_file subdirectory but latex can't
+ # find them so we update the tex file to look in the right place
+ try:
+ with codecs.open(quiz_file + '-pdf.tex', 'r', encoding='utf8') as pst_file:
+ with codecs.open(quiz_file + '-pdf-fixed.tex', 'w', encoding='utf8') as pst_fixed:
+ for line in pst_file:
+ pst_fixed.write(fix_svg.sub(r'\1{%s/\2.svg}' % quiz_file, line))
+ except OSError as err:
+ webquiz_util.webquiz_error(options.debugging,
+ 'there was an problem running pst2pdf for {}'.format(quiz_file),
+ err
+ )
+
+class WebQuizSettings:
+ r'''
+ Class for initialising webquiz. This covers both reading and writing the
+ webquizrc file and copying files into the web directories during
+ initialisation. The settings themselves are stored in the attribute
+ settings, which is a dictionary. The class reads and writes the settings to
+ the webquizrc file and the values of the settings are available as items:
+ >>> wq = WebQuizSettings()
+ >>> wq['webquiz_url']
+ ... /WebQuiz
+ >>> wq['webquiz_url'] = '/new_url'
+ '''
+
+ # default of settings for the webquizrc file - a dictionary of dictionaries
+ # the 'help' field is for printing descriptions of the settings to help the
+ # user - they are also printed in the webquizrc file
+ settings = dict(
+ webquiz_url={
+ 'default': '',
+ 'advanced': False,
+ 'help': 'Relative URL for the webquiz web directory',
+ },
+ webquiz_www={
+ 'default': '',
+ 'advanced': False,
+ 'help': 'Full path to WebQuiz web directory',
+ },
+ language={
+ 'default': 'english',
+ 'advanced': False,
+ 'help': 'Default language used on web pages'
+ },
+ engine = {
+ 'default': 'latex',
+ 'advanced': False,
+ 'help': 'Default TeX engine used to compile web pages',
+ 'values': dict(latex='', lua='--lua', xelatex='--xetex')
+ },
+ theme={
+ 'default': 'default',
+ 'advanced': False,
+ 'help': 'Default colour theme used on web pages'
+ },
+ breadcrumbs={
+ 'default': '',
+ 'advanced': False,
+ 'help': 'Breadcrumbs at the top of quiz page',
+ },
+ department={
+ 'default': '',
+ 'advanced': False,
+ 'help': 'Name of department',
+ },
+ department_url={
+ 'default': '/',
+ 'advanced': False,
+ 'help': 'URL for department',
+ },
+ institution={
+ 'default': '',
+ 'advanced': False,
+ 'help': 'Institution or university',
+ },
+ institution_url={
+ 'default': '/',
+ 'advanced': False,
+ 'help': 'URL for institution or university',
+ },
+ hide_side_menu={
+ 'default': 'false',
+ 'advanced': False,
+ 'help': 'Do not display the side menu at start of quiz',
+ },
+ one_page={
+ 'default': 'false',
+ 'advanced': False,
+ 'help': 'Display questions on one page',
+ },
+ random_order={
+ 'default': 'false',
+ 'advanced': False,
+ 'help': 'Randomly order the quiz questions',
+ },
+ webquiz_layout={
+ 'default': 'webquiz_layout',
+ 'advanced': True,
+ 'help': 'Name of python module that formats the quizzes',
+ },
+ make4ht={
+ 'default': '',
+ 'advanced': True,
+ 'help': 'Build file for make4ht',
+ },
+ mathjax={
+ 'default':
+ 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js',
+ 'advanced':
+ True,
+ 'help':
+ 'URL for mathjax',
+ },
+ version={
+ 'advanced': False,
+ 'help': 'WebQuiz version number for webquizrc settings',
+ })
+
+ # by default we assume we don't need to print a initialisation warning
+ initialise_warning = ''
+
+ # turn debugging on by default because any error message that we hit before
+ # we process the command line options really should not happen
+ debugging = True
+
+ # keep track of whether we have initialised
+ have_initialised = False
+
+ def __init__(self):
+ '''
+ First read the system webquizrc file and then read the
+ to use some system settings and to override others.
+
+ By default, there is no webquiz initialisation file. We first
+ look for webquizrc in the webquiz source directory and then
+ for .webquizrc file in the users home directory.
+ '''
+ self.settings['version']['default'] = metadata.version
+ for key in self.settings:
+ self.settings[key]['value'] = self.settings[key]['default']
+ if not 'editable' in self.settings[key]:
+ self.settings[key]['editable'] = False
+
+ # define user and system rc file and load the ones that exist
+
+ self.system_rcfile = os.path.join(webquiz_util.kpsewhich('-var TEXMFLOCAL'),
+ 'tex',
+ 'latex',
+ 'webquiz',
+ 'webquizrc'
+ )
+ self.read_webquizrc(self.system_rcfile)
+
+ # the user rc file defaults to:
+ # ~/.dotfiles/config/webquizrc if .dotfiles/config exists
+ # ~/.config/webquizrc if .config exists
+ # and otherwise to ~/.webquizrc
+ if os.path.isdir(os.path.join(os.path.expanduser('~'), '.dotfiles', 'config')):
+ self.user_rcfile = os.path.join(os.path.expanduser('~'), '.dotfiles', 'config', 'webquizrc')
+ elif os.path.isdir(os.path.join(os.path.expanduser('~'), '.config')):
+ self.user_rcfile = os.path.join(os.path.expanduser('~'), '.config', 'webquizrc')
+ else:
+ self.user_rcfile = os.path.join(os.path.expanduser('~'), '.webquizrc')
+
+ self.read_webquizrc(self.user_rcfile)
+
+ def webquiz_debug(self, msg):
+ r'''
+ Customised debugging message for the MakeSettings module
+ '''
+ webquiz_util.webquiz_debug(self.debugging, 'main: '+msg)
+
+ def webquiz_error(self, msg, err=None):
+ r'''
+ Customised error messages for the Module
+ '''
+ webquiz_util.webquiz_error(self.debugging, 'settings: '+msg, err)
+
+ def __getitem__(self, key):
+ r'''
+ Return the value of the corresponding setting. That is, it returns
+ self.settings[key]['value']
+ and an error if the key is unknown.
+ '''
+ if key in self.settings:
+ return self.settings[key]['value']
+
+ self.webquiz_error('getitem: unknown setting "{}" in webquizrc.'.format(key))
+
+ def __setitem__(self, key, value):
+ r'''
+ Set the value of the corresponding setting. This is the equivalent of
+ self.settings[key]['value'] = value
+ and an error if the key is unknown.
+ '''
+ if key in self.settings:
+ self.settings[key]['value'] = value
+ else:
+ self.webquiz_error('setitem: unknown setting "{}" in webquizrc'.format(key))
+
+ def read_webquizrc(self, rcfile, must_exist=False):
+ r'''
+ Read the settings from the specified webquizrc file - if it exists, in
+ which case set self.rcfile equal to this directory. If the file does
+ not exist then return without changing the current settings.
+ '''
+ if os.path.isfile(rcfile):
+ try:
+ with codecs.open(rcfile, 'r', encoding='utf8') as webquizrc:
+ for line in webquizrc:
+ if '#' in line: # remove comments
+ line = line[:line.index('#')]
+ if '=' in line:
+ key, value = line.split('=')
+ key = key.strip().lower().replace('-','_')
+ value = value.strip()
+ if key in self.settings:
+ if value != self[key]:
+ self[key] = value
+ elif key != '':
+ self.webquiz_error('unknown setting "{}" in {}'.format(key, rcfile))
+
+ # record the rcfile for later use
+ self.rcfile = rcfile
+
+ except OSError as err:
+ self.webquiz_error('there was a problem reading the rc-file {}'.format(rcfile), err)
+
+ except Exception as err:
+ self.webquiz_error('there was an error reading the webquizrc file,', err)
+
+ elif must_exist:
+ # this is only an error if we have been asked to read this file
+ self.webquiz_error('the rc-file "{}" does not exist'.format(rcfile))
+
+ def keys(self):
+ r'''
+ Return a list of keys for all settings, ordered alphabetically with the
+ advanced options last/
+ '''
+ return sorted(self.settings.keys(), key=lambda k: '{}{}'.format(self.settings[k]['advanced'], k))
+
+ def write_webquizrc(self):
+ r'''
+ Write the settings to the webquizrc file, defaulting to the user
+ rcfile if unable to write to the system rcfile
+ '''
+ if not hasattr(self, 'rcfile'):
+ # when initialising an rcfile will not exist yet
+ self.rcfile = self.system_rcfile
+
+ file_not_written = True
+ while file_not_written:
+ try:
+ dire = os.path.dirname(self.rcfile)
+ if dire != '' and not os.path.isdir(dire):
+ os.makedirs(dire, exist_ok=True)
+ with codecs.open(self.rcfile, 'w', encoding='utf8') as rcfile:
+ for key in self.keys():
+ # Only save settings in the rcfile if they have changed
+ # Note that changed means changed from the last read
+ # rcfile rather than from the default (of course, the
+ # defaults serve as the "initial rcfile")
+ if key == 'version' or self.settings[key]['default']!=self[key]:
+ rcfile.write('# {}\n{:<17} = {}\n'.format(
+ self.settings[key]['help'],
+ key.replace('_','-'),
+ self[key])
+ )
+
+ print('\nWebQuiz settings saved in {}\n'.format( self.rcfile))
+ input('Press RETURN to continue... ')
+ file_not_written = False
+
+ except (OSError, PermissionError) as err:
+ # if writing to the system_rcfile then try to write to user_rcfile
+ alt_rcfile = self.user_rcfile if self.rcfile != self.user_rcfile else self.system_rcfile
+ response = input(
+ webquiz_templates.rc_permission_error.format(
+ error=err,
+ rcfile=self.rcfile,
+ alt_rcfile=alt_rcfile))
+ if response.startswith('2'):
+ self.rcfile = alt_rcfile
+ elif response.startswith('3'):
+ rcfile = input('WebQuiz rc-file: ')
+ print('\nTo access this rc-file you will need to use: webquiz --rcfile {} ...'.format(rcfile))
+ self.rcfile = os.path.expanduser(rcfile)
+ elif not response.startswith('1'):
+ print('exiting...')
+ sys.exit(1)
+
+ def list_settings(self, setting='all'):
+ r'''
+ Print the non-default settings for webquiz from the webquizrc
+ '''
+ if not hasattr(self, 'rcfile'):
+ print(
+ 'Please initialise WebQuiz using the command: webquiz --initialise\n'
+ )
+
+ if setting not in ['all', 'verbose', 'help']:
+ setting = setting.replace('-', '_')
+ if setting in self.settings:
+ print(self.settings[setting]['value'])
+ else:
+ self.webquiz_error('{} is an invalid setting'.format(setting))
+
+ elif setting=='all':
+ dash = '-'*len('WebQuiz rc-file: {}'.format(self.rcfile))
+ print('{dash}\nWebQuiz rc-file: {rcfile}\n{dash}'.format(rcfile=self.rcfile, dash=dash))
+ for key in self.keys():
+ print('{:<17} = {}'.format(key.replace('_', '-'), self[key]))
+ print('{dash}'.format(dash=dash))
+
+ elif setting=='help':
+ for key in self.keys():
+ print('{:<17} {}'.format(key.replace('_', '-'), self.settings[key]['help'].lower()))
+
+ else:
+ print('WebQuiz settings from {}'.format(self.rcfile))
+ for key in self.keys():
+ print('# {}{}\n{:<17} = {:<17} {}'.format(
+ self.settings[key]['help'],
+ ' (advanced)' if self.settings[key]['advanced'] else '',
+ key.replace('_', '-'),
+ self[key],
+ '(default)' if self[key]==self.settings[key]['default'] else ''
+ )
+ )
+
+ def initialise_webquiz(self, need_to_initialise=False, developer=False):
+ r'''
+ Set the root for the WebQuiz web directory and copy the www files into
+ this directory. Once this is done save the settings to webquizrc.
+ This method should only be used when WebQuiz is being set up.
+
+ If `need_to_initialise` is `True` then this is a forced initialisation.
+ '''
+
+ # keep track of whether we have initialised
+ self.have_initialised = True
+
+ if need_to_initialise:
+ self.initialise_warning = webquiz_templates.web_initialise_warning
+ initialise = input(webquiz_templates.initialise_invite)
+ if initialise!='' and initialise.strip().lower()[0]!='y':
+ self['webquiz_url'] = 'http://www.maths.usyd.edu.au/u/mathas/WebQuiz'
+ return
+
+ if self['webquiz_url']=='':
+ self['webquiz_url'] = '/WebQuiz'
+
+ # prompt for directory and copy files - are these reasonable defaults
+ # for each OS?
+ if sys.platform == 'darwin':
+ default_root = '/Library/WebServer/Documents/WebQuiz'
+ platform = 'Mac OSX'
+ elif sys.platform.startswith('win'):
+ default_root = ' c:\inetpub\wwwroot\WebQuiz'
+ platform = 'Windows'
+ else:
+ default_root = '/var/www/html/WebQuiz'
+ platform = sys.platform.capitalize()
+
+ if self['webquiz_www'] != '':
+ webquiz_root = self['webquiz_www']
+ else:
+ webquiz_root = default_root
+
+ print(webquiz_templates.initialise_introduction)
+ input('Press RETURN to continue... ')
+
+ print(webquiz_templates.webroot_request.format(
+ platform=platform,
+ webquiz_dir = webquiz_root)
+ )
+ input('Press RETURN to continue... ')
+
+ files_copied = False
+ while not files_copied:
+ web_dir = input('\nWebQuiz web directory:\n[{}] '.format(webquiz_root))
+ if web_dir == '':
+ web_dir = webquiz_root
+ else:
+ web_dir = os.path.expanduser(web_dir)
+
+ print('Web directory set to {}'.format(web_dir))
+ if web_dir=='SMS':
+ # undocumented: allow links to SMS web pages
+ self['webquiz_www'] = 'SMS'
+ self['webquiz_url'] = 'http://www.maths.usyd.edu.au/u/mathas/WebQuiz'
+
+ else:
+ try:
+ # ...remove the doc directory
+ web_doc = os.path.join(web_dir, 'doc')
+ if os.path.isfile(web_doc) or os.path.islink(web_doc):
+ os.remove(web_doc)
+ elif os.path.isdir(web_doc):
+ shutil.rmtree(web_doc)
+
+ # Need to locate the www directory, which should be a subdirectory
+ # of the webquiz doc directory. First try using texdoc
+ webquiz_doc = ''
+ try:
+ webquiz_pdf = webquiz_util.shell_command('texdoc --list --machine webquiz.pdf').split()[-1]
+ if webquiz_pdf.endswith('webquiz.pdf'):
+ webquiz_doc = os.path.dirname(webquiz_pdf)
+ except subprocess.CalledProcessError:
+ pass
+
+ # if texdoc failed then try using TEXMFMAIN
+ if webquiz_doc=='':
+ try:
+ webquiz_doc = os.path.join(webquiz_util.kpsewhich('-var TEXMFMAIN'), 'doc','latex', 'webquiz')
+ except subprocess.CalledProcessError:
+ pass
+
+ # if we still don't have webquiz_doc then try working backwards from webquiz.cls
+ # unlikely to work if TEXMFMAIN doesn't
+ if not os.path.isdir(webquiz_doc):
+ parent = os.path.dirname
+ try:
+ texdist_dir = parent(parent(parent(parent(parent(webquiz_util.kpsewhich('webquiz.cls'))))))
+ except subprocess.CalledProcessError:
+ print(webquiz_templates.not_installed.format(metadata.repository))
+ sys.exit(1)
+
+ webquiz_doc = os.path.join(texdist_dir, 'doc', 'latex', 'webquiz')
+
+ # get the root directory of the source code for developer
+ # mode and just in case webquiz_www still does not exist
+ webquiz_src = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+
+ webquiz_www = os.path.join(webquiz_doc, 'www')
+ if not os.path.isdir(webquiz_www):
+ webquiz_www = os.path.join(webquiz_src, 'doc', 'www')
+
+ if developer and os.path.isdir(os.path.join(webquiz_src, 'doc')):
+ # this is a development version so add links from the
+ # web directory to the css,doc and js directories
+ print('\nInstalling files for development version')
+ print('Linking web files {} -> {} ...\n'.format(web_dir, webquiz_src))
+ if not os.path.exists(web_dir):
+ os.makedirs(web_dir)
+
+ for (src, target) in [('javascript', 'js'), ('css', 'css'), ('doc', 'doc')]:
+ newlink = os.path.join(web_dir, target)
+ try:
+ os.remove(newlink)
+ except FileNotFoundError:
+ pass
+ try:
+ os.symlink(os.path.join(webquiz_src,src), newlink)
+ except OSError as err:
+ print('There was a problem linking {}: {}'.format(newlink, err))
+
+ else:
+ # loop until we find some files to install or exit
+ while not os.path.isdir(webquiz_www) or webquiz_www=='':
+ print('\nUnable to find the WebQuiz web files')
+ webquiz_www = input('Please enter the location of the WebQuiz www directory\nor press RETURN to exit: ')
+ webquiz_www = os.path.expanduser(webquiz_www)
+ if webquiz_www=='':
+ sys.exit()
+ if not (webquiz_www.endswith('www/') or webquiz_www.endswith('www')):
+ print('\nThe webquiz web directory is called www, so\n {}\ncannot be the right directory.'.format(
+ webquiz_www)
+ )
+ webquiz_www = False
+
+
+ # the www directory exists so we copy it to web_dir
+ print('\nCopying web files to {} ...'.format(web_dir))
+ webquiz_util.copytree(webquiz_www, web_dir)
+
+ self['webquiz_www'] = web_dir
+ files_copied = True
+
+ except PermissionError:
+ print(webquiz_templates.permission_error.format(web_dir))
+
+ except OSError as err:
+ print(webquiz_templates.oserror_copying.format(web_dir=web_dir, err=err))
+
+ if self['webquiz_www']!='SMS':
+ # now prompt for the relative url
+ webquiz_url = input(webquiz_templates.webquiz_url_message.format(self['webquiz_url']))
+ if webquiz_url != '':
+ # removing trailing slashes from webquiz_url
+ while webquiz_url[-1] == '/':
+ webquiz_url = webquiz_url[:len(webquiz_url) - 1]
+
+ if webquiz_url[0] != '/': # force URL to start with /
+ webquiz_url = '/' + webquiz_url
+
+ if not web_dir.endswith(webquiz_url):
+ print(webquiz_templates.webquiz_url_warning)
+ input('Press RETURN to continue... ')
+
+ self['webquiz_url'] = webquiz_url
+
+ # save the settings and exit
+ self.write_webquizrc()
+ print(webquiz_templates.initialise_ending.format(web_dir=self['webquiz_www']))
+
+ def edit_settings(self):
+ r'''
+ Change current default values for the WebQuiz settings
+ '''
+ advanced_not_started = True
+ for key in self.keys():
+ if key not in ['webquiz_www', 'version']:
+ if advanced_not_started and self.settings[key]['advanced']:
+ print(webquiz_templates.advanced_settings)
+ advanced_not_started = False
+
+ skey = '{}'.format(self[key])
+ setting = input('{}{}[{}]: '.format(
+ self.settings[key]['help'],
+ ' ' if len(skey)<40 else '\n',
+ skey
+ )
+ ).strip()
+ if setting != '':
+ if key == 'webquiz_url' and setting[0] != '/':
+ print(" ** prepending '/' to webquiz_url **")
+ setting = '/' + setting
+
+ elif key == 'webquiz_layout':
+ setting = os.path.expanduser(setting)
+ if setting.endswith('.py'):
+ print(" ** removing .py extension from webquiz_layout **")
+ setting = setting[:-3]
+
+ elif key == 'engine' and setting not in self.settings['engine'].values:
+ print('setting not changed: {} is not a valid TeX engine'.format(setting))
+ setting = self['engine']
+
+ elif key in ['hide_side_menu', 'random_order']:
+ setting = setting.lower()
+ if setting not in ['true', 'false']:
+ print('setting not changed: {} must be True or False'.format(key))
+ setting = self[key]
+
+ elif setting=='NONE':
+ setting = ''
+
+ self[key] = setting
+
+ # save the settings, print them and exit
+ self.write_webquizrc()
+ self.list_settings()
+
+ def tex_install(self):
+ r'''
+ Install the tex files into the standard locations in TEXMFMAIN:
+ scripts -> TEXMFMAIN/scripts/webquiz
+ doc -> TEXMFMAIN/doc/latex/webquiz
+ latex -> TEXMFMAIN/tex/latex/webquiz
+ It is assumed that this is run from the zipfile installation. There
+ is little in the way of error checking or debugging.
+
+ Undocumented feature - useful for debugging initialisation routine
+ '''
+ webquiz_top = os.path.abspath(webquiz_util.webquiz_file('..'))
+ texmf = webquiz_util.kpsewhich('-var TEXMFMAIN')
+ for (src, target) in [('scripts', 'scripts'),
+ ('latex', 'tex/latex'),
+ ('doc', 'doc/latex')]:
+ try:
+ webquiz_util.copytree(os.path.join(webquiz_top,src), os.path.join(texmf, target, 'webquiz'))
+
+ except (FileExistsError,FileNotFoundError):
+ continue
+
+ except PermissionError as err:
+ print(webquiz_templates.insufficient_permissions.format(err))
+ sys.exit(1)
+
+ try:
+
+ # add a link to webquiz.py
+ texbin = os.path.dirname(webquiz_util.shell_command('which pdflatex').split()[-1])
+ os.symlink(os.path.join(texmf,'scripts','webquiz','webquiz.py'), os.path.join(texbin, 'webquiz'))
+ subprocess.call('mktexlsr', shell=True)
+
+ except (FileExistsError,FileNotFoundError):
+ pass
+
+ except PermissionError as err:
+ print(webquiz_templates.insufficient_permissions.format(err))
+ sys.exit(1)
+
+ except subprocess.CalledProcessError as err:
+ print('There was a problem running mktexlsr')
+ sys.exit(1)
+
+ def tex_uninstall(self):
+ r'''
+ UnInstall the tex files into TEXMFMAIN. It is assumed that the files
+ are installed in the natural locations in the TEXMFMAIN tree, namely:
+ scripts -> TEXMFMAIN/scripts/webquiz
+ doc -> TEXMFMAIN/doc/latex/webquiz
+ latex -> TEXMFMAIN/tex/latex/webquiz
+ There is little in the way of error checking or debugging.
+
+ Undocumented feature - useful for debugging initialisation routine
+ '''
+ webquiz_top = os.path.abspath(webquiz_util.webquiz_file('..'))
+ texmf = webquiz_util.kpsewhich('-var TEXMFMAIN')
+ for target in ['scripts', 'tex/latex', 'doc/latex']:
+ try:
+ shutil.rmtree(os.path.join(texmf, target, 'webquiz'))
+
+ except (FileExistsError,FileNotFoundError):
+ pass
+
+ except PermissionError as err:
+ print(webquiz_templates.insufficient_permissions.format(err))
+ sys.exit(1)
+
+ try:
+ # remove link from texbin to webquiz.py
+ texbin = os.path.dirname(webquiz_util.shell_command('which pdflatex').split()[-1])
+ os.remove(os.path.join(texbin, 'webquiz'))
+
+ except (FileExistsError,FileNotFoundError):
+ pass
+
+ except PermissionError as err:
+ print(webquiz_templates.insufficient_permissions.format(err))
+ sys.exit(1)
+
+ # remove any rcfiles that exist in obvious places
+ try:
+ if os.path.isfile(self.system_rcfile):
+ os.remove(self.system_rcfile)
+ if os.path.isfile(self.user_rcfile):
+ os.remove(self.user_rcfile)
+ if os.path.isfile(self.rcfile):
+ os.remove(self.rcfile)
+ except PermissionError:
+ print(webquiz_templates.insufficient_permissions.format(err))
+ sys.exit(1)
+
+ # remove link to webquiz.py
+ texbin = os.path.dirname(webquiz_util.shell_command('which pdflatex').split()[-1])
+ webquiz = os.path.join(texbin,'webquiz')
+ try:
+ target = os.readlink(webquiz)
+ if target==os.path.join(texmf,'scripts','webquiz','webquiz.py'):
+ os.remove(webquiz)
+
+ except (FileExistsError,FileNotFoundError):
+ pass
+
+ except OSError as err:
+ print('There was a problem removing the link to webquiz: {}'.format(err))
+
+ def uninstall_webquiz(self):
+ r'''
+ Remove all of the webquiz files from the webserver
+ '''
+
+ if os.path.isdir(self['webquiz_www']):
+ remove = input('Do you really want to remove the WebQuiz from your web server [N/yes]? ')
+ if remove != 'yes':
+ print('WebQuiz unistall aborted!')
+ return
+
+ try:
+ shutil.rmtree(self['webquiz_www'])
+ print('WebQuiz files successfully removed from {}'.format(self['webquiz_www']))
+
+ except PermissionError as err:
+ print(webquiz_templates.insufficient_permissions.format(err))
+ sys.exit(1)
+
+ except OSError as err:
+ self.webquiz_error('There was a problem removing webquiz files from {}'.format(self['webquiz_www']), err)
+
+ # now reset and save the locations of the webquiz files and URL
+ self['webquiz_url'] = ''
+ self['webquiz_www'] = ''
+ self.write_webquizrc()
+
+ else:
+ self.webquiz_error('uninstall: no webwquiz files are installed on your web server??')
+
+ for rfile in ['system', 'user']:
+ rcfile = getattr(self, rfile+'_rcfile')
+ if os.path.isfile(rcfile):
+ rm = input('Remove {} rcfile {}\n[Y/no] '.format(rfile, rcfile))
+ if rm != 'no':
+ try:
+ os.remove(rcfile)
+ except (OSError, PermissionError) as err:
+ self.webquiz_error('There was a problem deleting {}'.format(rcfile), err)
+
+
+# =====================================================
+if __name__ == '__main__':
+ try:
+ settings = WebQuizSettings()
+
+ # parse the command line options
+ parser = argparse.ArgumentParser(description=metadata.description)
+
+ parser.add_argument(
+ 'quiz_file',
+ nargs='*',
+ type=str,
+ default=None,
+ help='latex quiz files')
+
+ parser.add_argument(
+ '-q',
+ '--quiet',
+ action='count',
+ default=0,
+ help='Suppress tex4ht messages (also -qq etc)')
+
+ parser.add_argument(
+ '-d', '--draft',
+ action='store_true',
+ default=False,
+ help='Use make4ht draft mode')
+
+ parser.add_argument(
+ '-s',
+ '--shell-escape',
+ action='store_true',
+ default=False,
+ help='Shell escape for tex4ht/make4ht')
+
+ engine = parser.add_mutually_exclusive_group()
+ engine.add_argument(
+ '--latex',
+ action='store_const',
+ const='latex',
+ default=settings['engine'],
+ dest='engine',
+ help='Use latex to compile document with make4ht (default)')
+ engine.add_argument(
+ '-l',
+ '--lua',
+ action='store_const',
+ const='lua',
+ dest='engine',
+ help='Use lualatex to compile the quiz')
+ engine.add_argument(
+ '-x',
+ '--xelatex',
+ action='store_const',
+ const='xelatex',
+ dest='engine',
+ help='Use xelatex to compile the quiz')
+
+ parser.add_argument(
+ '-r',
+ '--rcfile',
+ action='store',
+ default=None,
+ help='Specify location of the webquiz rc-file ')
+
+ settings_parser = parser.add_mutually_exclusive_group()
+ settings_parser.add_argument(
+ '-i',
+ '--initialise',
+ action='store_true',
+ default=False,
+ help='Install web components of webquiz')
+ settings_parser.add_argument(
+ '-e', '--edit-settings',
+ action='store_true',
+ default=False,
+ help='Edit default settings for webquiz')
+ settings_parser.add_argument(
+ '--settings',
+ action='store',
+ const='all',
+ default='',
+ nargs='?',
+ type=str,
+ help='List default settings for webquiz'
+ )
+ settings_parser.add_argument(
+ '--developer',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+
+ # options suppressed from the help message
+ parser.add_argument(
+ '-m',
+ '--make4ht',
+ action='store',
+ type=str,
+ dest='make4ht_options',
+ default=settings['make4ht'],
+ help=argparse.SUPPRESS
+ )
+
+ parser.add_argument(
+ '--webquiz_layout',
+ action='store',
+ type=str,
+ dest='webquiz_layout',
+ default=settings['webquiz_layout'],
+ help=argparse.SUPPRESS
+ )
+
+ install_parser = parser.add_mutually_exclusive_group()
+ install_parser.add_argument(
+ '--tex-install',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+ install_parser.add_argument(
+ '--tex-uninstall',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+ install_parser.add_argument(
+ '--uninstall',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+
+ parser.add_argument(
+ '--version',
+ action='version',
+ version='%(prog)s version {}'.format(metadata.version),
+ help=argparse.SUPPRESS)
+
+ parser.add_argument(
+ '--debugging',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS)
+
+ parser.add_argument(
+ '--shorthelp',
+ action='store_true',
+ default=False,
+ help=argparse.SUPPRESS
+ )
+
+ # parse the options
+ options = parser.parse_args()
+ options.prog = parser.prog
+
+ # set debugging mode from options
+ settings.debugging = options.debugging
+
+ # read the rcfile and throw an error if we are not adjusting the settings
+ if options.rcfile is not None:
+ rcfile = os.path.expanduser(options.rcfile)
+ settings.read_webquizrc(rcfile)
+
+ if options.uninstall:
+ # uninstall web files and exit
+ settings.uninstall_webquiz()
+ sys.exit()
+ elif options.tex_install:
+ # install files from zip file into tex distribution and then exit
+ settings.tex_install()
+ sys.exit()
+ elif options.tex_uninstall:
+ # install files from zip file into tex distribution and then exit
+ settings.tex_uninstall()
+ sys.exit()
+
+ # initialise and exit
+ if options.initialise or options.developer:
+ settings.initialise_webquiz(developer=options.developer)
+
+ # force initialisation if the url is not set
+ elif settings['webquiz_url'] == '':
+ settings.initialise_webquiz(need_to_initialise=True)
+
+ # list settings and exit
+ if options.settings != '':
+ settings.list_settings(options.settings)
+ sys.exit()
+
+ # edit settings and exit
+ if options.edit_settings:
+ settings.edit_settings()
+ sys.exit()
+
+ # print short help and exit
+ if options.shorthelp:
+ parser.print_usage()
+ sys.exit()
+
+ # if no filename then exit
+ if options.quiz_file==[]:
+ if settings.have_initialised:
+ sys.exit()
+ else:
+ parser.print_help()
+ sys.exit(1)
+
+ # import the local page formatter
+ mod_dir, mod_layout = os.path.split(options.webquiz_layout)
+ if mod_dir != '':
+ sys.path.insert(0, mod_dir)
+ options.write_web_page = __import__(mod_layout).write_web_page
+
+ # run() is a shorthand for executing system commands depending on the quietness
+ # - we need to use shell=True because otherwise pst2pdf gives an error
+ # options.talk() is a shorthand for letting the user know what is happening
+ if options.quiet == 0:
+ options.run = lambda cmd: subprocess.call(cmd, shell=True)
+ options.talk = lambda msg: print(msg)
+ elif options.quiet == 1:
+ options.run = lambda cmd: subprocess.call(cmd, shell=True, stdout=open(os.devnull, 'wb'))
+ options.talk = lambda msg: print(msg)
+ else:
+ options.run = lambda cmd: subprocess.call(cmd, shell=True, stdout=open(os.devnull, 'wb'), stderr=open(os.devnull, 'wb'))
+ options.talk = lambda msg: None
+
+ # run through the list of quizzes and make them
+ for quiz_file in options.quiz_file:
+ if len(options.quiz_file) > 1 and options.quiet < 3:
+ print('Making web page for {}'.format(quiz_file))
+ # quiz_file is assumed to be a tex file if no extension is given
+ if not '.' in quiz_file:
+ quiz_file += '.tex'
+
+ if not os.path.isfile(quiz_file):
+ print('WebQuiz error: cannot read file {}'.format(quiz_file))
+
+ else:
+
+ # the quiz name and the quiz_file will be if pst2pdf is used
+ quiz_name = quiz_file
+ if options.quiet < 2:
+ print('WebQuiz generating web page for {}'.format(quiz_file))
+
+ # If the pst2podf option is used then we need to preprocess
+ # the latex file BEFORE passing it to MakeWebQuiz. Set
+ # options.pst2pdf = True if pst2pdf is given as an option to
+ # the webquiz documentclass
+ with codecs.open(quiz_file, 'r', encoding='utf8') as q_file:
+ doc = q_file.read()
+
+ options.pst2pdf = False
+ try:
+ brac = doc.index(r'\documentclass[') + 15 # start of class options
+ if 'pst2pdf' in [
+ opt.strip()
+ for opt in doc[brac:brac+doc[brac:].index(']')].split(',')
+ ]:
+ preprocess_with_pst2pdf(options, quiz_file[:-4])
+ options.pst2pdf = True
+ # now run webquiz on the modified tex file
+ quiz_file = quiz_file[:-4] + '-pdf-fixed.tex'
+ except ValueError:
+ pass
+
+ # the file exists and is readable so make the quiz
+ webquiz_makequiz.MakeWebQuiz(quiz_name, quiz_file, options, settings, metadata)
+
+ quiz_name = quiz_name[:quiz_name.index('.')] # remove the extension
+
+ # move the css file into the directory for the quiz
+ css_file = os.path.join(quiz_name, quiz_name + '.css')
+ if os.path.isfile(quiz_name + '.css'):
+ if os.path.isfile(css_file):
+ os.remove(css_file)
+ shutil.move(quiz_name + '.css', css_file)
+
+ # now clean up unless debugging
+ if not options.debugging:
+ for ext in ['4ct', '4tc', 'dvi', 'idv', 'lg', 'log',
+ 'ps', 'pdf', 'tmp', 'xml', 'xref'
+ ]:
+ if os.path.isfile(quiz_name + '.' + ext):
+ os.remove(quiz_name + '.' + ext)
+
+ # files created when using pst2pdf
+ if options.pst2pdf:
+ for file in glob.glob(quiz_name + '-pdf.*'):
+ os.remove(file)
+ for file in glob.glob(quiz_name + '-pdf-fixed.*'):
+ os.remove(file)
+ for extention in ['.preamble', '.plog', '-tmp.tex',
+ '-pst.tex', '-fig.tex'
+ ]:
+ if os.path.isfile(quiz_name + extention):
+ os.remove(quiz_name + extention)
+ if os.path.isdir(os.path.join(quiz_name, quiz_name)):
+ shutil.rmtree(os.path.join(quiz_name, quiz_name))
+
+ if settings.initialise_warning != '':
+ print(webquiz_templates.text_initialise_warning)
+
+ except Exception as err:
+
+ # there is a small chance that there is an error before we the
+ # settings.debugging flag has been set
+ webquiz_util.webquiz_error(settings.debugging if 'settings' in globals() else True,
+ 'unknown problem.\n\nIf you think this is a bug please report it by creating an issue at\n {}\n'
+ .format(metadata.repository), err)
diff --git a/Master/tlpkg/bin/ctan2tl b/Master/tlpkg/bin/ctan2tl
index d14e079eec1..3b274800162 100755
--- a/Master/tlpkg/bin/ctan2tl
+++ b/Master/tlpkg/bin/ctan2tl
@@ -158,6 +158,9 @@ fi
# check for file names differing in case only.
find $pkg \( -name .svn \) -prune -o -print | sort -f | uniq -i -d >&2
+# check for symlinks
+find $pkg -type l -ls | grep -v /Master/bin | sort >&2
+
# show list of files (and tee to tmp).
find $pkg \! -type d -printf "%TY%Tm%Td.%TH%TM %p\n" | sort -k2 \
| tee ${TMPDIR-/tmp}/ctan2tl.files
diff --git a/Master/tlpkg/bin/deref-symlinks b/Master/tlpkg/bin/deref-symlinks
new file mode 100755
index 00000000000..3b1dbd6202f
--- /dev/null
+++ b/Master/tlpkg/bin/deref-symlinks
@@ -0,0 +1,22 @@
+#!/bin/sh
+# $Id$
+# Originally written by Karl Berry. Public domain.
+#
+# Replace symlinks to files with the actual files.
+# Symlinks to anything else are not touched.
+
+if test "x$1" = --save-links; then
+ savelinks=true
+else
+ savelinks=false
+fi
+
+for f in "$@"; do
+ test -h "$f" || continue # skip non-symlinks
+ test -f "$f" || continue # skip links to anything but regular files
+
+ cp --dereference "$f" "$f".file # expand link
+ mv "$f" "$f".link # move link out of the way
+ mv -v "$f".file "$f" # replace with regular file
+ $savelinks || rm "$f".link # remove link unless keeping
+done
diff --git a/Master/tlpkg/bin/tl-update-messages b/Master/tlpkg/bin/tl-update-messages
index 6649b8c0cf1..c44779e1c41 100755
--- a/Master/tlpkg/bin/tl-update-messages
+++ b/Master/tlpkg/bin/tl-update-messages
@@ -58,6 +58,12 @@ else
done
fi
-rm -f $tmpfile $tmpa $tmpb messages.prev
+pwd
+ls -l $tmpfile $tmpa $tmpb messages.prev
+rm -v $tmpfile $tmpa $tmpb messages.prev
+
+if test "$failed" != 0; then
+ echo "$0: exiting with bad status $failed." >&2
+fi
exit $failed
diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds
index 4feb6e99844..87534158a23 100755
--- a/Master/tlpkg/libexec/ctan2tds
+++ b/Master/tlpkg/libexec/ctan2tds
@@ -1494,6 +1494,7 @@ chomp ($Build = `cd $Master/../Build/source && pwd`);
'tudscr' => '&POSTtudscr',
'uplatex' => '&POST_preserve_man',
'velthuis' => '&POSTvelthuis',
+ 'webquiz' => '&POST_deref_symlink',
'xecyr' => '&POSTxecyr',
'xetex-pstricks' => '&POSTxetexpstricks',
'xindex' => '&POSTxindex',
@@ -6712,6 +6713,11 @@ sub POST_rmsymlink {
&SYSTEM ("find $DEST -type l | xargs --no-run-if-empty $RM");
}
+sub POST_deref_symlink {
+ print "POST$package - dereferencing symlinks\n";
+ &SYSTEM ("find $DEST -type l | xargs deref-symlinks");
+}
+
# Allow overrides. In particular, CTAN can change some hashes to make
# packages with licenses that TL doesn't allow.