Skip to content

Commit 6e74e69

Browse files
committed
WeBWorK: xsl and pretext for 2.19
1 parent 026b3a4 commit 6e74e69

File tree

7 files changed

+270
-121
lines changed

7 files changed

+270
-121
lines changed

pretext/lib/pretext.py

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,7 @@ def webwork_to_xml(
11231123
ww_reps_dir = os.path.join(generated_dir, "webwork")
11241124
# where generated images from webwork exercises will live
11251125
ww_images_dir = os.path.join(ww_reps_dir, "images")
1126+
11261127
else:
11271128
msg = "".join(
11281129
[
@@ -1165,7 +1166,10 @@ def webwork_to_xml(
11651166
extracted_pg_xml = ET.parse(extracted_pg_filename).getroot()
11661167
localization = extracted_pg_xml.get("localization")
11671168
webwork2_server = extracted_pg_xml.find("server-params-pub").get("webwork2-server")
1169+
renderer_server = extracted_pg_xml.find("server-params-pub").get("renderer")
11681170
numbered_title_filesafe = extracted_pg_xml.get("numbered-title-filesafe")
1171+
generated_dir_source = extracted_pg_xml.find("directories").get("generated-directory-source")
1172+
external_dir_source = extracted_pg_xml.find("directories").get("external-directory-source")
11691173
ww_project_dir = os.path.join(ww_reps_dir, "pg", numbered_title_filesafe)
11701174
if not (os.path.isdir(ww_project_dir)):
11711175
os.mkdir(ww_project_dir)
@@ -1185,6 +1189,7 @@ def webwork_to_xml(
11851189
"user": extracted_pg_xml.find("server-params-pub").get("user-id"),
11861190
"passwd": extracted_pg_xml.find("server-params-pub").get("password"),
11871191
"disableCookies": '1',
1192+
"renderer": renderer_server
11881193
}
11891194
static_processing = extracted_pg_xml.find("processing").attrib["static"]
11901195
pg_location = extracted_pg_xml.find("processing").attrib["pg-location"]
@@ -1196,8 +1201,10 @@ def webwork_to_xml(
11961201
"user": "anonymous",
11971202
"passwd": "anonymous",
11981203
"disableCookies": '1',
1204+
"renderer": "https://webwork-dev.aimath.org"
11991205
}
12001206
static_processing = 'webwork2'
1207+
interactive_processing = 'webwork2'
12011208
pg_location = '/opt/webwork/pg'
12021209

12031210
# ideally, pub_file is in use, in which case server_params_pub is nonempty.
@@ -1235,6 +1242,8 @@ def webwork_to_xml(
12351242
courseID = server_params_pub["courseID"]
12361243
user = server_params_pub["user"]
12371244
passwd = server_params_pub["passwd"]
1245+
if static_processing == "renderer":
1246+
renderer = sanitize_url(server_params_pub["renderer"])
12381247

12391248
webwork2_domain_webwork2 = webwork2_domain + "/webwork2/"
12401249
webwork2_render_rpc = webwork2_domain_webwork2 + "render_rpc"
@@ -1250,6 +1259,12 @@ def webwork_to_xml(
12501259
or (extracted_pg_xml.xpath("//problem[@origin='webwork2']"))
12511260
)
12521261

1262+
# Establish if there is any need to use renderer
1263+
need_for_renderer = (
1264+
(static_processing == 'renderer')
1265+
and (extracted_pg_xml.xpath("//problem[@origin!='webwork2']"))
1266+
)
1267+
12531268
# Establish if there is any need to use a socket
12541269
need_for_socket = (
12551270
(static_processing == 'local')
@@ -1264,6 +1279,8 @@ def webwork_to_xml(
12641279
raise ImportError(__module_warning.format("requests"))
12651280

12661281
# Establish WW server version, for live rendering if nothing else
1282+
# Note: if the renderer is doing live rendering there may be no need for a WW server and
1283+
# this entire block can be conditioned on need_for_webwork2
12671284
# First try to identify the WW version according to what a response hash says it is.
12681285
# This should work for 2.17 and beyond.
12691286
try:
@@ -1317,6 +1334,14 @@ def webwork_to_xml(
13171334
+ " Is there a WeBWorK landing page at {}?\n"
13181335
+ " And does it display the WeBWorK version?\n"
13191336
)
1337+
raise ValueError(msg.format(webwork2_domain_webwork2))
1338+
1339+
if webwork2_major_version != 2 or webwork2_minor_version < 16:
1340+
msg = (
1341+
"PTX:ERROR: PreTeXt supports WeBWorK 2.16 and later, and it appears you are attempting to use version: {}\n"
1342+
+ " Server: {}\n"
1343+
+ " You may want to use the AIM WeBWorK server at webwork-ptx.aimath.org.\n"
1344+
)
13201345
raise ValueError(msg.format(webwork2_version, webwork2_domain))
13211346

13221347
webwork2_path = webwork2_render_rpc if (webwork2_major_version == 2 and webwork2_minor_version >= 19) else webwork2_html2xml
@@ -1340,19 +1365,14 @@ def webwork_to_xml(
13401365
pghuman[problem.get("id")] = problem.find("pghuman").text
13411366
pgdense[problem.get("id")] = problem.find("pgdense").text
13421367

1343-
if webwork2_major_version != 2 or webwork2_minor_version < 16:
1344-
msg = (
1345-
"PTX:ERROR: PreTeXt supports WeBWorK 2.16 and later, and it appears you are attempting to use version: {}\n"
1346-
+ " Server: {}\n"
1347-
+ " You may want to use the AIM WeBWorK server at webwork-ptx.aimath.org.\n"
1348-
)
1349-
raise ValueError(msg.format(ww_version, ww_domain))
1350-
13511368
# using a "Session()" will pool connection information
13521369
# since we always hit the same server, this should increase performance
13531370
if need_for_webwork2:
13541371
webwork2_session = requests.Session()
13551372

1373+
if need_for_renderer:
1374+
renderer_session = requests.Session()
1375+
13561376
clientsocket = None
13571377

13581378
if need_for_socket:
@@ -1397,13 +1417,15 @@ def webwork_to_xml(
13971417
webwork_representations = ET.Element("webwork-representations", nsmap=NSMAP)
13981418
# Choose one of the dictionaries to take its keys as what to loop through
13991419
for problem in origin:
1400-
if origin[problem] == "webwork2":
1420+
if origin[problem] == "external":
1421+
msg = "building representations of external WeBWorK problem"
1422+
elif origin[problem] == "webwork2":
14011423
msg = "building representations of webwork2-hosted WeBWorK problem"
14021424
elif origin[problem] == "generated":
14031425
msg = "building representations of generated WeBWorK problem"
14041426
else:
14051427
raise ValueError(
1406-
"PTX:ERROR: problem origin should be 'webwork2' or 'generated', not '{}'".format(
1428+
"PTX:ERROR: problem origin should be 'external', 'webwork2', or 'generated', not '{}'".format(
14071429
origin[problem]
14081430
)
14091431
)
@@ -1431,7 +1453,7 @@ def webwork_to_xml(
14311453
# but kill this for the code that is used repeatedly by embedded problems in HTML
14321454
# So here we branch a copy for embedding where we kill `$refreshCachedImages=1;`
14331455
# But we can't literally just remove that, since an author may have used something
1434-
# like `$refreshCachedImages = 'true' ;` so instead, we change `$refreshCachedImages`
1456+
# like `$refreshCachedImages = 'true' ;` so instead, we change `refreshCachedImages`
14351457
# to something inert
14361458
if origin[problem] == "generated":
14371459
embed_problem = re.sub(r'(refreshCachedImages)(?![\w\d])', r'\1Inert', pgdense[problem])
@@ -1460,16 +1482,44 @@ def webwork_to_xml(
14601482
if not received: break
14611483
buffer.extend(received)
14621484
if buffer.endswith(b'ENDOFSOCKETDATA'): break
1463-
14641485
response = buffer.decode().replace('ENDOFSOCKETDATA', '')
14651486

1487+
elif static_processing == 'renderer' and origin[problem] != 'webwork2':
1488+
if origin[problem] == "external":
1489+
with open(os.path.join(external_dir, path[problem])) as f: rawProblemSource = f.read()
1490+
server_params_source = {"rawProblemSource":rawProblemSource}
1491+
else:
1492+
server_params_source = {"rawProblemSource":pgdense[problem]}
1493+
1494+
server_params = {
1495+
"showSolutions": "1",
1496+
"showHints": "1",
1497+
"displayMode": "PTX",
1498+
"courseID": courseID,
1499+
"user": user,
1500+
"passwd": passwd,
1501+
"outputformat": "ptx",
1502+
"problemSeed": seed[problem],
1503+
"problemUUID": problem,
1504+
}
1505+
server_params.update(server_params_source)
1506+
1507+
msg = "sending {} to renderer to save in {}: origin is '{}'"
1508+
log.info(msg.format(problem, ww_reps_file, origin[problem]))
1509+
1510+
response = renderer_session.post(renderer + '/renderer/render-ptx', data=server_params)
1511+
response = response.text
1512+
14661513
else:
14671514
# Construct URL to get static version from server
14681515
# First establish how the acctual problem code
14691516
# should be delivered to whatever will render it
14701517
if webwork2_minor_version >= 19:
14711518
if origin[problem] == "webwork2":
14721519
server_params_source = {"sourceFilePath":path[problem]}
1520+
elif origin[problem] == "external":
1521+
with open(os.path.join(external_dir, path[problem])) as f: rawProblemSource = f.read()
1522+
server_params_source = {"rawProblemSource":rawProblemSource}
14731523
else:
14741524
server_params_source = {"rawProblemSource":pgdense[problem]}
14751525
else:
@@ -1511,7 +1561,7 @@ def webwork_to_xml(
15111561

15121562
msg = "sending {} to server to save in {}: origin is '{}'"
15131563
log.info(msg.format(problem, ww_reps_file, origin[problem]))
1514-
if origin[problem] == "webwork2":
1564+
if origin[problem] == "external" or origin[problem] == "webwork2":
15151565
log.debug(
15161566
"server-to-ptx: {}\n{}\n{}\n{}".format(
15171567
problem, webwork2_path, path[problem], ww_reps_file
@@ -1685,7 +1735,10 @@ def webwork_to_xml(
16851735
if static_processing == 'local' and origin[problem] != 'webwork2':
16861736
ww_image_scheme = ''
16871737
else:
1688-
ww_image_url = urllib.parse.urljoin(webwork2_domain, ww_image_full_path)
1738+
if static_processing == 'renderer' and origin[problem] != 'webwork2':
1739+
ww_image_url = urllib.parse.urljoin(renderer, ww_image_full_path)
1740+
else:
1741+
ww_image_url = urllib.parse.urljoin(webwork2_domain, ww_image_full_path)
16891742
# strip away the scheme and location, if present (e.g 'https://webwork-ptx.aimath.org/')
16901743
ww_image_url_parsed = urllib.parse.urlparse(ww_image_url)
16911744
ww_image_scheme = ww_image_url_parsed.scheme
@@ -1753,7 +1806,10 @@ def webwork_to_xml(
17531806
# download actual image files
17541807
# http://stackoverflow.com/questions/13137817/how-to-download-image-using-requests
17551808
try:
1756-
image_response = webwork2_session.get(image_url)
1809+
if static_processing == 'renderer' and origin[problem] != 'webwork2':
1810+
image_response = renderer_session.get(image_url)
1811+
else:
1812+
image_response = webwork2_session.get(image_url)
17571813
except requests.exceptions.RequestException as e:
17581814
root_cause = str(e)
17591815
msg = "PTX:ERROR: there was a problem downloading an image file,\n URL: {}\n"
@@ -1806,7 +1862,7 @@ def webwork_to_xml(
18061862
webwork_reps.set("ww-id", problem)
18071863
static = ET.SubElement(webwork_reps, "static")
18081864
static.set("seed", seed[problem])
1809-
if origin[problem] == "webwork2":
1865+
if origin[problem] == "external" or origin[problem] == "webwork2":
18101866
static.set("source", path[problem])
18111867

18121868
# If there is "badness"...
@@ -1916,6 +1972,8 @@ def static_webwork_level(write, read):
19161972
else:
19171973
if origin[problem] == "webwork2":
19181974
source_value = path[problem]
1975+
elif origin[problem] == "external" and webwork2_minor_version >= 19:
1976+
source_value = os.path.join(external_dir, path[problem])
19191977
else:
19201978
if webwork2_minor_version < 19:
19211979
source_value = embed_problem_base64
@@ -1929,6 +1987,7 @@ def static_webwork_level(write, read):
19291987
rendering_data.set(source_key, source_value)
19301988
rendering_data.set("origin", origin[problem])
19311989
rendering_data.set("domain", webwork2_domain)
1990+
rendering_data.set("renderer", renderer_server)
19321991
rendering_data.set("course-id", courseID)
19331992
rendering_data.set("user-id", user)
19341993
rendering_data.set("passwd", passwd)
@@ -1951,7 +2010,7 @@ def static_webwork_level(write, read):
19512010
else:
19522011
formatted_pg = pghuman[problem]
19532012
pg.text = ET.CDATA("\n" + formatted_pg)
1954-
elif origin[problem] == "webwork2":
2013+
elif origin[problem] == "external" or origin[problem] == "webwork2":
19552014
pg.set("source", path[problem])
19562015

19572016
# write to file
@@ -1976,6 +2035,14 @@ def static_webwork_level(write, read):
19762035
webwork2_session.close()
19772036
except:
19782037
pass
2038+
try:
2039+
renderer_session.close()
2040+
except:
2041+
pass
2042+
2043+
# close the socket
2044+
if clientsocket:
2045+
clientsocket.send(b'quit')
19792046

19802047
# close the socket
19812048
if clientsocket:
@@ -2032,9 +2099,15 @@ def pg_macros(xml_source, pub_file, stringparams, dest_dir):
20322099

20332100
if pub_file:
20342101
stringparams["publisher"] = pub_file
2102+
20352103
ptx_xsl_dir = get_ptx_xsl_path()
20362104
extraction_xslt = os.path.join(ptx_xsl_dir, "support", "pretext-pg-macros.xsl")
2037-
xsltproc(extraction_xslt, xml_source, None, output_dir=dest_dir, stringparams=stringparams)
2105+
tmp_dir = get_temporary_directory()
2106+
xsltproc(extraction_xslt, xml_source, None, output_dir=tmp_dir, stringparams=stringparams)
2107+
copy_build_directory(tmp_dir, dest_dir)
2108+
#folder_name = os.listdir(tmp_dir)[0]
2109+
#folder = os.path.join(tmp_dir, folder_name)
2110+
#copy_build_directory(folder, os.path.join(dest_dir,folder_name))
20382111

20392112
############################################################
20402113
#

pretext/pretext

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,10 @@ def get_cli_arguments():
326326
("latex-image", "LaTeX pictures (method: [xelatex], pdflatex)"),
327327
(
328328
"webwork",
329-
"WeBWorK problems in authored, PG, url, and static representations",
329+
"WeBWorK problems in PG files and various representations",
330330
),
331331
("references", "References and citations via Citation Stylesheet Language"),
332-
("pg-macros", "Server-side macros for WeBWorK problem generation"),
332+
("pg-macros", "PG macros for WeBWorK problem generation"),
333333
("dynamic", "Expression subsitutions for dynamically generated exercises"),
334334
("youtube", "Thumbnails for YouTube videos (JPEG only)"),
335335
("play-button", "generate generic static video image"),
@@ -387,7 +387,7 @@ def get_cli_arguments():
387387
),
388388
("assembly-version", "PreTeXt source, after pre-processor resolves versions and customizations (for developers)", ),
389389
("sagenb", "Sage worksheet conversion (removed)"),
390-
("webwork-sets", "Folder tree of PG files, set definitions, and set headers"),
390+
("webwork-sets", "Folder tree of PG files, PG macros, set definitions, and set headers"),
391391
("all", "All available output formats"),
392392
]
393393
format_help = "Output formats are:\n" + "\n".join(

0 commit comments

Comments
 (0)