otterden-lms-interface 0.7.3__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.
- otterden_lms_interface-0.7.3/.githooks/pre-commit +11 -0
- otterden_lms_interface-0.7.3/.github/pull_request_template.md +34 -0
- otterden_lms_interface-0.7.3/.github/workflows/release.yml +81 -0
- otterden_lms_interface-0.7.3/.gitignore +22 -0
- otterden_lms_interface-0.7.3/AGENTS.md +37 -0
- otterden_lms_interface-0.7.3/CODEOWNERS +1 -0
- otterden_lms_interface-0.7.3/PKG-INFO +17 -0
- otterden_lms_interface-0.7.3/README.md +286 -0
- otterden_lms_interface-0.7.3/course_plans/cst334_compact.yaml +458 -0
- otterden_lms_interface-0.7.3/course_plans/cst334_from_sheet.yaml +646 -0
- otterden_lms_interface-0.7.3/course_plans/cst463.yaml +322 -0
- otterden_lms_interface-0.7.3/example_files/peer-review-questions.yaml +95 -0
- otterden_lms_interface-0.7.3/example_files/report.pdf +0 -0
- otterden_lms_interface-0.7.3/lms_interface/__init__.py +13 -0
- otterden_lms_interface-0.7.3/lms_interface/backends.py +95 -0
- otterden_lms_interface-0.7.3/lms_interface/canvas_interface.py +1307 -0
- otterden_lms_interface-0.7.3/lms_interface/classes.py +369 -0
- otterden_lms_interface-0.7.3/lms_interface/cleanup_missing_ui.py +326 -0
- otterden_lms_interface-0.7.3/lms_interface/course_plan.py +3227 -0
- otterden_lms_interface-0.7.3/lms_interface/helpers.py +849 -0
- otterden_lms_interface-0.7.3/lms_interface/interfaces.py +43 -0
- otterden_lms_interface-0.7.3/lms_interface/peer_review.py +489 -0
- otterden_lms_interface-0.7.3/lms_interface/privacy.py +282 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/__init__.py +1 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/test_canvas_assignment_submissions.py +72 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/test_canvas_interface.py +766 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/test_course_plan.py +672 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/test_helpers.py +421 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/test_manual_grade.py +78 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/test_peer_review.py +274 -0
- otterden_lms_interface-0.7.3/lms_interface/tests/test_privacy.py +102 -0
- otterden_lms_interface-0.7.3/pyproject.toml +54 -0
- otterden_lms_interface-0.7.3/schemas/course_plan.schema.yaml +404 -0
- otterden_lms_interface-0.7.3/scripts/git_bump.sh +153 -0
- otterden_lms_interface-0.7.3/scripts/install_git_hooks.sh +8 -0
- otterden_lms_interface-0.7.3/scripts/validate_course_plan.py +104 -0
- otterden_lms_interface-0.7.3/scripts/vendor_into_project.py +843 -0
- otterden_lms_interface-0.7.3/test.py +147 -0
- otterden_lms_interface-0.7.3/uv.lock +628 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# If pyproject version changes, ensure uv.lock is staged in the same commit.
|
|
5
|
+
if git diff --cached -- pyproject.toml | grep -Eq '^[+-][[:space:]]*version[[:space:]]*='; then
|
|
6
|
+
if ! git diff --cached --name-only | grep -qx 'uv.lock'; then
|
|
7
|
+
echo "ERROR: pyproject version changed but uv.lock is not staged."
|
|
8
|
+
echo "Run: uv lock && git add uv.lock"
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
fi
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Pull Request
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
<!-- Provide a brief description of what this PR accomplishes -->
|
|
5
|
+
|
|
6
|
+
## Changes Made
|
|
7
|
+
<!-- List the key changes made in this PR -->
|
|
8
|
+
-
|
|
9
|
+
|
|
10
|
+
## Type of Change
|
|
11
|
+
<!-- Mark the appropriate option with an [x] -->
|
|
12
|
+
- [ ] Bug fix (non-breaking change that fixes an issue)
|
|
13
|
+
- [ ] New feature (non-breaking change that adds functionality)
|
|
14
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
15
|
+
- [ ] Documentation update
|
|
16
|
+
- [ ] Refactoring (no functional changes)
|
|
17
|
+
|
|
18
|
+
## Testing Done
|
|
19
|
+
<!-- Describe the testing you've done to verify your changes -->
|
|
20
|
+
|
|
21
|
+
## Checklist
|
|
22
|
+
<!-- Mark the appropriate options with an [x] -->
|
|
23
|
+
- [ ] My code follows the style guidelines of this project
|
|
24
|
+
- [ ] I have performed a self-review of my own code
|
|
25
|
+
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
26
|
+
- [ ] I have updated the documentation accordingly
|
|
27
|
+
- [ ] My changes generate no new warnings
|
|
28
|
+
- [ ] Any dependent changes have been merged and published
|
|
29
|
+
|
|
30
|
+
## Screenshots (if applicable)
|
|
31
|
+
<!-- Add screenshots here if they help explain your changes -->
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
<!-- Any additional notes or context about the PR -->
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
name: Build And Publish Release Artifacts
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build Distributions
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Check out repository
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Set up uv
|
|
22
|
+
uses: astral-sh/setup-uv@v6
|
|
23
|
+
|
|
24
|
+
- name: Verify tag matches project version
|
|
25
|
+
run: |
|
|
26
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
27
|
+
project_version="$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml | head -n 1)"
|
|
28
|
+
if [[ "${tag_version}" != "${project_version}" ]]; then
|
|
29
|
+
echo "Tag version (${tag_version}) does not match pyproject version (${project_version})."
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
- name: Build wheel and sdist
|
|
34
|
+
run: uv build
|
|
35
|
+
|
|
36
|
+
- name: Upload distribution artifacts
|
|
37
|
+
uses: actions/upload-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: dist
|
|
40
|
+
path: dist/*
|
|
41
|
+
|
|
42
|
+
publish:
|
|
43
|
+
name: Publish to PyPI
|
|
44
|
+
needs: build
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment:
|
|
47
|
+
name: pypi
|
|
48
|
+
permissions:
|
|
49
|
+
id-token: write
|
|
50
|
+
steps:
|
|
51
|
+
- name: Set up uv
|
|
52
|
+
uses: astral-sh/setup-uv@v6
|
|
53
|
+
|
|
54
|
+
- name: Download distribution artifacts
|
|
55
|
+
uses: actions/download-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: dist
|
|
58
|
+
path: dist
|
|
59
|
+
|
|
60
|
+
- name: Publish to PyPI
|
|
61
|
+
run: uv publish
|
|
62
|
+
|
|
63
|
+
release:
|
|
64
|
+
name: Create GitHub Release
|
|
65
|
+
needs: publish
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
permissions:
|
|
68
|
+
contents: write
|
|
69
|
+
steps:
|
|
70
|
+
- name: Download distribution artifacts
|
|
71
|
+
uses: actions/download-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: dist
|
|
74
|
+
path: dist
|
|
75
|
+
|
|
76
|
+
- name: Publish release with artifacts
|
|
77
|
+
uses: softprops/action-gh-release@v2
|
|
78
|
+
with:
|
|
79
|
+
tag_name: ${{ github.ref_name }}
|
|
80
|
+
generate_release_notes: true
|
|
81
|
+
files: dist/*
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
.idea/
|
|
3
|
+
.DS_Store
|
|
4
|
+
*.zip
|
|
5
|
+
.env
|
|
6
|
+
.envrc
|
|
7
|
+
.venv/
|
|
8
|
+
*.pyc
|
|
9
|
+
*.pyo
|
|
10
|
+
*.pyd
|
|
11
|
+
__pycache__/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.mypy_cache/
|
|
14
|
+
CLAUDE.md
|
|
15
|
+
*.so
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.mypy_cache/
|
|
20
|
+
dist/
|
|
21
|
+
build/
|
|
22
|
+
*.egg-info/
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Project Structure & Module Organization
|
|
4
|
+
`lms_interface/` contains the library code. Key modules include `canvas_interface.py` (Canvas API integration), `backends.py`/`interfaces.py` (backend abstraction), `privacy.py` (FERPA-friendly redaction), and `helpers.py` (CLI helpers such as `cleanup-missing`).
|
|
5
|
+
|
|
6
|
+
Tests live in `lms_interface/tests/` and mirror module behavior (`test_canvas_interface.py`, `test_helpers.py`, `test_privacy.py`). Keep new tests in this directory so vendoring stays clean.
|
|
7
|
+
|
|
8
|
+
Repository utilities live in `scripts/`:
|
|
9
|
+
- `install_git_hooks.sh` installs local hooks and `git bump`.
|
|
10
|
+
- `git_bump.sh` bumps version, runs tests, updates `uv.lock`, and commits.
|
|
11
|
+
- `vendor_into_project.py` vendors this package into downstream tools.
|
|
12
|
+
|
|
13
|
+
## Build, Test, and Development Commands
|
|
14
|
+
- `uv sync --dev`: install runtime and dev dependencies from `pyproject.toml`/`uv.lock`.
|
|
15
|
+
- `uv run pytest -q`: run the full test suite (default `testpaths` is `lms_interface/tests`).
|
|
16
|
+
- `uv run ruff check lms_interface lms_interface/tests`: run lint/import-order checks.
|
|
17
|
+
- `uv run black lms_interface lms_interface/tests`: format code (88-char lines).
|
|
18
|
+
- `bash scripts/install_git_hooks.sh`: enable repo hooks (`.githooks/pre-commit`).
|
|
19
|
+
- `uv lock`: regenerate lockfile after dependency or version updates.
|
|
20
|
+
|
|
21
|
+
## Coding Style & Naming Conventions
|
|
22
|
+
Use Python 3.12 and 4-space indentation. Follow Black formatting (`line-length = 88`) and keep imports Ruff-clean (`I` rules enabled). Use `snake_case` for functions/variables, `PascalCase` for classes, and `test_*.py` for test modules.
|
|
23
|
+
|
|
24
|
+
Prefer small, focused functions and explicit names around LMS concepts (`CanvasCourse`, `PrivacyBackend`, etc.).
|
|
25
|
+
|
|
26
|
+
## Testing Guidelines
|
|
27
|
+
Use `pytest` for all tests. Add regression tests for bug fixes and behavior changes, especially for Canvas request/response handling and privacy redaction paths.
|
|
28
|
+
|
|
29
|
+
Name tests clearly by behavior (for example, `test_cleanup_missing_skips_future_due_assignments`). Run `uv run pytest -q` before opening a PR.
|
|
30
|
+
|
|
31
|
+
## Commit & Pull Request Guidelines
|
|
32
|
+
Commit messages in this repo are short, imperative, and task-focused (for example, `Add LMS backend abstractions`, `Bump to version 0.4.4`).
|
|
33
|
+
|
|
34
|
+
PRs should follow `.github/pull_request_template.md`: include a clear description, change type, testing performed, and any relevant screenshots/notes. If `pyproject.toml` version changes, stage `uv.lock` in the same commit (enforced by pre-commit hook).
|
|
35
|
+
|
|
36
|
+
## Security & Configuration Tips
|
|
37
|
+
Store Canvas credentials in environment variables (`CANVAS_API_URL`, `CANVAS_API_KEY`, plus optional `_prod` variants). Never commit secrets or real student-identifying data.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @samogden
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: otterden-lms-interface
|
|
3
|
+
Version: 0.7.3
|
|
4
|
+
Summary: LMS interface library for teaching tools
|
|
5
|
+
Author-email: Sam Ogden <samuel.s.ogden@gmail.com>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: canvasapi<4,>=3.2
|
|
8
|
+
Requires-Dist: python-dotenv<2,>=1
|
|
9
|
+
Requires-Dist: pyyaml<7,>=6
|
|
10
|
+
Requires-Dist: requests<3,>=2.32.4
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: black; extra == 'dev'
|
|
13
|
+
Requires-Dist: flake8; extra == 'dev'
|
|
14
|
+
Requires-Dist: jsonschema; extra == 'dev'
|
|
15
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# LMSInterface
|
|
2
|
+
|
|
3
|
+
Lightweight LMS abstraction focused on Canvas. This repo is primarily used
|
|
4
|
+
as a vendored dependency in teaching tools.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
Set Canvas credentials in `~/.env` (or pass `env_path` to `CanvasInterface`):
|
|
9
|
+
|
|
10
|
+
- `CANVAS_API_URL`
|
|
11
|
+
- `CANVAS_API_KEY`
|
|
12
|
+
|
|
13
|
+
For production, use:
|
|
14
|
+
|
|
15
|
+
- `CANVAS_API_URL_prod`
|
|
16
|
+
- `CANVAS_API_KEY_prod`
|
|
17
|
+
|
|
18
|
+
## Tests
|
|
19
|
+
|
|
20
|
+
Run:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pytest
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Tests live in `lms_interface/tests/` so they vendor cleanly.
|
|
27
|
+
|
|
28
|
+
## Cleanup Missing Helper
|
|
29
|
+
|
|
30
|
+
Use `cleanup-missing` to normalize Canvas late-policy status for unsubmitted work:
|
|
31
|
+
|
|
32
|
+
- Past due: set to `missing`
|
|
33
|
+
- Future due: set to `none`
|
|
34
|
+
|
|
35
|
+
The helper can also clear stale placeholder grades (`Incomplete`/`0`) on future-due,
|
|
36
|
+
contentless placeholder submissions.
|
|
37
|
+
|
|
38
|
+
When run in an interactive terminal, the helper now shows a live progress view
|
|
39
|
+
with the current assignment and a running results table. Use `--plain-output` if
|
|
40
|
+
you want the older plain text behavior.
|
|
41
|
+
|
|
42
|
+
Run via module:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
python -m lms_interface.helpers cleanup-missing --course-id <COURSE_ID> --dry-run
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or via script entry point:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
lms-interface-helper cleanup-missing --course-id <COURSE_ID> --dry-run
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Useful flags:
|
|
55
|
+
|
|
56
|
+
- `--assignment-id <ID>`: scope to one assignment
|
|
57
|
+
- `--clear-placeholder-grade`: clear stale placeholder grades
|
|
58
|
+
- `--include-unpublished`: include unpublished assignments
|
|
59
|
+
- `--debug`: verbose per-student logs
|
|
60
|
+
|
|
61
|
+
Recommended rollout:
|
|
62
|
+
|
|
63
|
+
1. Run `--dry-run` on one assignment.
|
|
64
|
+
2. Run live on one assignment and verify in Canvas UI.
|
|
65
|
+
3. Run course-wide with `--dry-run`.
|
|
66
|
+
4. Run course-wide live.
|
|
67
|
+
|
|
68
|
+
## Manual Grade Test Harness
|
|
69
|
+
|
|
70
|
+
Use `test.py` for one-off grading against a real Canvas submission while you
|
|
71
|
+
are validating rubric behavior or feedback formatting.
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
python test.py grade \
|
|
77
|
+
--course-id <COURSE_ID> \
|
|
78
|
+
--assignment-id <ASSIGNMENT_ID> \
|
|
79
|
+
--student-id <STUDENT_ID> \
|
|
80
|
+
--score 8 \
|
|
81
|
+
--comments "Well done." \
|
|
82
|
+
--rubric-assessment '{"criterion 1": 5, "crit2": {"points": 3, "rating_id": "rat3"}}'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Useful notes:
|
|
86
|
+
|
|
87
|
+
- `--rubric-assessment` accepts either inline JSON/YAML or a path to a file
|
|
88
|
+
containing an object.
|
|
89
|
+
- Rubric keys should be the exact criterion names shown in Canvas, such as
|
|
90
|
+
`criterion 1` and `crit2` in the example above.
|
|
91
|
+
- If you omit `--score`, the Canvas wrapper derives the posted grade from the
|
|
92
|
+
rubric points total.
|
|
93
|
+
- This command posts feedback to an existing assignment. It does not create or
|
|
94
|
+
attach rubrics to assignments.
|
|
95
|
+
|
|
96
|
+
## Course Plan Helper
|
|
97
|
+
|
|
98
|
+
Generate a student-facing calendar (HTML + JSON) from a course plan YAML and
|
|
99
|
+
optionally publish it to Canvas as a page.
|
|
100
|
+
|
|
101
|
+
Dry-run / local generation only:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
lms-interface-helper plan-course --yaml-path course_plans/cst334_compact.yaml --output-dir build/cst334
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Publish to Canvas:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
lms-interface-helper plan-course --yaml-path course_plans/cst334_compact.yaml --course-id <COURSE_ID> --publish
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Useful flags:
|
|
114
|
+
|
|
115
|
+
- `--dry-run`: show Canvas actions without writing when `--publish` is used
|
|
116
|
+
- `--page-title`: override published page title
|
|
117
|
+
- `--module-name`: module to place schedule page link in (default: `Course Schedule`)
|
|
118
|
+
- `--publish-weekly-slides`: create/reuse `Week XX` modules and add slide URL links
|
|
119
|
+
- `--weekly-module-template`: override week module naming (default from plan: `Week {week_number}`)
|
|
120
|
+
|
|
121
|
+
Plan hint:
|
|
122
|
+
|
|
123
|
+
- Set `sync.topics_per_meeting: 2` (or higher) when a single class session covers multiple topic blocks.
|
|
124
|
+
- Set `duration_hours` on a topic when it needs extra in-class time (for example `duration_hours: 3`).
|
|
125
|
+
- Use reusable placeholders to insert spacer blocks without redefining `tbd-*` topics each term.
|
|
126
|
+
- Define `resource_defaults.lecture_slides_base_url` to avoid repeating full slide URLs in each topic.
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
resource_defaults:
|
|
132
|
+
lecture_slides_base_url: https://github.com/CSUMB-SCD-instructors/CST334/tree/main/slides/pdfs
|
|
133
|
+
|
|
134
|
+
topics:
|
|
135
|
+
- id: process-scheduling-os7
|
|
136
|
+
lecture_slides:
|
|
137
|
+
- OSTEP 07.pdf
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Reusable placeholder example:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
placeholders:
|
|
144
|
+
tbd:
|
|
145
|
+
title: Buffer / TBD
|
|
146
|
+
new_material: false
|
|
147
|
+
|
|
148
|
+
topics:
|
|
149
|
+
- id: mlfq
|
|
150
|
+
duration_hours: 3
|
|
151
|
+
- placeholder: tbd
|
|
152
|
+
duration_hours: 1
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Validate a plan file against the schema:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
python scripts/validate_course_plan.py course_plans/cst334_compact.yaml
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Parallel Uploads
|
|
162
|
+
|
|
163
|
+
Question uploads support multi-threading to speed up large quizzes. The default
|
|
164
|
+
behavior uses a small worker pool and limits in-flight requests.
|
|
165
|
+
|
|
166
|
+
Defaults:
|
|
167
|
+
|
|
168
|
+
- `max_workers = 4`
|
|
169
|
+
- `max_in_flight = 8`
|
|
170
|
+
|
|
171
|
+
If any worker receives a Canvas `429`, all workers respect a shared backoff
|
|
172
|
+
window before continuing.
|
|
173
|
+
|
|
174
|
+
You can override these via `CanvasCourse.create_question(...)` or by calling
|
|
175
|
+
the lower-level `_upload_question_payloads(...)`.
|
|
176
|
+
|
|
177
|
+
## Backend Abstraction (LTI-Ready)
|
|
178
|
+
|
|
179
|
+
New tools should depend on the LMS-agnostic interfaces and adapters instead
|
|
180
|
+
of Canvas directly. This makes it easier to add an LTI backend later.
|
|
181
|
+
|
|
182
|
+
Recommended entry points:
|
|
183
|
+
|
|
184
|
+
- `CanvasBackend` for the current Canvas API
|
|
185
|
+
- `PrivacyBackend` to enforce FERPA-friendly anonymization
|
|
186
|
+
- `CanvasInterface(privacy_mode="id_only")` for quick ID-only redaction
|
|
187
|
+
|
|
188
|
+
By default, student names are redacted. To request real names, pass
|
|
189
|
+
`include_names=True` to:
|
|
190
|
+
|
|
191
|
+
- `CanvasCourse.get_students(...)`
|
|
192
|
+
- `CanvasAssignment.get_submissions(...)`
|
|
193
|
+
- `CanvasQuiz.get_quiz_submissions(...)`
|
|
194
|
+
|
|
195
|
+
Example:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from lms_interface.backends import CanvasBackend
|
|
199
|
+
from lms_interface.privacy import PrivacyBackend
|
|
200
|
+
|
|
201
|
+
backend = CanvasBackend(prod=False)
|
|
202
|
+
backend = PrivacyBackend(backend, salt="my-course-salt", mode="pseudonymous")
|
|
203
|
+
|
|
204
|
+
course = backend.get_course(12345)
|
|
205
|
+
students = course.get_students()
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Privacy modes:
|
|
209
|
+
|
|
210
|
+
- `pseudonymous`: hashed IDs (requires `LMS_PRIVACY_SALT`)
|
|
211
|
+
- `id_only`: uses the real Canvas ID but redacts names (`Student <id>`)
|
|
212
|
+
|
|
213
|
+
## Vendoring
|
|
214
|
+
|
|
215
|
+
Use the shared script to vendor into another project:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python scripts/vendor_into_project.py /path/to/target --top-level
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## GitHub Release Artifacts
|
|
222
|
+
|
|
223
|
+
This repo can publish wheel/sdist artifacts to GitHub Releases on tag pushes.
|
|
224
|
+
|
|
225
|
+
Flow:
|
|
226
|
+
|
|
227
|
+
1. Bump version and publish commit/tag in one step (for example via `git bump patch`).
|
|
228
|
+
2. GitHub Actions builds `dist/*` and attaches artifacts to that release.
|
|
229
|
+
|
|
230
|
+
Default one-command option:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
git bump patch
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Optional local-only bump (no push/tag):
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
git bump patch --no-tag --no-push
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Tag/version contract:
|
|
243
|
+
|
|
244
|
+
- Tag `vX.Y.Z` must match `project.version = "X.Y.Z"` in `pyproject.toml`.
|
|
245
|
+
- The release workflow fails if they do not match.
|
|
246
|
+
|
|
247
|
+
Downstream projects can pin to a release artifact instead of vendoring:
|
|
248
|
+
|
|
249
|
+
```toml
|
|
250
|
+
dependencies = [
|
|
251
|
+
"lms-interface @ https://github.com/<org>/LMSInterface/releases/download/v0.4.5/lms_interface-0.4.5-py3-none-any.whl",
|
|
252
|
+
]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Peer-Review Surveys
|
|
256
|
+
|
|
257
|
+
Create one restricted Canvas graded survey per PDF, with a shared YAML rubric and
|
|
258
|
+
randomized student assignments:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
lms-interface-helper peer-review --course-id <COURSE_ID> \
|
|
262
|
+
--yaml-path example_files/peer-review-questions.yaml --dry-run
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Remove `--dry-run` to create the surveys. The beta deliberately creates a new
|
|
266
|
+
batch on every live run. The YAML requires `unlock_at`, `due_at`, and `lock_at`;
|
|
267
|
+
`points_possible` defaults to 1, `published` defaults to `false`, and
|
|
268
|
+
`reviews_per_student` is a per-reviewer cap (default 3). Each PDF receives at least
|
|
269
|
+
three reviews by default, and every eligible reviewer receives at least one distinct
|
|
270
|
+
PDF when possible. Configure `min_reviews_per_submission` and
|
|
271
|
+
`min_reviews_per_student` to override those minimums.
|
|
272
|
+
|
|
273
|
+
For submitted reports, use the source assignment instead of the `pdfs` list:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
lms-interface-helper peer-review --course-id <COURSE_ID> \
|
|
277
|
+
--source-assignment-id <ASSIGNMENT_ID> --yaml-path peer-review.yaml
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The helper takes the first PDF attachment from each source submission and never
|
|
281
|
+
assigns a student their own submission. Local `pdfs` remain available for debug
|
|
282
|
+
runs. Use `--seed` to reproduce a randomized allocation.
|
|
283
|
+
|
|
284
|
+
Peer-review YAML ignores legacy section `note` fields. Use `instructions` on the
|
|
285
|
+
rubric (shown above the PDF) or on a section (shown under that section heading)
|
|
286
|
+
for student-facing guidance.
|