canvaslms 5.7__tar.gz → 5.8__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 (113) hide show
  1. {canvaslms-5.7 → canvaslms-5.8}/PKG-INFO +1 -1
  2. {canvaslms-5.7 → canvaslms-5.8}/pyproject.toml +1 -1
  3. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/results.nw +128 -14
  4. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/Makefile +3 -0
  5. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/grades.nw +1 -0
  6. canvaslms-5.8/src/canvaslms/grades/participation.nw +218 -0
  7. {canvaslms-5.7 → canvaslms-5.8}/LICENSE +0 -0
  8. {canvaslms-5.7 → canvaslms-5.8}/README.md +0 -0
  9. {canvaslms-5.7 → canvaslms-5.8}/makefiles/.circleci/config.yml +0 -0
  10. {canvaslms-5.7 → canvaslms-5.8}/makefiles/.git +0 -0
  11. {canvaslms-5.7 → canvaslms-5.8}/makefiles/.gitignore +0 -0
  12. {canvaslms-5.7 → canvaslms-5.8}/makefiles/CONTRIBUTING.md +0 -0
  13. {canvaslms-5.7 → canvaslms-5.8}/makefiles/Dockerfile +0 -0
  14. {canvaslms-5.7 → canvaslms-5.8}/makefiles/Dockerfile.nw +0 -0
  15. {canvaslms-5.7 → canvaslms-5.8}/makefiles/Dockerfile.tex +0 -0
  16. {canvaslms-5.7 → canvaslms-5.8}/makefiles/LICENSE +0 -0
  17. {canvaslms-5.7 → canvaslms-5.8}/makefiles/Makefile +0 -0
  18. {canvaslms-5.7 → canvaslms-5.8}/makefiles/Makefile.nw +0 -0
  19. {canvaslms-5.7 → canvaslms-5.8}/makefiles/Makefile.tex +0 -0
  20. {canvaslms-5.7 → canvaslms-5.8}/makefiles/README.md +0 -0
  21. {canvaslms-5.7 → canvaslms-5.8}/makefiles/doc.mk +0 -0
  22. {canvaslms-5.7 → canvaslms-5.8}/makefiles/doc.mk.nw +0 -0
  23. {canvaslms-5.7 → canvaslms-5.8}/makefiles/doc.tex +0 -0
  24. {canvaslms-5.7 → canvaslms-5.8}/makefiles/exam.bib +0 -0
  25. {canvaslms-5.7 → canvaslms-5.8}/makefiles/exam.mk +0 -0
  26. {canvaslms-5.7 → canvaslms-5.8}/makefiles/exam.mk.nw +0 -0
  27. {canvaslms-5.7 → canvaslms-5.8}/makefiles/exam.tex +0 -0
  28. {canvaslms-5.7 → canvaslms-5.8}/makefiles/gitattributes +0 -0
  29. {canvaslms-5.7 → canvaslms-5.8}/makefiles/haskell.mk +0 -0
  30. {canvaslms-5.7 → canvaslms-5.8}/makefiles/haskell.mk.nw +0 -0
  31. {canvaslms-5.7 → canvaslms-5.8}/makefiles/haskell.tex +0 -0
  32. {canvaslms-5.7 → canvaslms-5.8}/makefiles/intro.tex +0 -0
  33. {canvaslms-5.7 → canvaslms-5.8}/makefiles/latexmkrc +0 -0
  34. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/_minted/AAA0D43723DCC5BE9DB71A96B52C1142.highlight.minted +0 -0
  35. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/_minted/E5724293DA12A769F97F0E91498CAEEF.highlight.minted +0 -0
  36. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/_minted/FFC039B25D180E99FC9FEBDB2D42EBAF.highlight.minted +0 -0
  37. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/_minted/_7945F0071C510291A619CE1658F673CB.index.minted +0 -0
  38. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/_minted/default.style.minted +0 -0
  39. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.aux +0 -0
  40. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.bbl +0 -0
  41. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.bcf +0 -0
  42. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.blg +0 -0
  43. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.fdb_latexmk +0 -0
  44. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.fls +0 -0
  45. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.log +0 -0
  46. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.out +0 -0
  47. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.pdf +0 -0
  48. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.run.xml +0 -0
  49. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.toc +0 -0
  50. {canvaslms-5.7 → canvaslms-5.8}/makefiles/ltxobj/makefiles.xdv +0 -0
  51. {canvaslms-5.7 → canvaslms-5.8}/makefiles/makefiles.bib +0 -0
  52. {canvaslms-5.7 → canvaslms-5.8}/makefiles/makefiles.tex +0 -0
  53. {canvaslms-5.7 → canvaslms-5.8}/makefiles/miun.course.mk +0 -0
  54. {canvaslms-5.7 → canvaslms-5.8}/makefiles/miun.depend.mk +0 -0
  55. {canvaslms-5.7 → canvaslms-5.8}/makefiles/miun.docs.mk +0 -0
  56. {canvaslms-5.7 → canvaslms-5.8}/makefiles/miun.port.mk +0 -0
  57. {canvaslms-5.7 → canvaslms-5.8}/makefiles/miun.pub.mk +0 -0
  58. {canvaslms-5.7 → canvaslms-5.8}/makefiles/noweb.mk +0 -0
  59. {canvaslms-5.7 → canvaslms-5.8}/makefiles/noweb.mk.nw +0 -0
  60. {canvaslms-5.7 → canvaslms-5.8}/makefiles/noweb.tex +0 -0
  61. {canvaslms-5.7 → canvaslms-5.8}/makefiles/pkg.mk +0 -0
  62. {canvaslms-5.7 → canvaslms-5.8}/makefiles/pkg.mk.nw +0 -0
  63. {canvaslms-5.7 → canvaslms-5.8}/makefiles/pkg.tex +0 -0
  64. {canvaslms-5.7 → canvaslms-5.8}/makefiles/portability.mk +0 -0
  65. {canvaslms-5.7 → canvaslms-5.8}/makefiles/portability.mk.nw +0 -0
  66. {canvaslms-5.7 → canvaslms-5.8}/makefiles/portability.tex +0 -0
  67. {canvaslms-5.7 → canvaslms-5.8}/makefiles/preamble.tex +0 -0
  68. {canvaslms-5.7 → canvaslms-5.8}/makefiles/pub.mk +0 -0
  69. {canvaslms-5.7 → canvaslms-5.8}/makefiles/pub.mk.nw +0 -0
  70. {canvaslms-5.7 → canvaslms-5.8}/makefiles/pub.tex +0 -0
  71. {canvaslms-5.7 → canvaslms-5.8}/makefiles/results.mk +0 -0
  72. {canvaslms-5.7 → canvaslms-5.8}/makefiles/results.mk.nw +0 -0
  73. {canvaslms-5.7 → canvaslms-5.8}/makefiles/results.tex +0 -0
  74. {canvaslms-5.7 → canvaslms-5.8}/makefiles/subdir.mk +0 -0
  75. {canvaslms-5.7 → canvaslms-5.8}/makefiles/subdir.mk.nw +0 -0
  76. {canvaslms-5.7 → canvaslms-5.8}/makefiles/subdir.tex +0 -0
  77. {canvaslms-5.7 → canvaslms-5.8}/makefiles/tex.bib +0 -0
  78. {canvaslms-5.7 → canvaslms-5.8}/makefiles/tex.mk +0 -0
  79. {canvaslms-5.7 → canvaslms-5.8}/makefiles/tex.mk.nw +0 -0
  80. {canvaslms-5.7 → canvaslms-5.8}/makefiles/tex.tex +0 -0
  81. {canvaslms-5.7 → canvaslms-5.8}/makefiles/transform.bib +0 -0
  82. {canvaslms-5.7 → canvaslms-5.8}/makefiles/transform.mk +0 -0
  83. {canvaslms-5.7 → canvaslms-5.8}/makefiles/transform.mk.nw +0 -0
  84. {canvaslms-5.7 → canvaslms-5.8}/makefiles/transform.tex +0 -0
  85. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/Makefile +0 -0
  86. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/__init__.py +0 -0
  87. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/Makefile +0 -0
  88. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/assignments.nw +0 -0
  89. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/cache.nw +0 -0
  90. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/calendar.nw +0 -0
  91. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/cli.nw +0 -0
  92. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/content.nw +0 -0
  93. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/courses.nw +0 -0
  94. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/discussions.nw +0 -0
  95. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/grade.nw +0 -0
  96. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/login.nw +0 -0
  97. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/modules.nw +0 -0
  98. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/pages.nw +0 -0
  99. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/quizzes.nw +0 -0
  100. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/results.py.broken +0 -0
  101. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/submissions.nw +0 -0
  102. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/users.nw +0 -0
  103. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/cli/utils.nw +0 -0
  104. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/conjunctavg.nw +0 -0
  105. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/conjunctavgsurvey.nw +0 -0
  106. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/disjunctmax.nw +0 -0
  107. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/grades.py +0 -0
  108. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/maxgradesurvey.nw +0 -0
  109. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/grades/tilkryLAB1.nw +0 -0
  110. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/hacks/Makefile +0 -0
  111. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/hacks/__init__.py +0 -0
  112. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/hacks/attachment_cache.nw +0 -0
  113. {canvaslms-5.7 → canvaslms-5.8}/src/canvaslms/hacks/canvasapi.nw +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: canvaslms
3
- Version: 5.7
3
+ Version: 5.8
4
4
  Summary: Command-line interface to Canvas LMS
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "canvaslms"
3
- version = "5.7"
3
+ version = "5.8"
4
4
  description = "Command-line interface to Canvas LMS"
5
5
  authors = [
6
6
  {name = "Daniel Bosk", email = "daniel@bosk.se"}
@@ -39,6 +39,7 @@ import importlib.machinery
39
39
  import importlib.util
40
40
  import os
41
41
  import pathlib
42
+ import pkgutil
42
43
  import re
43
44
  import sys
44
45
 
@@ -55,13 +56,27 @@ The command requires two arguments: course and assignment.
55
56
  We also want the option to filter on users.
56
57
  We can add these by using [[add_assignment_option]], however, we don't need the
57
58
  ungraded flag as we want to export results (\ie graded material).
58
- Also, we can just add the [[add_user_or_group_option]] to be able to filter on
59
+ Also, we can just add the [[add_user_or_group_option]] to be able to filter on
59
60
  users or groups.
61
+
62
+ The epilog contains a formatted list of available grading modules.
63
+ We use [[RawDescriptionHelpFormatter]] to preserve the whitespace formatting in
64
+ the epilog while still allowing argument help text to wrap normally.
65
+
66
+ However, we must be careful about how we construct the epilog string.
67
+ When [[black]] formats the generated Python, triple-quoted strings passed as
68
+ arguments get indented on continuation lines.
69
+ Since [[RawDescriptionHelpFormatter]] preserves this whitespace literally, we'd
70
+ end up with oddly indented prose text in the help output.
71
+
72
+ To avoid this, we build the epilog via the [[format_results_epilog]] function,
73
+ which joins individual lines without introducing unwanted indentation.
60
74
  <<add results command to subp>>=
61
75
  results_parser = subp.add_parser("results",
62
76
  help="Lists results of a course",
63
77
  description="""<<results command description>>""",
64
- epilog="""<<results command epilog>>""")
78
+ epilog=format_results_epilog(),
79
+ formatter_class=argparse.RawDescriptionHelpFormatter)
65
80
  results_parser.set_defaults(func=results_command)
66
81
  assignments.add_assignment_option(results_parser, ungraded=False)
67
82
  users.add_user_or_group_option(results_parser)
@@ -83,13 +98,10 @@ prevent the student from getting a grade. Output format, CSV:
83
98
  <course code> <component code> <student ID> <missing assignment> <reason>
84
99
 
85
100
  The reason can be "not submitted" or "not graded".
86
- <<results command epilog>>=
87
- If you specify an assignment group, the results of the assignments in that
88
- group will be summarized. You can supply your own function for summarizing
89
- grades through the -S option. See `pydoc3 canvaslms.grades` for different
90
- options.
91
- @ We will cover the option for and loading of the custom summary module later,
101
+ @ We will cover the option for and loading of the custom summary module later,
92
102
  in \cref{custom-summary-modules}.
103
+ We will also cover the [[format_grading_modules_help]] function, which
104
+ dynamically discovers available modules in the [[canvaslms.grades]] package.
93
105
 
94
106
  Now, that [[results_command]] function must take three arguments: [[config]],
95
107
  [[canvas]] and [[args]].
@@ -395,18 +407,120 @@ if grade is None or grade_date is None \
395
407
  continue
396
408
  @
397
409
 
410
+ \subsection{Discovering available grading modules}
411
+ \label{grading-module-discovery}
412
+
413
+ When users install [[canvaslms]] via pipx, the package resides in an isolated
414
+ virtual environment.
415
+ This means the traditional approach of using [[pydoc3 canvaslms.grades]] to
416
+ discover available modules no longer works---pydoc searches the system Python
417
+ path, not pipx's isolated environment.
418
+
419
+ To solve this, we dynamically discover available modules at runtime using
420
+ [[pkgutil.iter_modules]].
421
+ This works regardless of installation method because we introspect from within
422
+ the package itself.
423
+ If a module fails to import (for example, due to missing dependencies), we
424
+ simply skip it rather than failing the entire discovery process.
425
+
426
+ We provide two helper functions: one to list the modules with their
427
+ descriptions, and one to format them for display in help text.
428
+ <<functions>>=
429
+ def list_grading_modules():
430
+ """
431
+ Discover available grading modules in canvaslms.grades package.
432
+ Returns a list of (module_name, description) tuples.
433
+ """
434
+ import canvaslms.grades
435
+
436
+ modules = []
437
+ # These are internal modules, not user-facing grading strategies
438
+ excluded = {"__init__", "grades"}
439
+
440
+ for importer, modname, ispkg in pkgutil.iter_modules(canvaslms.grades.__path__):
441
+ if modname in excluded:
442
+ continue
443
+ try:
444
+ module = importlib.import_module(f"canvaslms.grades.{modname}")
445
+ doc = module.__doc__
446
+ if doc:
447
+ # Take only the first line of the docstring
448
+ first_line = doc.strip().split('\n')[0]
449
+ else:
450
+ first_line = "(no description)"
451
+ modules.append((modname, first_line))
452
+ except ImportError:
453
+ continue
454
+
455
+ return sorted(modules)
456
+
457
+ def format_grading_modules_help():
458
+ """Format the list of grading modules for help text display."""
459
+ modules = list_grading_modules()
460
+ if not modules:
461
+ return " (no modules found)"
462
+
463
+ lines = []
464
+ for name, desc in modules:
465
+ lines.append(f" canvaslms.grades.{name}: {desc}")
466
+ return "\n\n".join(lines)
467
+
468
+ def format_results_epilog():
469
+ """
470
+ Build the epilog for results command help.
471
+
472
+ We construct the epilog by joining lines explicitly rather than using a
473
+ triple-quoted string. This avoids indentation issues when black formats
474
+ the generated Python code---triple-quoted strings in function arguments
475
+ get continuation-line indentation that RawDescriptionHelpFormatter
476
+ would preserve literally.
477
+ """
478
+ parts = [
479
+ "If you specify an assignment group, the results of the assignments in that",
480
+ "group will be summarized. You can supply your own function for summarizing",
481
+ "grades through the -S option.",
482
+ "",
483
+ "Available grading modules:",
484
+ format_grading_modules_help(),
485
+ "",
486
+ "You can also provide a path to your own Python file with a summarize_group",
487
+ "function."
488
+ ]
489
+ return "\n".join(parts)
490
+ @
491
+
492
+ The [[list_grading_modules]] function uses [[pkgutil.iter_modules]] to iterate
493
+ over all submodules in the [[canvaslms.grades]] package.
494
+ We exclude [[__init__]] (the package initializer) and [[grades]] (an internal
495
+ module) since these are not user-facing grading strategies.
496
+
497
+ For each module, we import it and extract the first line of its docstring as a
498
+ brief description.
499
+ This gives users immediate visibility into what each module does, right in the
500
+ help output.
501
+
502
+ The [[format_grading_modules_help]] function joins module entries with blank
503
+ lines between them (double newline) for visual separation in the help output.
504
+ This makes the list easier to scan when module descriptions vary in length.
505
+
506
+ The [[format_results_epilog]] function builds the complete epilog by joining
507
+ individual lines.
508
+ This approach avoids the pitfall where [[black]] indents continuation lines in
509
+ triple-quoted strings, which [[RawDescriptionHelpFormatter]] would preserve
510
+ literally, resulting in oddly indented prose in the help output.
511
+
512
+
398
513
  \subsection{Loading a custom summary module}
399
514
  \label{custom-summary-modules}
400
515
 
401
- Different teachers have different policies for merging several assignments into
516
+ Different teachers have different policies for merging several assignments into
402
517
  one grade.
403
518
  We now want to provide a way to override the default function.
404
519
  <<summary module option doc>>=
405
- Name of Python module or file containing module to load with a custom
406
- summarization function to summarize assignment groups. The default module is
407
- part of the `canvaslms` package: `{default_summary_module}`. But it could be
408
- any Python file in the file system or other built-in modules. See `pydoc3
409
- canvaslms.grades` for alternative modules or how to build your own.
520
+ Name of Python module or file to load with a custom summarization function.
521
+ Default: `{default_summary_module}`. Available modules: """ + \
522
+ ", ".join(m[0] for m in list_grading_modules()) + """. \
523
+ Or provide a path to your own Python file.
410
524
  <<add option for custom summary module>>=
411
525
  default_summary_module = "canvaslms.grades.conjunctavg"
412
526
  results_parser.add_argument("-S", "--summary-module",
@@ -6,6 +6,7 @@ MODULES+= disjunctmax.py
6
6
  MODULES+= maxgradesurvey.py
7
7
  MODULES+= conjunctavgsurvey.py
8
8
  MODULES+= tilkryLAB1.py
9
+ MODULES+= participation.py
9
10
 
10
11
  .PHONY: all
11
12
  all: grades.tex
@@ -14,6 +15,7 @@ all: disjunctmax.tex
14
15
  all: maxgradesurvey.tex
15
16
  all: conjunctavgsurvey.tex
16
17
  all: tilkryLAB1.tex
18
+ all: participation.tex
17
19
  all: ${MODULES}
18
20
 
19
21
  grades.tex: conjunctavg.tex
@@ -21,6 +23,7 @@ grades.tex: disjunctmax.tex
21
23
  grades.tex: maxgradesurvey.tex
22
24
  grades.tex: conjunctavgsurvey.tex
23
25
  grades.tex: tilkryLAB1.tex
26
+ grades.tex: participation.tex
24
27
 
25
28
  __init__.py: init.py
26
29
  ${MV} $^ $@
@@ -135,3 +135,4 @@ We can also give the relative or absolute path to \texttt{mysum.py} instead.
135
135
  \input{../src/canvaslms/grades/disjunctmax.tex}
136
136
  \input{../src/canvaslms/grades/maxgradesurvey.tex}
137
137
  \input{../src/canvaslms/grades/tilkryLAB1.tex}
138
+ \input{../src/canvaslms/grades/participation.tex}
@@ -0,0 +1,218 @@
1
+ \section{Conjunctive P/F grading, the \texttt{participation} module}
2
+
3
+ This module implements conjunctive P/F grading for participation-based
4
+ assignments.
5
+ It's useful when students must complete \emph{all} assignments in a group to
6
+ pass, where each assignment is graded as complete/incomplete or 100/0.
7
+
8
+ As an example, consider a course where students pass by participating in
9
+ seminars and completing reflections:
10
+ \begin{minted}{text}
11
+ $ canvaslms assignments list -c vetcyb25h -A "^Participation INL1" \
12
+ | cut -f 1-3
13
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on literature reviews
14
+ DA2215 HT25 (vetcyb25h) Participation INL1 Overview of Science in Security
15
+ DA2215 HT25 (vetcyb25h) Participation INL1 How to Design Computer Security Experiments
16
+ DA2215 HT25 (vetcyb25h) Participation INL1 How do you know it's secure? Passwords
17
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension, literature review: Of passwords and people
18
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Graphical Passwords: Learning from the First Twelve Years
19
+ DA2215 HT25 (vetcyb25h) Participation INL1 Achieving Rigor in Literature Reviews Insights from Qualitative Data Analysis and Tool-Support
20
+ DA2215 HT25 (vetcyb25h) Participation INL1 Live seminar 13/11 at 13:15
21
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Of Passwords and People, Measuring the Effect of Password-Composition Policies
22
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Can long passwords be secure and usable?
23
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Why phishing works
24
+ DA2215 HT25 (vetcyb25h) Participation INL1 Live seminar 6/11 at 15:15
25
+ DA2215 HT25 (vetcyb25h) Participation INL1 The RSA and ElGamal cryptosystems
26
+ DA2215 HT25 (vetcyb25h) Participation INL1 On the Security of EIGamal Based Encryption
27
+ DA2215 HT25 (vetcyb25h) Participation INL1 Stealing Keys from PCs using a Radio: Cheap Electromagnetic Attacks on Windowed Exponentiation
28
+ DA2215 HT25 (vetcyb25h) Participation INL1 Timing Analysis of Keystrokes and Timing Attacks on SSH
29
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on the use of models, part I
30
+ DA2215 HT25 (vetcyb25h) Participation INL1 Theorem proving: 1. Introduction
31
+ DA2215 HT25 (vetcyb25h) Participation INL1 Theorem proving: 2. Formal methods and Interactive Theorem Proving
32
+ DA2215 HT25 (vetcyb25h) Participation INL1 Theorem proving: 4. Examples for what can be verified with Interactive Theorem Provers
33
+ DA2215 HT25 (vetcyb25h) Participation INL1 Theorem proving: 5. Limitations of Interactive Theorem Proving and Conclusion
34
+ DA2215 HT25 (vetcyb25h) Participation INL1 Model checking: algorithmic verification and debugging
35
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on the use of models, part II
36
+ DA2215 HT25 (vetcyb25h) Participation INL1 Live seminar 28/11 at 10:15
37
+ DA2215 HT25 (vetcyb25h) Participation INL1 Dos and Don'ts of Machine Learning in Computer Security
38
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on the use of statistics
39
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Why Johnny can't encrypt
40
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Comparing the Usability of Cryptographic APIs
41
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on qualitative methods
42
+ DA2215 HT25 (vetcyb25h) Participation INL1 SoK: Science, Security and the Elusive Goal of Security as a Scientific Pursuit
43
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on Science in Security
44
+ DA2215 HT25 (vetcyb25h) Participation INL1 Live seminar 12/12 at 12:15
45
+ DA2215 HT25 (vetcyb25h) Participation INL1 Tor: The Second-Generation Onion Router
46
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Users get routed: traffic correlation on tor by realistic adversaries
47
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Shadow: Running Tor in a Box for Accurate and Efficient Experimentation
48
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Website Fingerprinting with Website Oracles
49
+ DA2215 HT25 (vetcyb25h) Participation INL1 Comprehension: Online Website Fingerprinting: Evaluating Website Fingerprinting Attacks on Tor in the Real World
50
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on the use of models, part III
51
+ DA2215 HT25 (vetcyb25h) Participation INL1 Live seminar 3/12 at 12:15
52
+ DA2215 HT25 (vetcyb25h) Participation INL1 Reflection on inferences, experiments and measurements
53
+ \end{minted}
54
+ The exact assignments and names might change though.
55
+ But that's fine.
56
+ The grading system remains the same, we have the following grades:
57
+ \begin{minted}{text}
58
+ $ canvaslms submissions list -c vetcyb25h -A "^Participation INL1" \
59
+ | cut -f 4 | sort -u
60
+
61
+ 100
62
+ complete
63
+ incomplete
64
+ \end{minted}
65
+ We want all assignments to have either complete or 100 to get a passing grade.
66
+ Otherwise the student gets an F.
67
+
68
+ \subsection{Grading logic: conjunctive P/F}
69
+
70
+ This module implements what we call \emph{conjunctive P/F grading}.
71
+ Let's contrast this with other approaches to understand what makes it unique:
72
+ \begin{description}
73
+ \item[conjunctavg] All assignments must pass, and A--E grades are averaged.
74
+ Here we have no A--E grades, only complete/incomplete or 100/empty.
75
+ \item[disjunctmax] At least one assignment must pass (disjunctive), and we take
76
+ the best grade.
77
+ Here \emph{all} assignments must pass (conjunctive), and the only possible
78
+ grades are P or F.
79
+ \end{description}
80
+
81
+ The grading rule is simple: if \emph{all} participation assignments have a
82
+ passing grade, the student passes.
83
+ If \emph{any} assignment is missing or has an incomplete grade, the student
84
+ fails.
85
+
86
+ \subsection{Module structure}
87
+
88
+ We follow the standard module structure: a [[summarize_group]] function that
89
+ iterates over users and delegates to a [[summarize]] helper function.
90
+ <<[[participation.py]]>>=
91
+ """
92
+ <<module doc>>
93
+ """
94
+ import datetime as dt
95
+ from canvaslms.cli import results
96
+ from canvasapi.exceptions import ResourceDoesNotExist
97
+
98
+ <<helper functions>>
99
+
100
+ def summarize_group(assignments_list, users_list):
101
+ """
102
+ Summarizes participation assignments using conjunctive P/F grading.
103
+ All assignments must have 'complete' or '100' for P, otherwise F.
104
+ """
105
+ for user in users_list:
106
+ grade, grade_date, graders = summarize(user, assignments_list)
107
+ yield [user, grade, grade_date, *graders]
108
+ @
109
+
110
+ <<module doc>>=
111
+ Summarizes participation assignments using conjunctive P/F grading.
112
+ All assignments must have 'complete' or '100' for P, otherwise F.
113
+ @
114
+
115
+ \subsection{What counts as passing?}
116
+
117
+ A grade is considered passing if it is either:
118
+ \begin{itemize}
119
+ \item [[complete]] (case-insensitive, so [[Complete]] or [[COMPLETE]] also work)
120
+ \item [[100]] (a numeric grade indicating full completion)
121
+ \end{itemize}
122
+
123
+ Anything else---including [[incomplete]], [[None]], or a missing
124
+ submission---counts as not passing.
125
+ <<helper functions>>=
126
+ def is_passing_grade(grade):
127
+ """
128
+ Returns True if the grade indicates passing (complete or 100).
129
+ """
130
+ if grade is None:
131
+ return False
132
+ if isinstance(grade, str):
133
+ if grade.casefold() == "complete":
134
+ return True
135
+ if grade == "100":
136
+ return True
137
+ return False
138
+ @
139
+
140
+ \subsection{Summarizing a student's participation}
141
+
142
+ The [[summarize]] function iterates through all assignments and checks whether
143
+ each one has a passing grade.
144
+ We collect dates and graders along the way.
145
+ <<helper functions>>=
146
+ def summarize(user, assignments_list):
147
+ """
148
+ Extracts user's submissions for all participation assignments.
149
+ Returns (grade, date, graders) where grade is P if all passed, F otherwise.
150
+ """
151
+ passed = []
152
+ dates = []
153
+ graders = []
154
+
155
+ for assignment in assignments_list:
156
+ <<get submission for assignment>>
157
+ <<check if grade is passing>>
158
+ <<add graders to [[graders]] list>>
159
+ <<add date to [[dates]] list>>
160
+
161
+ <<determine final grade>>
162
+
163
+ return (final_grade, final_date, graders)
164
+ @
165
+
166
+ For each assignment, we attempt to fetch the student's submission.
167
+ If the submission doesn't exist (which can happen in rare cases), we treat it
168
+ as not passing.
169
+ <<get submission for assignment>>=
170
+ try:
171
+ submission = assignment.get_submission(user,
172
+ include=["submission_history"])
173
+ except ResourceDoesNotExist:
174
+ passed.append(False)
175
+ continue
176
+
177
+ submission.assignment = assignment
178
+ @
179
+
180
+ We extract the grade from the submission and check whether it's passing.
181
+ <<check if grade is passing>>=
182
+ grade = submission.grade
183
+ passed.append(is_passing_grade(grade))
184
+ @
185
+
186
+ We collect all graders from the submission history.
187
+ This ensures we credit everyone who participated in the grading process, not
188
+ just the last person to grade.
189
+ <<add graders to [[graders]] list>>=
190
+ graders += results.all_graders(submission)
191
+ @
192
+
193
+ For the date, we prefer the submission date but fall back to the grading date
194
+ if no submission was made (for example, oral presentations where the student
195
+ didn't submit anything).
196
+ <<add date to [[dates]] list>>=
197
+ grade_date = submission.submitted_at or submission.graded_at
198
+ if grade_date:
199
+ grade_date = dt.date.fromisoformat(grade_date.split("T")[0])
200
+ dates.append(grade_date)
201
+ @
202
+
203
+ \subsection{Determining the final grade}
204
+
205
+ The final grade is P if \emph{all} assignments passed, F otherwise.
206
+ If there are no dates (meaning the student has no activity at all), we return
207
+ [[None]] for both grade and date.
208
+ <<determine final grade>>=
209
+ if dates:
210
+ final_date = max(dates)
211
+ if all(passed):
212
+ final_grade = "P"
213
+ else:
214
+ final_grade = "F"
215
+ else:
216
+ final_date = None
217
+ final_grade = None
218
+ @
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes