diff options
author | Karl Berry <karl@freefriends.org> | 2019-03-16 22:43:33 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2019-03-16 22:43:33 +0000 |
commit | 7e80fa208736f4ab99b1d57dd1571da41f1e3744 (patch) | |
tree | 606bd82a5737d587ef239fd58a5a58c256666178 /Build/source/texk/texlive/linked_scripts | |
parent | 9de7d0ea371387f647487812b48b51030c739bdf (diff) |
webquiz
git-svn-id: svn://tug.org/texlive/trunk@50419 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/texlive/linked_scripts')
4 files changed, 1098 insertions, 0 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.am b/Build/source/texk/texlive/linked_scripts/Makefile.am index 163aa0d90d4..0577a8b5753 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.am +++ b/Build/source/texk/texlive/linked_scripts/Makefile.am @@ -215,6 +215,7 @@ texmf_other_scripts = \ ulqda/ulqda.pl \ urlbst/urlbst \ vpe/vpe.pl \ + webquiz/webquiz.py \ xindex/xindex.lua \ yplan/yplan texmf_context_scripts = \ diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.in b/Build/source/texk/texlive/linked_scripts/Makefile.in index cc1219d9930..6e8781db9a8 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.in +++ b/Build/source/texk/texlive/linked_scripts/Makefile.in @@ -429,6 +429,7 @@ texmf_other_scripts = \ ulqda/ulqda.pl \ urlbst/urlbst \ vpe/vpe.pl \ + webquiz/webquiz.py \ xindex/xindex.lua \ yplan/yplan diff --git a/Build/source/texk/texlive/linked_scripts/scripts.lst b/Build/source/texk/texlive/linked_scripts/scripts.lst index 36350248993..ab738ceb07b 100644 --- a/Build/source/texk/texlive/linked_scripts/scripts.lst +++ b/Build/source/texk/texlive/linked_scripts/scripts.lst @@ -159,6 +159,7 @@ tlshell/tlshell.tcl ulqda/ulqda.pl urlbst/urlbst vpe/vpe.pl +webquiz/webquiz.py xindex/xindex.lua yplan/yplan context/stubs/unix/context diff --git a/Build/source/texk/texlive/linked_scripts/webquiz/webquiz.py b/Build/source/texk/texlive/linked_scripts/webquiz/webquiz.py new file mode 100755 index 00000000000..283f21d1321 --- /dev/null +++ b/Build/source/texk/texlive/linked_scripts/webquiz/webquiz.py @@ -0,0 +1,1095 @@ +#!/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) |