Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidLeoni committed Nov 3, 2020
1 parent 84c5c9b commit 131869e
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 80 deletions.
4 changes: 2 additions & 2 deletions conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def setup(app):
jm.zip_folders('exams/*/solutions',
lambda x: '%s-%s-exam' % (jm.filename, x.split('/')[-2]))
# Build Project
def sub(x):
def remap(x):
if x == 'requirements.txt':
return 'NAME-SURNAME-ID/requirements.txt'
elif x.startswith('project/'):
Expand All @@ -455,7 +455,7 @@ def sub(x):

jm.zip_paths(['project', 'requirements.txt'],
'_static/generated/project-template',
patterns = sub)
remap=remap)


source_suffix = {
Expand Down
2 changes: 1 addition & 1 deletion create-exam-example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ trap '[[ $BASH_COMMAND != echo* ]] && echo $BASH_COMMAND' DEBUG


python3 exam.py init 2000-12-31
python3 exam.py package 2000-12-31
python3 exam.py package --site --server 2000-12-31

echo
echo "------- Simulating some shipped exams..."
Expand Down
108 changes: 57 additions & 51 deletions exam.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,27 +82,31 @@ def init(parser, context,args):

"""
jupman-2000-12-31-FIRSTNAME-LASTNAME-ID
exams
2000-12-31
exercise1.py
exercise2.py
exercise3.py
exercise1.py
exercise2.py
exercise3.py
"""
@subcmd(help='Zips a builded exam, making it ready for deploy on the exam server')
def package(parser,context,args):
ld = arg_date(parser, args)

parser.add_argument('date', help="date in format 'yyyy-mm-dd'" )
parser.add_argument('-t','--site', action='store_true', help="zips the site" )
parser.add_argument('-r','--server', action='store_true', help="zips the server" )

vs = vars(parser.parse_args(args))
ld = jmt.parse_date_str(vs['date'])

zip_site = vs['site']
zip_server = vs['server']

eld_admin = '_private/' + ld
eld_solutions = '_private/%s/solutions' % ld
eld_notebook = '%s/exam-%s.ipynb' % (eld_solutions, ld)
eld_notebook = '%s/exam-%s.ipynb' % (eld_solutions, ld)
target_student = get_target_student(ld)
target_student_pdf = '%s/%s' % (get_target_student(ld), get_exam_text_filename(ld, 'pdf'))
# no pdf as hiding cells is too boring, have still
# to properly review cells filtering https://github.com/DavidLeoni/jupman/issues/4
# target_student_pdf = target_student + '/' + 'exam-' + ld + '.pdf'
target_student_pdf = '%s/%s' % (get_target_student(ld), get_exam_text_filename(ld, 'pdf'))
target_student_zip = "%s/server/%s-%s-exam" % (eld_admin,jm.filename,ld) # without '.zip'
target_server_zip = "%s/%s-%s-server" % (eld_admin, jm.filename,ld) # without '.zip'



if not os.path.exists(jm.build):
fatal("%s WAS NOT BUILT !" % jm.build)
Expand All @@ -127,67 +131,68 @@ def package(parser,context,args):
if os.path.exists(server_jupman):
jmt.delete_tree(server_jupman, "_private/%s/server" % ld)

info("Copying built website ...")
shutil.copytree(jm.build, server_jupman)
if zip_site:
info("Copying built website ...")
shutil.copytree(jm.build, server_jupman)

if not os.path.exists(target_student):
os.makedirs(target_student)


info("Copying exercises to " + str(target_student))
jm.copy_code(eld_solutions, target_student, copy_solutions=False)


info("Building pdf ..")
import nbformat
import nbconvert
from nbconvert import PDFExporter

from nbconvert import PDFExporter
pdf_exporter = PDFExporter()

#Dav dic 2019: couldn't make it work, keeps complaining about missing files
#pdf_exporter.template_file = '_templates/classic.tplx
# as a result we have stupid extra date and worse extra numbering in headers


from nbconvert.preprocessors import ExecutePreprocessor
with open(eld_notebook) as f:
nb = nbformat.read(f, as_version=4)
old_title = jmt._replace_title(nb,
with open('%s/exam-%s.ipynb' % (target_student, ld)) as f:
ex_nb = nbformat.read(f, as_version=4)
old_title = jmt._replace_title(ex_nb,
eld_notebook,
"").strip('#')
nb.cells = [cell for cell in nb.cells \
"").strip('#').strip(jm.ipynb_exercises)

ex_nb.cells = [cell for cell in ex_nb.cells \
if not ('nbsphinx' in cell.metadata \
and cell.metadata['nbsphinx'] == 'hidden')]

(body, resources) = pdf_exporter.from_notebook_node(nb,
resources={'metadata': {'name': old_title}})


(body, resources) = pdf_exporter.from_notebook_node(ex_nb,
resources={'metadata': {'name': old_title}})

if not os.path.exists(target_student):
os.makedirs(target_student)
#ep = ExecutePreprocessor(timeout=600, kernel_name='python3')
#ep.preprocess(nb, {'metadata': {'path': './'}})
with open(target_student_pdf, 'wb') as pdf_f:
#print("resources = %s" % resources)
pdf_f.write(body)


info("Copying exercises to " + str(target_student))
jm.copy_code(eld_solutions, target_student, copy_solutions=False)

with open(target_student_pdf, 'wb') as pdf_f:
pdf_f.write(body)

info("Creating student exercises zip: %s.zip" % target_student_zip)

def mysub(fname):
def remap(fname):
if fname.startswith('_private/'):
return fname[len('_private/YYYY-MM-DD/student-zip/'):]
else:
return '/%s/%s' % (jm.get_exam_student_folder(ld), fname)

deglobbed_common_files, deglobbed_common_files_patterns = jm._common_files_maps(target_student_zip)

jm.zip_paths([target_student] + jm.chapter_files,
jm.zip_paths([target_student] + deglobbed_common_files,
target_student_zip,
mysub)
#shutil.make_archive(target_student_zip, 'zip', target_student_zip)
info("Creating server zip: %s.zip" % target_server_zip )
shutil.make_archive(target_server_zip, 'zip', eld_admin + "/server")
print("")
info("You can now browse the website at: %s" % (os.path.abspath(eld_admin + "/server/" + jm.filename + "/html/index.html")))
print("")
patterns=deglobbed_common_files_patterns,
remap=remap)

if zip_server:
info("Creating server zip: %s.zip" % target_server_zip )
shutil.make_archive(target_server_zip, 'zip', eld_admin + "/server")

if zip_server and zip_site:
print("")
info("You can now browse the website at: %s" % (os.path.abspath(eld_admin + "/server/" + jm.filename + "/html/index.html")))
print("")


@subcmd(help='Set up grading for the provided exam')
def grade(parser,context,args):
Expand Down Expand Up @@ -268,8 +273,9 @@ def publish(parser,context,args):
info("Copying solutions to %s" % dest)
shutil.copytree(source_solutions, dest)

info("Copying exam PDF text")
shutil.copyfile(student_pdf, '%s/%s' % (dest, get_exam_text_filename(ld, 'pdf')))
# better avoiding this, I often fix exams post-publication
#info("Copying exam PDF text")
#shutil.copyfile(student_pdf, '%s/%s' % (dest, get_exam_text_filename(ld, 'pdf')))

info()
info("Exam Python files copied.")
Expand Down
4 changes: 3 additions & 1 deletion index.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@
"\n",
"## News\n",
"\n",
"**Thursday 22 October: A TUTORING SERVICE for data science studentes has been set up, see** [announcement on Moodle](https://didatticaonline.unitn.it/dol/mod/forum/discuss.php?d=182045)\n"
"**Tuesday 3 November 2020: published** [midterm simulation solutions](exams/2020-11-02/solutions/exam-2020-11-02-sol.ipynb)\n",
"\n",
"**Thursday 22 October 2020: A TUTORING SERVICE for data science studentes has been set up, see** [announcement on Moodle](https://didatticaonline.unitn.it/dol/mod/forum/discuss.php?d=182045)\n"
]
},
{
Expand Down
62 changes: 37 additions & 25 deletions jupman_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ def tag_regex(string, must_begin=True, preserve_line=False):
must_begin : if True, provided string must be at the beginning of code / cell
preserve_line : if True, characters following the tag until the end of the line are ignored
@since 3.2
@since 3.3
"""
if len(string) == 0:
raise ValueError("Expect a non-empty string !")
Expand Down Expand Up @@ -875,6 +875,19 @@ def copy_code(self, source_dir, dest_dir, copy_solutions=False):
dest_fn)


def _common_files_maps(self, zip_name):
"""
@since 3.2 Created in order to make exam.py work
"""
deglobbed_common_files = []
deglobbed_common_files_patterns = []
for common_path in self.chapter_files:
cur_deglobbed = glob.glob(common_path, recursive=True)
deglobbed_common_files.extend(cur_deglobbed)
deglobbed_common_files_patterns.extend(
[("^(%s)$" % x, "%s/%s" % (zip_name, x)) for x in cur_deglobbed])
return (deglobbed_common_files, deglobbed_common_files_patterns)

def zip_folder(self, source_folder, renamer=None):
""" Takes source folder and creates a zip with processed files
Expand All @@ -901,19 +914,13 @@ def zip_folder(self, source_folder, renamer=None):

self.copy_code(source_folder, build_folder, copy_solutions=True)

deglobbed_common_files = []
deglobbed_common_files_patterns = []
for common_path in self.chapter_files:
cur_deglobbed = glob.glob(common_path, recursive=True)
deglobbed_common_files.extend(cur_deglobbed)
deglobbed_common_files_patterns.extend(
[("^(%s)$" % x, "%s/%s" % (zip_name, x)) for x in cur_deglobbed])

deglobbed_common_files, deglobbed_common_files_patterns = self._common_files_maps(zip_name)

info("zip_name = %s" % zip_name)
zip_path = os.path.join(self.generated, zip_name)
self.zip_paths( deglobbed_common_files + [build_folder],
zip_path,
patterns= deglobbed_common_files_patterns + [("^(%s)" % build_jupman,"")])
patterns = deglobbed_common_files_patterns + [("^(%s)" % build_jupman,"")])
info("Done zipping %s" % source_folder )

def zip_folders(self, selector, renamer=None):
Expand Down Expand Up @@ -990,16 +997,15 @@ def latex_maketitle(self, html_baseurl):
\noindent \url{%s}''' % html_baseurl)


def zip_paths(self, rel_paths, zip_path, patterns=[]):
def zip_paths(self, rel_paths, zip_path, patterns=[], remap=None):
""" zips provided rel_folder to file zip_path (WITHOUT .zip) !
rel_paths MUST be relative to project root
This function was needed as default python zipping machinery created weird zips
people couldn't open in Windows
patterns can be:
- a list of tuples source regexes to dest
- a function that takes a string and returns a string
- patterns is a list of tuples source regexes to dest
- remap is a function that takes a string and returns a string, and is applied after patterns
"""

Expand All @@ -1021,21 +1027,27 @@ def write_file(fname):
#info('Zipping: %s' % fname)


if isinstance(patterns, (list,)):
if len(patterns) > 0:
if isinstance(patterns, types.FunctionType):
warn("zip_paths: using patterns as a function is deprecated, please use remap attribute instead")
the_remap = patterns
the_patterns = []
else:
the_patterns = patterns
the_remap = remap

if len(the_patterns) == 0 and the_remap == None:
to_name = '/%s' % fname
else:
if len(the_patterns) > 0:
to_name = fname
for pattern, to in patterns:
for pattern, to in the_patterns:
try:
to_name = re.sub(pattern, to, to_name)
except Exception as ex:
error("Couldn't substitute pattern \n %s\nto\n %s\nin string\n %s\n\n" % (pattern, to, to_name) , ex)
else:
to_name = '/%s' % fname

elif isinstance(patterns, types.FunctionType):
to_name = patterns(fname)
else:
error('Unknown patterns type %s' % type(patterns))

if the_remap != None:
to_name = the_remap(fname)

#info('to_name = %s' % to_name)

Expand All @@ -1059,5 +1071,5 @@ def write_file(fname):
raise ValueError("Don't know how to handle %s" % rel_path)
archive.close()

info("Wrote %s" % zip_path)
info("Wrote %s.zip" % zip_path)

0 comments on commit 131869e

Please sign in to comment.