canvaslms 5.10__py3-none-any.whl → 6.1__py3-none-any.whl

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.
canvaslms/cli/quizzes.nw CHANGED
@@ -3204,6 +3204,11 @@ def _parse_quiz_full_format(data, format_type):
3204
3204
 
3205
3205
  if 'settings' in data:
3206
3206
  settings = data['settings'].copy()
3207
+ # Allow metadata (like id/regex) at the top level.
3208
+ # This makes the format more forgiving for hand-edited files.
3209
+ for k in ('id', 'regex', 'title'):
3210
+ if k in data and k not in settings:
3211
+ settings[k] = data[k]
3207
3212
  else:
3208
3213
  # Simple format: entire dict is settings (excluding metadata keys)
3209
3214
  settings = {k: v for k, v in data.items()
@@ -3252,6 +3257,13 @@ FILE MODE (-f):
3252
3257
  The JSON/YAML format is the same as 'quizzes export' output, enabling
3253
3258
  a round-trip workflow: export, modify, edit.
3254
3259
 
3260
+ When using -f, the -a/--assignment selector is optional if the file provides
3261
+ enough information to identify the quiz. We use the first available of:
3262
+ - settings.id (exact ID match)
3263
+ - settings.regex (regex matching title or ID)
3264
+ - settings.title (exact title match; errors if duplicate titles exist
3265
+ within a course)
3266
+
3255
3267
  ITEM HANDLING:
3256
3268
  By default, items/questions in the file are ignored to protect student
3257
3269
  submissions. Use --replace-items to replace all questions (with confirmation
@@ -3266,7 +3278,13 @@ try:
3266
3278
  except argparse.ArgumentError:
3267
3279
  pass
3268
3280
 
3269
- add_quiz_option(edit_parser, required=True)
3281
+ # NOTE: We intentionally do not call add_quiz_option(required=False) here.
3282
+ # That helper defaults to '.*', which would select all quizzes if -a is
3283
+ # omitted. For quizzes edit, -a is required only in interactive mode.
3284
+ edit_parser.add_argument("-a", "--assignment",
3285
+ required=False,
3286
+ default=None,
3287
+ help="Regex matching quiz title or Canvas ID")
3270
3288
 
3271
3289
  edit_parser.add_argument("-f", "--file",
3272
3290
  help="Read content from file (format auto-detected: .json, .yaml, .yml, .md)",
@@ -3299,12 +3317,14 @@ In file mode, we read the content from the specified file.
3299
3317
  <<functions>>=
3300
3318
  def edit_command(config, canvas, args):
3301
3319
  """Edits quiz settings and instructions"""
3302
- quiz_list = process_quiz_option(canvas, args)
3303
3320
  requester = canvas._Canvas__requester
3304
3321
 
3305
3322
  if args.file:
3306
3323
  <<edit quiz from file>>
3307
3324
  else:
3325
+ if not getattr(args, 'assignment', None):
3326
+ canvaslms.cli.err(1, "-a/--assignment is required unless -f/--file is used")
3327
+ quiz_list = process_quiz_option(canvas, args)
3308
3328
  <<edit quiz interactively>>
3309
3329
  @
3310
3330
 
@@ -3329,21 +3349,80 @@ except ValueError as e:
3329
3349
  <<apply settings from file>>
3330
3350
  @
3331
3351
 
3332
- We extract [[id]] from either the top level or the settings:
3352
+ We can identify the target quiz in two ways:
3353
+ \begin{description}
3354
+ \item[By command-line selector] If [[-a/--assignment]] is provided, we select
3355
+ quizzes using that regex (matching quiz title or ID), just like interactive
3356
+ mode.
3357
+ \item[By file metadata] If [[-a]] is omitted, the file must provide one of
3358
+ [[settings.id]], [[settings.regex]], or [[settings.title]]. This is the same
3359
+ idea as the [[regex]] field used by [[pages]] and [[assignments]]: we preserve
3360
+ the user's selection expression so scripted edits stay stable.
3361
+ \end{description}
3333
3362
 
3334
3363
  <<identify target quiz from file data>>=
3335
- settings = quiz_data['settings']
3336
- quiz_id = settings.get('id')
3364
+ settings = quiz_data.get('settings') or {}
3365
+ course_list = courses.process_course_option(canvas, args)
3366
+
3367
+ if getattr(args, 'assignment', None):
3368
+ quiz_list = list(filter_quizzes(course_list, args.assignment))
3369
+ if not quiz_list:
3370
+ canvaslms.cli.err(1, f"No quiz found matching: {args.assignment}")
3371
+ else:
3372
+ quiz_id = settings.get('id')
3373
+ quiz_regex = settings.get('regex')
3374
+ quiz_title = settings.get('title')
3375
+
3376
+ if quiz_id is not None and str(quiz_id).strip() != "":
3377
+ quiz_list = []
3378
+ for course in course_list:
3379
+ for quiz in fetch_all_quizzes(course):
3380
+ if str(quiz.id) == str(quiz_id):
3381
+ quiz.course = course
3382
+ quiz_list.append(quiz)
3383
+ if not quiz_list:
3384
+ canvaslms.cli.err(1, f"Quiz with ID {quiz_id} not found")
3385
+ if len(quiz_list) > 1:
3386
+ canvaslms.cli.err(1, f"Multiple quizzes match ID {quiz_id}")
3387
+ elif quiz_regex is not None and str(quiz_regex).strip() != "":
3388
+ quiz_list = list(filter_quizzes(course_list, str(quiz_regex)))
3389
+ if not quiz_list:
3390
+ canvaslms.cli.err(1, f"No quiz found matching file regex: {quiz_regex}")
3391
+ elif quiz_title is not None and str(quiz_title).strip() != "":
3392
+ quiz_list = []
3393
+ title = str(quiz_title)
3394
+ for course in course_list:
3395
+ matches = [q for q in fetch_all_quizzes(course)
3396
+ if getattr(q, 'title', None) == title]
3397
+ for q in matches:
3398
+ q.course = course
3399
+ if len(matches) > 1:
3400
+ canvaslms.cli.err(
3401
+ 1,
3402
+ f"Multiple quizzes titled '{title}' in course {course.course_code}. "
3403
+ "Use settings.regex (or -a) to disambiguate."
3404
+ )
3405
+ if matches:
3406
+ quiz_list.extend(matches)
3407
+
3408
+ if not quiz_list:
3409
+ canvaslms.cli.err(1, f"No quiz found with title: {title}")
3410
+ else:
3411
+ canvaslms.cli.err(
3412
+ 1,
3413
+ "File mode without -a requires one of settings.id, settings.regex, "
3414
+ "or settings.title"
3415
+ )
3337
3416
 
3338
- if quiz_id:
3339
- target_quiz = None
3417
+ if len(quiz_list) > 1:
3418
+ print(f"Found {len(quiz_list)} quizzes to update:")
3340
3419
  for quiz in quiz_list:
3341
- if str(quiz.id) == str(quiz_id):
3342
- target_quiz = quiz
3343
- break
3344
- if not target_quiz:
3345
- canvaslms.cli.err(1, f"Quiz with ID {quiz_id} not found")
3346
- quiz_list = [target_quiz]
3420
+ course_code = quiz.course.course_code if hasattr(quiz, 'course') else "?"
3421
+ print(f" - {course_code}: {quiz.title} (ID: {quiz.id})")
3422
+ response = input("\nEdit all? [y/N] ").strip().lower()
3423
+ if response != 'y':
3424
+ print("Aborted.")
3425
+ return
3347
3426
  @
3348
3427
 
3349
3428
  If [[--replace-items]] is specified and the file contains items, we replace
@@ -4056,7 +4135,7 @@ def quiz_attributes_to_api_params(attributes, is_new, html_body):
4056
4135
  params = {}
4057
4136
 
4058
4137
  for key, value in attributes.items():
4059
- if key == 'id':
4138
+ if key in ('id', 'regex', 'modules'):
4060
4139
  # Don't send ID as an update parameter
4061
4140
  continue
4062
4141
 
@@ -4374,6 +4453,10 @@ def export_command(config, canvas, args):
4374
4453
  if not quiz_list:
4375
4454
  canvaslms.cli.err(1, f"No quiz found matching: {args.assignment}")
4376
4455
 
4456
+ if len(quiz_list) > 1:
4457
+ canvaslms.cli.err(1, f"Multiple quizzes match '{args.assignment}'. "
4458
+ "Please use a more specific pattern.")
4459
+
4377
4460
  quiz = quiz_list[0]
4378
4461
  requester = canvas._Canvas__requester
4379
4462
  include_banks = args.include_banks and not args.no_banks
@@ -4385,6 +4468,9 @@ def export_command(config, canvas, args):
4385
4468
  else:
4386
4469
  export = export_full_classic_quiz(quiz, importable)
4387
4470
 
4471
+ # Preserve the user's selection expression for round-trip syncing.
4472
+ export.setdefault('settings', {})['regex'] = args.assignment
4473
+
4388
4474
  # Output as JSON
4389
4475
  print(json.dumps(export, indent=2, ensure_ascii=False))
4390
4476
  @
canvaslms/cli/quizzes.py CHANGED
@@ -2380,6 +2380,11 @@ def _parse_quiz_full_format(data, format_type):
2380
2380
 
2381
2381
  if "settings" in data:
2382
2382
  settings = data["settings"].copy()
2383
+ # Allow metadata (like id/regex) at the top level.
2384
+ # This makes the format more forgiving for hand-edited files.
2385
+ for k in ("id", "regex", "title"):
2386
+ if k in data and k not in settings:
2387
+ settings[k] = data[k]
2383
2388
  else:
2384
2389
  # Simple format: entire dict is settings (excluding metadata keys)
2385
2390
  settings = {
@@ -2402,7 +2407,6 @@ def _parse_quiz_full_format(data, format_type):
2402
2407
 
2403
2408
  def edit_command(config, canvas, args):
2404
2409
  """Edits quiz settings and instructions"""
2405
- quiz_list = process_quiz_option(canvas, args)
2406
2410
  requester = canvas._Canvas__requester
2407
2411
 
2408
2412
  if args.file:
@@ -2413,18 +2417,75 @@ def edit_command(config, canvas, args):
2413
2417
  except ValueError as e:
2414
2418
  canvaslms.cli.err(1, f"Invalid file format: {e}")
2415
2419
 
2416
- settings = quiz_data["settings"]
2417
- quiz_id = settings.get("id")
2420
+ settings = quiz_data.get("settings") or {}
2421
+ course_list = courses.process_course_option(canvas, args)
2422
+
2423
+ if getattr(args, "assignment", None):
2424
+ quiz_list = list(filter_quizzes(course_list, args.assignment))
2425
+ if not quiz_list:
2426
+ canvaslms.cli.err(1, f"No quiz found matching: {args.assignment}")
2427
+ else:
2428
+ quiz_id = settings.get("id")
2429
+ quiz_regex = settings.get("regex")
2430
+ quiz_title = settings.get("title")
2431
+
2432
+ if quiz_id is not None and str(quiz_id).strip() != "":
2433
+ quiz_list = []
2434
+ for course in course_list:
2435
+ for quiz in fetch_all_quizzes(course):
2436
+ if str(quiz.id) == str(quiz_id):
2437
+ quiz.course = course
2438
+ quiz_list.append(quiz)
2439
+ if not quiz_list:
2440
+ canvaslms.cli.err(1, f"Quiz with ID {quiz_id} not found")
2441
+ if len(quiz_list) > 1:
2442
+ canvaslms.cli.err(1, f"Multiple quizzes match ID {quiz_id}")
2443
+ elif quiz_regex is not None and str(quiz_regex).strip() != "":
2444
+ quiz_list = list(filter_quizzes(course_list, str(quiz_regex)))
2445
+ if not quiz_list:
2446
+ canvaslms.cli.err(
2447
+ 1, f"No quiz found matching file regex: {quiz_regex}"
2448
+ )
2449
+ elif quiz_title is not None and str(quiz_title).strip() != "":
2450
+ quiz_list = []
2451
+ title = str(quiz_title)
2452
+ for course in course_list:
2453
+ matches = [
2454
+ q
2455
+ for q in fetch_all_quizzes(course)
2456
+ if getattr(q, "title", None) == title
2457
+ ]
2458
+ for q in matches:
2459
+ q.course = course
2460
+ if len(matches) > 1:
2461
+ canvaslms.cli.err(
2462
+ 1,
2463
+ f"Multiple quizzes titled '{title}' in course {course.course_code}. "
2464
+ "Use settings.regex (or -a) to disambiguate.",
2465
+ )
2466
+ if matches:
2467
+ quiz_list.extend(matches)
2468
+
2469
+ if not quiz_list:
2470
+ canvaslms.cli.err(1, f"No quiz found with title: {title}")
2471
+ else:
2472
+ canvaslms.cli.err(
2473
+ 1,
2474
+ "File mode without -a requires one of settings.id, settings.regex, "
2475
+ "or settings.title",
2476
+ )
2418
2477
 
2419
- if quiz_id:
2420
- target_quiz = None
2478
+ if len(quiz_list) > 1:
2479
+ print(f"Found {len(quiz_list)} quizzes to update:")
2421
2480
  for quiz in quiz_list:
2422
- if str(quiz.id) == str(quiz_id):
2423
- target_quiz = quiz
2424
- break
2425
- if not target_quiz:
2426
- canvaslms.cli.err(1, f"Quiz with ID {quiz_id} not found")
2427
- quiz_list = [target_quiz]
2481
+ course_code = (
2482
+ quiz.course.course_code if hasattr(quiz, "course") else "?"
2483
+ )
2484
+ print(f" - {course_code}: {quiz.title} (ID: {quiz.id})")
2485
+ response = input("\nEdit all? [y/N] ").strip().lower()
2486
+ if response != "y":
2487
+ print("Aborted.")
2488
+ return
2428
2489
  items = quiz_data.get("items")
2429
2490
  if args.replace_items and items:
2430
2491
  for quiz in quiz_list:
@@ -2455,6 +2516,9 @@ def edit_command(config, canvas, args):
2455
2516
  else:
2456
2517
  canvaslms.cli.warn(f"Failed to update quiz: {quiz.title}")
2457
2518
  else:
2519
+ if not getattr(args, "assignment", None):
2520
+ canvaslms.cli.err(1, "-a/--assignment is required unless -f/--file is used")
2521
+ quiz_list = process_quiz_option(canvas, args)
2458
2522
  # Confirm if multiple quizzes match
2459
2523
  if len(quiz_list) > 1:
2460
2524
  print(f"Found {len(quiz_list)} quizzes matching the pattern:")
@@ -3008,7 +3072,7 @@ def quiz_attributes_to_api_params(attributes, is_new, html_body):
3008
3072
  params = {}
3009
3073
 
3010
3074
  for key, value in attributes.items():
3011
- if key == "id":
3075
+ if key in ("id", "regex", "modules"):
3012
3076
  # Don't send ID as an update parameter
3013
3077
  continue
3014
3078
 
@@ -3189,6 +3253,13 @@ def export_command(config, canvas, args):
3189
3253
  if not quiz_list:
3190
3254
  canvaslms.cli.err(1, f"No quiz found matching: {args.assignment}")
3191
3255
 
3256
+ if len(quiz_list) > 1:
3257
+ canvaslms.cli.err(
3258
+ 1,
3259
+ f"Multiple quizzes match '{args.assignment}'. "
3260
+ "Please use a more specific pattern.",
3261
+ )
3262
+
3192
3263
  quiz = quiz_list[0]
3193
3264
  requester = canvas._Canvas__requester
3194
3265
  include_banks = args.include_banks and not args.no_banks
@@ -3200,6 +3271,9 @@ def export_command(config, canvas, args):
3200
3271
  else:
3201
3272
  export = export_full_classic_quiz(quiz, importable)
3202
3273
 
3274
+ # Preserve the user's selection expression for round-trip syncing.
3275
+ export.setdefault("settings", {})["regex"] = args.assignment
3276
+
3203
3277
  # Output as JSON
3204
3278
  print(json.dumps(export, indent=2, ensure_ascii=False))
3205
3279
 
@@ -5423,6 +5497,13 @@ def add_edit_command(subp):
5423
5497
  The JSON/YAML format is the same as 'quizzes export' output, enabling
5424
5498
  a round-trip workflow: export, modify, edit.
5425
5499
 
5500
+ When using -f, the -a/--assignment selector is optional if the file provides
5501
+ enough information to identify the quiz. We use the first available of:
5502
+ - settings.id (exact ID match)
5503
+ - settings.regex (regex matching title or ID)
5504
+ - settings.title (exact title match; errors if duplicate titles exist
5505
+ within a course)
5506
+
5426
5507
  ITEM HANDLING:
5427
5508
  By default, items/questions in the file are ignored to protect student
5428
5509
  submissions. Use --replace-items to replace all questions (with confirmation
@@ -5438,7 +5519,16 @@ def add_edit_command(subp):
5438
5519
  except argparse.ArgumentError:
5439
5520
  pass
5440
5521
 
5441
- add_quiz_option(edit_parser, required=True)
5522
+ # NOTE: We intentionally do not call add_quiz_option(required=False) here.
5523
+ # That helper defaults to '.*', which would select all quizzes if -a is
5524
+ # omitted. For quizzes edit, -a is required only in interactive mode.
5525
+ edit_parser.add_argument(
5526
+ "-a",
5527
+ "--assignment",
5528
+ required=False,
5529
+ default=None,
5530
+ help="Regex matching quiz title or Canvas ID",
5531
+ )
5442
5532
 
5443
5533
  edit_parser.add_argument(
5444
5534
  "-f",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: canvaslms
3
- Version: 5.10
3
+ Version: 6.1
4
4
  Summary: Command-line interface to Canvas LMS
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,17 +1,17 @@
1
1
  canvaslms/Makefile,sha256=9zE09HzyU-S2B1RI72UV1S9mmqXKiE9gIn-NuhyQP08,119
2
2
  canvaslms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  canvaslms/cli/Makefile,sha256=8uQf6xKYRr_4mrYA-FnYYh7xAq7CcBvF3rbqDTNLUyY,820
4
- canvaslms/cli/__init__.py,sha256=AI7_1x4LfsQxpBX10_MMNn6Mlraw02Q2zcrk-erVK-U,6759
5
- canvaslms/cli/assignments.nw,sha256=bgKytV48ya0cGJsuur6sIZ_V7ZvuNR1qF7qPvg1xf2U,54781
6
- canvaslms/cli/assignments.py,sha256=gphQuMgB0H_y-w4flidu9QRPREheBtuLF8zg70-9gZs,38754
4
+ canvaslms/cli/__init__.py,sha256=uIL-Urx6Spw1qSEZDYw2yTA54cAt9UYEIti25csjTno,7050
5
+ canvaslms/cli/assignments.nw,sha256=gJj0_FEEMHkatLRtdZp7VrAD8Y0YPYH7v-LqnPzvuBc,59127
6
+ canvaslms/cli/assignments.py,sha256=y4_jWUFKFhcjkmxvBmVXJ3hF9gDMb8yLLJHvqKXc7YU,47393
7
7
  canvaslms/cli/cache.nw,sha256=TV9nUkwSUrFyltH98uUJ-3BLXT_-yNGsilaFlbByaxU,13350
8
8
  canvaslms/cli/cache.py,sha256=EBD2YLYa7Efq_RWsDgZOf44SaUemsyPWgeYVS-eW1uk,8217
9
9
  canvaslms/cli/calendar.nw,sha256=-dN8Sx9LWGBwhq2wc3w0GjQL3ywwPKfvi9gjsRJ6qVg,22887
10
10
  canvaslms/cli/calendar.py,sha256=f8af6McSxg5G2Etf6TAxwp1cnYodlcyw7SJgcJJLBks,20898
11
11
  canvaslms/cli/canvas_calendar.py,sha256=AbA6nOZ7e-zACfAqwK1utBjuShVGsaERxIJ4xRKEUZc,22196
12
- canvaslms/cli/cli.nw,sha256=JrPmqzm-0E4Jyk58U5XFGb7ArYGiu7SN6G7xRtTEerQ,24348
13
- canvaslms/cli/content.nw,sha256=hg_KBHAPXTGyZFMV2Y96oE3VUHaVxsY2hvJBQDDHvOA,36995
14
- canvaslms/cli/content.py,sha256=OQiyhtwNenOZ1V0665f7p9VE9P3Zz3AX6vIzz0VqpKo,17362
12
+ canvaslms/cli/cli.nw,sha256=S7cq2H-4LirgQIr8UlsOA2u7jDFurw5q1VY8xt3jjHc,24916
13
+ canvaslms/cli/content.nw,sha256=C0hIYuEfMHI4z2Q0FquWpga3Nrr3Auo8GCR3ptfht9M,46917
14
+ canvaslms/cli/content.py,sha256=KlwTrcC7ILZoTWXvSAjInoFguN31VqZrzSX12g32Cxs,20077
15
15
  canvaslms/cli/courses.nw,sha256=W8hNE8wQYPoSRDbqdU_qFvmOFWF6mRap0NLaz-edHH0,14569
16
16
  canvaslms/cli/courses.py,sha256=XKeV43xNT1or_qv40_IATJgey2zbVEg8qHqnz-Z7qUo,3743
17
17
  canvaslms/cli/discussions.nw,sha256=woTvzURp10kimseuYTZs2RTFpCKnpxCsCnDTFK_4jNI,46687
@@ -22,10 +22,10 @@ canvaslms/cli/login.nw,sha256=93LyHO_LXL1WdEvMg3OLhWulgkdoO8pfjYZVLwUbX4I,4419
22
22
  canvaslms/cli/login.py,sha256=wbA5Q9fTsW1J-vraRcdq2kG4h_LFtvH_MTEay6h8GcE,2737
23
23
  canvaslms/cli/modules.nw,sha256=rvrZe4AU_ok955GZ_ZwwFNFIHIKzft1LR9zTVLW_gic,24122
24
24
  canvaslms/cli/modules.py,sha256=8fZpW_x7eUMUgcn8JrvbGuwtrmHsbVdNvqfcRgfF4x8,12953
25
- canvaslms/cli/pages.nw,sha256=ZCv1NAwwXRzSy27ePJ3CI1ET1BBZJsM2scnoZxCW4n4,28959
26
- canvaslms/cli/pages.py,sha256=7E6gjEfs3L6A9_c84gRHvx7auDDV7MQWUXDwQ2-dkz4,27750
27
- canvaslms/cli/quizzes.nw,sha256=Jwne15UNJeumvNQZ9Su1nXBp7w54sI5K5MEZmZYWWh4,265048
28
- canvaslms/cli/quizzes.py,sha256=qLkLHpKpwbn4B_TpuaemQ6gwXQP1Gnho6zmWUMvX69k,199089
25
+ canvaslms/cli/pages.nw,sha256=_6f1X4kh1MqDZRxhtXSWPHx4i6GkpjvSKFqE8NA7Njk,31223
26
+ canvaslms/cli/pages.py,sha256=5FGb3iXhF3GgvA9DqA_kGTLEUaT4r3fUgjSo4Zvb4jg,28922
27
+ canvaslms/cli/quizzes.nw,sha256=0_21aFzNPjQBYXHrPQeWWXzWO638zlGfnWM09sLiScE,268816
28
+ canvaslms/cli/quizzes.py,sha256=gH4HnDvM2YBVIaleQ5iR1JFybM1iM3fDF5sZ-3QoJIo,203234
29
29
  canvaslms/cli/results.nw,sha256=6i2tfIOZ86WpEfx4OmJrk8aJyXl8gGh86bLX3pscFXA,28563
30
30
  canvaslms/cli/results.py,sha256=a-Xy5Z0OMCRwA08nX8RkCyCjipGkKJqtljjo7YIKFh0,17049
31
31
  canvaslms/cli/results.py.broken,sha256=njHu8mKfPHqH4daxy-4LMpO6FdUBLPHiVKFFmyH8aJQ,13047
@@ -59,8 +59,8 @@ canvaslms/hacks/attachment_cache.py,sha256=LcOZqaa6jPrEJWUD-JYN5GTc3bxCbv2fr_vqu
59
59
  canvaslms/hacks/canvasapi.nw,sha256=ixmIHn4tgy-ZKtQ1rqWSw97hfY2m0qtGX0de2x89lwA,136470
60
60
  canvaslms/hacks/canvasapi.py,sha256=A-r48x7gO6143_QkuZ8n6EW66i-a2AXXr7X7oeehOAU,31868
61
61
  canvaslms/hacks/test_hacks.py,sha256=JSJNvZqHu1E_s51HsPD7yr1gC-R-xVe-tuMMAKU9Gj8,66709
62
- canvaslms-5.10.dist-info/METADATA,sha256=Gob4TKBlMmIZ0rAP2bZ6HYYpkguKio11OJJTB7IaOVY,6079
63
- canvaslms-5.10.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
64
- canvaslms-5.10.dist-info/entry_points.txt,sha256=lyblfkLbodN5yb7q1c6-rwIoJPV-ygXrB9PYb5boHXM,48
65
- canvaslms-5.10.dist-info/licenses/LICENSE,sha256=N_TKsbzzD5Ax5fWJqEQk9bkwtf394MJkNeFld4HV6-E,1074
66
- canvaslms-5.10.dist-info/RECORD,,
62
+ canvaslms-6.1.dist-info/METADATA,sha256=Ae-_3M9DEoiXGx5dBZ2fPIxT2dZ4_NF2Udi-cg-THYQ,6078
63
+ canvaslms-6.1.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
64
+ canvaslms-6.1.dist-info/entry_points.txt,sha256=lyblfkLbodN5yb7q1c6-rwIoJPV-ygXrB9PYb5boHXM,48
65
+ canvaslms-6.1.dist-info/licenses/LICENSE,sha256=N_TKsbzzD5Ax5fWJqEQk9bkwtf394MJkNeFld4HV6-E,1074
66
+ canvaslms-6.1.dist-info/RECORD,,