cornellGrading 2.19.0__tar.gz → 2.20.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {cornellgrading-2.19.0/cornellGrading.egg-info → cornellgrading-2.20.0}/PKG-INFO +1 -1
  2. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading/__init__.py +1 -1
  3. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading/cornellGrading.py +239 -3
  4. cornellgrading-2.20.0/cornellGrading/utils.py +55 -0
  5. {cornellgrading-2.19.0 → cornellgrading-2.20.0/cornellGrading.egg-info}/PKG-INFO +1 -1
  6. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading.egg-info/SOURCES.txt +1 -0
  7. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/LICENSE +0 -0
  8. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/MANIFEST.in +0 -0
  9. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/README.md +0 -0
  10. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading/cornellInterface.py +0 -0
  11. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading/cornellQualtrics.py +0 -0
  12. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading/dueDatesFromCSV.py +0 -0
  13. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading/pandocHTMLParser.py +0 -0
  14. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading/upload_MC_questions.py +0 -0
  15. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading.egg-info/dependency_links.txt +0 -0
  16. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading.egg-info/entry_points.txt +0 -0
  17. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading.egg-info/requires.txt +0 -0
  18. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/cornellGrading.egg-info/top_level.txt +0 -0
  19. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/pyproject.toml +0 -0
  20. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/scripts/canvastex.py +0 -0
  21. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/scripts/matlabgrader.py +0 -0
  22. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/scripts/setupSelfGrading.py +0 -0
  23. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/setup.cfg +0 -0
  24. {cornellgrading-2.19.0 → cornellgrading-2.20.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cornellGrading
3
- Version: 2.19.0
3
+ Version: 2.20.0
4
4
  Summary: Routines for interacting with Cornell installations of Canvas and Qualtrics
5
5
  Home-page: https://github.com/dsavransky/grading
6
6
  Author: Dmitry Savransky
@@ -1,4 +1,4 @@
1
1
  name = "cornellGrading"
2
- __version__ = "2.19.0"
2
+ __version__ = "2.20.0"
3
3
  from .cornellGrading import cornellGrading
4
4
  from .cornellQualtrics import cornellQualtrics
@@ -5,6 +5,7 @@ import keyring
5
5
  import time
6
6
  from datetime import datetime, timedelta
7
7
  import pytz
8
+ import canvasapi
8
9
  from canvasapi import Canvas
9
10
  from canvasapi.exceptions import InvalidAccessToken
10
11
  import tempfile
@@ -16,6 +17,8 @@ import urllib.parse
16
17
  import subprocess
17
18
  import shutil
18
19
  import requests
20
+ import uuid
21
+ from cornellGrading.utils import convalllatex
19
22
 
20
23
  try:
21
24
  from cornellGrading.pandocHTMLParser import pandocHTMLParser
@@ -2054,7 +2057,7 @@ class cornellGrading:
2054
2057
  assignmentNum,
2055
2058
  nprobs,
2056
2059
  solfile,
2057
- item,
2060
+ npoints=3,
2058
2061
  preamble="",
2059
2062
  selfGradeDueDelta=7,
2060
2063
  selfGradeReleasedDelta=3,
@@ -2070,8 +2073,8 @@ class cornellGrading:
2070
2073
  Number of howmework problems
2071
2074
  solfile (str):
2072
2075
  Full path to solutions file to upload
2073
- item (dict):
2074
- New Quiz item payload dictionary. See `addNewQuizItem`
2076
+ npoints (int):
2077
+ Number of points each question is scored out of. Defaults to 3.
2075
2078
  preamble (str):
2076
2079
  Preamble for all naming.
2077
2080
  See :py:meth:`cornellGrading.cornellGrading.getHomework` for details.
@@ -2132,6 +2135,9 @@ class cornellGrading:
2132
2135
  }
2133
2136
  )
2134
2137
 
2138
+ # create item
2139
+ item = self.genNpointNewQuizItem(npoints)
2140
+
2135
2141
  # add new quiz items
2136
2142
  for j in range(nprobs):
2137
2143
  tmp = item.copy()
@@ -2256,3 +2262,233 @@ class cornellGrading:
2256
2262
  self.uploadScores(hw, netids, scores)
2257
2263
 
2258
2264
  return submittedScoreNoAssignment, submittedAssignmentNoScore
2265
+
2266
+ def genNpointNewQuizItem(self, n, item_body=None, title=None):
2267
+ """Generate a New Quiz multiple choice, variable point question with answers
2268
+ ranging from 0 to n and each answer worth the equivalent number of points.
2269
+
2270
+ Args:
2271
+ n (int):
2272
+ Number of points possible. Question will have n+1 options (from 0 to n)
2273
+ with each response worth the equivalent number of points
2274
+ item_body (str, optional):
2275
+ Question text (html formatted). If None (default) this is set to
2276
+ <p>Enter your score based on the rubric in the syllabus</p>
2277
+ title (str, optional):
2278
+ Question title. If None (default) this is set to:
2279
+ HW Problem Score
2280
+
2281
+ Returns:
2282
+ dict:
2283
+ New Quiz Multiple choice question definition
2284
+
2285
+ """
2286
+
2287
+ # set default body text and title
2288
+ if item_body is None:
2289
+ item_body = "<p>Enter your score based on the rubric in the syllabus</p>"
2290
+
2291
+ if title is None:
2292
+ title = "HW Problem Score"
2293
+
2294
+ # generate UUIDs
2295
+ uuids = [uuid.uuid4() for _ in range(n + 1)]
2296
+
2297
+ # create choices and values dict list
2298
+ choices = []
2299
+ values = []
2300
+ for j in range(n + 1):
2301
+ choices.append(
2302
+ {"id": f"{uuids[j]}", "position": j + 1, "item_body": f"<p>{j}</p>"}
2303
+ )
2304
+ values.append({"value": f"{uuids[j]}", "points": j})
2305
+
2306
+ q = {
2307
+ "position": position,
2308
+ "points_possible": float(n),
2309
+ "entry_type": "Item",
2310
+ "status": "immutable",
2311
+ "entry": {
2312
+ "title": title,
2313
+ "item_body": item_body,
2314
+ "calculator_type": "none",
2315
+ "interaction_data": {"choices": choices},
2316
+ "properties": {
2317
+ "shuffle_rules": {"choices": {"to_lock": [], "shuffled": False}},
2318
+ "vary_points_by_answer": True,
2319
+ },
2320
+ "scoring_data": {
2321
+ "value": f"{uuids[-1]}",
2322
+ "values": values,
2323
+ },
2324
+ "answer_feedback": {f"{uuids[0]}": ""},
2325
+ "scoring_algorithm": "VaryPointsByAnswer",
2326
+ "interaction_type_slug": "choice",
2327
+ "feedback": {},
2328
+ },
2329
+ }
2330
+
2331
+ return q
2332
+
2333
+ def genNewQuizMultipleChoice(
2334
+ self, question, options, correct_ind, points=1, position=0
2335
+ ):
2336
+ """Generate a new quiz-style multiple choice question dictionary
2337
+
2338
+ Args:
2339
+ question (str):
2340
+ The raw question text (may include LaTeX)
2341
+ options (list(str) or ~numpy.ndarray(str)):
2342
+ Possible answers. All contents must be strings (may include LaTeX)
2343
+ correct_ind (ind):
2344
+ Index of correct answer in `options` input.
2345
+ points (int):
2346
+ Number of points for correct answer (defaults to 1).
2347
+ position (int):
2348
+ Position of question in quiz. Defaults to 0
2349
+
2350
+ Returns:
2351
+ dict:
2352
+ Question-defining dictionary
2353
+
2354
+ """
2355
+
2356
+ # generate UUIDs
2357
+ uuids = [uuid.uuid4() for _ in range(len(options))]
2358
+
2359
+ # create choices dict list
2360
+ choices = []
2361
+ for j in range(len(options)):
2362
+ choices.append(
2363
+ {
2364
+ "id": f"{uuids[j]}",
2365
+ "position": j + 1,
2366
+ "item_body": f"<p>{convalllatex(options[j])}</p>",
2367
+ }
2368
+ )
2369
+
2370
+ q = {
2371
+ "position": position,
2372
+ "points_possible": float(points),
2373
+ "properties": {},
2374
+ "entry_type": "Item",
2375
+ "entry_editable": True,
2376
+ "stimulus_quiz_entry_id": "",
2377
+ "status": "mutable",
2378
+ "entry": {
2379
+ "title": question,
2380
+ "item_body": f"<p>{convalllatex(question)}</p>",
2381
+ "calculator_type": "none",
2382
+ "interaction_data": {"choices": choices},
2383
+ "properties": {
2384
+ "shuffle_rules": {"choices": {"to_lock": [], "shuffled": False}},
2385
+ "vary_points_by_answer": False,
2386
+ },
2387
+ "scoring_data": {"value": f"{uuids[correct_ind]}"},
2388
+ "answer_feedback": {},
2389
+ "scoring_algorithm": "Equivalence",
2390
+ "interaction_type_slug": "choice",
2391
+ "feedback": {},
2392
+ },
2393
+ }
2394
+
2395
+ return q
2396
+
2397
+ def genQuizMultipleChoice(
2398
+ self, question, options, correct_ind, points=1, position=0
2399
+ ):
2400
+ """ "Generate a classic quiz-style multiple choice question dictionary
2401
+
2402
+ Args:
2403
+ question (str):
2404
+ The raw question text (may include LaTeX)
2405
+ options (list(str) or ~numpy.ndarray(str)):
2406
+ Possible answers. All contents must be strings (may include LaTeX)
2407
+ correct_ind (ind):
2408
+ Index of correct answer in `options` input.
2409
+ points (int):
2410
+ Number of points for correct answer (defaults to 1).
2411
+ position (int):
2412
+ Position of question in quiz. Defaults to 0
2413
+
2414
+ Returns:
2415
+ dict:
2416
+ Question-defining dictionary
2417
+
2418
+ """
2419
+
2420
+ # create answers dict list
2421
+ answers = []
2422
+ for j in range(len(options)):
2423
+ answers.append(
2424
+ {
2425
+ "answer_html": f"<p>{convalllatex(options[j])}</p>",
2426
+ "answer_weight": 100 if j == correct_ind else 0,
2427
+ }
2428
+ )
2429
+
2430
+ q = {
2431
+ "question_name": question,
2432
+ "question_text": f"<p>{convalllatex(question)}</p>",
2433
+ "question_type": "multiple_choice_question",
2434
+ "position": 0,
2435
+ "points_possible": points,
2436
+ "answers": answers,
2437
+ }
2438
+
2439
+ return q
2440
+
2441
+ def genQuizFromPolev(self, allqs, quiz):
2442
+ """Generate quiz items from a PollEv formatted CSV input file
2443
+
2444
+ Args:
2445
+ allqs (pandas.DataFrame):
2446
+ Table of questions and answers, formatted in PolEV CSV upload style
2447
+ quiz (canvasapi.new_quiz.NewQuiz or canvasapi.quiz.Quiz):
2448
+ Quiz object
2449
+
2450
+ Returns:
2451
+ None
2452
+
2453
+ .. note::
2454
+ Poll Everywhere upload format documentation is available here:
2455
+ https://support.polleverywhere.com/hc/en-us/articles/1260801546530-Import-questions
2456
+
2457
+ """
2458
+
2459
+ assert isinstance(
2460
+ quiz, (canvasapi.new_quiz.NewQuiz, canvasapi.quiz.Quiz)
2461
+ ), "quiz input must be a Quiz or New Quiz object."
2462
+
2463
+ # identify response columns:
2464
+ optcols = []
2465
+ for col in allqs.columns:
2466
+ if col.startswith("Option"):
2467
+ optcols.append(col)
2468
+
2469
+ # iterate through rows, creating the questions
2470
+ for j, row in allqs.iterrows():
2471
+ question = row.Title
2472
+ opts = row[optcols].values
2473
+ opts = opts[~pandas.isna(opts)]
2474
+ correct_ind = [
2475
+ j
2476
+ for j, s in enumerate(opts)
2477
+ if isinstance(s, str) and s.startswith("***")
2478
+ ]
2479
+ assert len(correct_ind) == 1
2480
+ correct_ind = correct_ind[0]
2481
+ opts[correct_ind] = opts[correct_ind].strip("***")
2482
+
2483
+ # create and add the question depending on quiz type
2484
+ if isinstance(quiz, canvasapi.new_quiz.NewQuiz):
2485
+ q = self.genNewQuizMultipleChoice(
2486
+ question, opts.astype(str), correct_ind, position=j + 1
2487
+ )
2488
+
2489
+ self.addNewQuizItem(quiz.id, q)
2490
+ else:
2491
+ q = self.genQuizMultipleChoice(
2492
+ question, opts.astype(str), correct_ind, position=j + 1
2493
+ )
2494
+ quiz.create_question(question=q)
@@ -0,0 +1,55 @@
1
+ import urllib.parse
2
+ import re
3
+
4
+
5
+ def convalllatex(text):
6
+ """Convert all instances of LaTeX in a string to Canvas-compatible images
7
+
8
+ Args:
9
+ text (str):
10
+ Input text
11
+
12
+ Returns:
13
+ str:
14
+ Output text
15
+
16
+ """
17
+
18
+ p = re.compile(r"\${1,2}(.*?)\${1,2}")
19
+
20
+ return p.sub(convlatex, text)
21
+
22
+
23
+ def convlatex(texstr):
24
+ """Convert input latex string to Canvas's img html
25
+
26
+ Args:
27
+ texstr (str or re.Match):
28
+ LaTeX-formatted equation string
29
+
30
+ Returns:
31
+ str:
32
+ Canvas-style image string
33
+
34
+ """
35
+
36
+ # handle case where input is re.Match
37
+ if isinstance(texstr, re.Match):
38
+ texstr = texstr.groups()[0]
39
+
40
+ # replace problematic commands int LaTeX
41
+ texsubdict = {
42
+ r"\\textrm": "",
43
+ }
44
+ for key, val in texsubdict.items():
45
+ texstr = re.sub(key, val, texstr)
46
+
47
+ convstr = urllib.parse.quote(urllib.parse.quote(texstr))
48
+ qtxt = (
49
+ f"""<img class="equation_image" title="{texstr}" """
50
+ f"""src="/equation_images/{convstr}?scale=1" """
51
+ f"""alt="LaTeX: {texstr}" """
52
+ f""" data-equation-content="{texstr}" data-ignore-a11y-check="">"""
53
+ )
54
+
55
+ return qtxt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cornellGrading
3
- Version: 2.19.0
3
+ Version: 2.20.0
4
4
  Summary: Routines for interacting with Cornell installations of Canvas and Qualtrics
5
5
  Home-page: https://github.com/dsavransky/grading
6
6
  Author: Dmitry Savransky
@@ -11,6 +11,7 @@ cornellGrading/cornellQualtrics.py
11
11
  cornellGrading/dueDatesFromCSV.py
12
12
  cornellGrading/pandocHTMLParser.py
13
13
  cornellGrading/upload_MC_questions.py
14
+ cornellGrading/utils.py
14
15
  cornellGrading.egg-info/PKG-INFO
15
16
  cornellGrading.egg-info/SOURCES.txt
16
17
  cornellGrading.egg-info/dependency_links.txt
File without changes