canvas-pull 2.4.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.
@@ -0,0 +1,181 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+
176
+ # canvas-pull local settings (may contain tokens)
177
+ /config/
178
+ /config.yml
179
+ .canvas-pull/
180
+ .DS_Store
181
+ .idea/
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5
+ uses [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [2.4.0] - 2026-11-01
10
+
11
+ First release on PyPI: `uv tool install canvas-pull` or `pipx install canvas-pull`.
12
+
13
+ ### Added
14
+ - Windows: the sync lock now works there too (`msvcrt`), so a manual and a background sync can't
15
+ run at the same time.
16
+ - CI on macOS, Linux and Windows with Python 3.11–3.13; tagged releases publish to PyPI
17
+ automatically.
18
+ - Contributor guide, security policy, issue templates and a list of tested schools.
19
+
20
+ ### Changed
21
+ - Paths under your home folder print as `~/...`.
22
+ - `sync` lists changes sorted by course, change type and path, not in download order.
23
+
24
+ ### Fixed
25
+ - File and folder names that are reserved on Windows (`CON`, `NUL`, `COM1`, …) get a `_` prefix
26
+ so they can be saved.
27
+
28
+ ## [2.3.0] - 2026-11-01
29
+
30
+ ### Changed
31
+ - Much faster scanning. Courses are scanned in parallel, folder and file lists are fetched at
32
+ the same time, and linked files are resolved in parallel. On a 32-course account: 75 s → 10.6 s
33
+ (Files tab only), 410 s → 60 s (all sources).
34
+ - Module items are requested inline (`include[]=items`).
35
+
36
+ ### Added
37
+ - `scan_concurrency` setting (default 8) caps the number of API calls in flight.
38
+ - Completed courses are scanned once. After a clean sync they are skipped; use `--refresh` to
39
+ scan them again. Enabling a new source rescans them automatically.
40
+
41
+ ## [2.2.0] - 2026-11-01
42
+
43
+ ### Changed
44
+ - Only the Files tab is synced by default. Other sources are opt-in through `sources`. When the
45
+ Files tab is hidden or empty, Modules is still used as a fallback.
46
+
47
+ ## [2.1.0] - 2026-11-01
48
+
49
+ ### Added
50
+ - `canvas-pull schedule enable|disable|status|run`: automatic sync through launchd (macOS) or a
51
+ systemd user timer (Linux).
52
+ - `sync --notify` shows desktop notifications for changes, failed downloads and a rejected
53
+ token.
54
+ - More file sources: syllabus, assignment descriptions, pages, announcements, discussions.
55
+ - `--completed` archives concluded courses.
56
+ - `log --since 7d`: merged summary of all changes in a time window.
57
+
58
+ ## [2.0.0] - 2026-11-01
59
+
60
+ Complete rewrite as an installable CLI.
61
+
62
+ ### Added
63
+ - Commands: `init`, `courses`, `status`, `sync`, `log`, `config`.
64
+ - Incremental sync tracked by Canvas file ID. It reports new, modified, moved, deleted and
65
+ restored files, and detects renames without downloading again.
66
+ - Replaced versions are kept in `.canvas-pull/history/`.
67
+ - Token stored in the system keychain.
68
+ - Optional `--git` commit after each sync.
69
+
70
+ ### Fixed
71
+ - Pagination is followed, so courses with more than 10 files are complete.
72
+ - Unchanged files are no longer downloaded again on every run.
73
+ - HTTP errors are reported instead of ignored.
74
+
75
+ ## [1.0.0] - 2025-03-02
76
+
77
+ - First version: download all files of enrolled courses.
78
+
79
+ [Unreleased]: https://github.com/gxxxr-111/canvas-pull/compare/v2.4.0...HEAD
80
+ [2.4.0]: https://github.com/gxxxr-111/canvas-pull/compare/v1.0.0...v2.4.0
81
+ [1.0.0]: https://github.com/gxxxr-111/canvas-pull/releases/tag/v1.0.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mirage_
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,280 @@
1
+ Metadata-Version: 2.5
2
+ Name: canvas-pull
3
+ Version: 2.4.0
4
+ Summary: Sync your Canvas LMS course files: download only what changed and see what instructors added, modified, moved or deleted.
5
+ Project-URL: Homepage, https://github.com/gxxxr-111/canvas-pull
6
+ Project-URL: Repository, https://github.com/gxxxr-111/canvas-pull
7
+ Project-URL: Issues, https://github.com/gxxxr-111/canvas-pull/issues
8
+ Project-URL: Changelog, https://github.com/gxxxr-111/canvas-pull/blob/main/CHANGELOG.md
9
+ Author-email: Mirage_ <gaoxuran111@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: canvas,canvas-lms,cli,download,instructure,lms,students,sync
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: Microsoft :: Windows
19
+ Classifier: Operating System :: POSIX :: Linux
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Education
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.11
28
+ Requires-Dist: httpx>=0.27
29
+ Requires-Dist: keyring>=25
30
+ Requires-Dist: platformdirs>=4.2
31
+ Requires-Dist: pydantic>=2.7
32
+ Requires-Dist: rich>=13.7
33
+ Requires-Dist: typer>=0.12
34
+ Description-Content-Type: text/markdown
35
+
36
+ <h1 align="center">Canvas Pull</h1>
37
+
38
+ <p align="center">
39
+ <b>Keep a local copy of your Canvas course files, and see exactly what changed.</b>
40
+ </p>
41
+
42
+ <p align="center">
43
+ <a href="https://github.com/gxxxr-111/canvas-pull/actions/workflows/ci.yml"><img src="https://github.com/gxxxr-111/canvas-pull/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
44
+ <a href="https://pypi.org/project/canvas-pull/"><img src="https://img.shields.io/pypi/v/canvas-pull" alt="PyPI" /></a>
45
+ <a href="https://pypi.org/project/canvas-pull/"><img src="https://img.shields.io/pypi/pyversions/canvas-pull" alt="Python versions" /></a>
46
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT license" /></a>
47
+ </p>
48
+
49
+ <p align="center">
50
+ <a href="#quick-start">Quick start</a> ·
51
+ <a href="#commands">Commands</a> ·
52
+ <a href="#faq">FAQ</a> ·
53
+ <a href="#中文说明">中文说明</a>
54
+ </p>
55
+
56
+ canvas-pull downloads the files of your Canvas LMS courses with one command. Every later run
57
+ downloads only what changed and tells you which files your instructors **added, modified,
58
+ renamed/moved or deleted**. It can run on its own every morning and notify you.
59
+
60
+ <p align="center">
61
+ <img src="https://raw.githubusercontent.com/gxxxr-111/canvas-pull/main/docs/demo.svg" alt="canvas-pull sync and log --since output" width="760" />
62
+ </p>
63
+
64
+ ## Features
65
+
66
+ - **Incremental sync.** Files are tracked by their Canvas file ID, so unchanged files are never
67
+ downloaded again and renames are detected without re-downloading.
68
+ - **Change report.** `status` previews changes, `sync` applies them, `log` shows past syncs,
69
+ and `log --since 7d` gives a merged weekly summary.
70
+ - **Automatic sync.** `schedule enable` runs a sync every day (or every few hours) and shows a
71
+ desktop notification when something changes.
72
+ - **Old versions kept.** When a file changes, the previous copy is moved to
73
+ `.canvas-pull/history/<timestamp>/`. Files deleted on Canvas stay on your disk.
74
+ - **Files tab by default, more on request.** Only the Files tab is synced by default (with
75
+ Modules as a fallback when a course hides it). Opt in to the syllabus, assignment
76
+ descriptions, pages, announcements and discussions through the `sources` setting.
77
+ - **Current courses, or everything.** Courses whose term has ended are skipped by default;
78
+ `--completed` also archives concluded courses. A completed course is scanned once; later syncs
79
+ skip it (use `--refresh` to scan it again).
80
+ - **Safe token storage.** `init` stores your token in the system keychain, not in a text file.
81
+ - **Optional git.** `sync --git` commits each sync in the save folder.
82
+ - **Read-only and private.** Only reads from Canvas; no telemetry, nothing leaves your machine.
83
+ - **Fast.** Courses are scanned in parallel (a 32-course account scans in about 10 s instead of
84
+ 75 s), with parallel downloads, automatic retries with back-off and full pagination.
85
+
86
+ ## Install
87
+
88
+ ```bash
89
+ uv tool install canvas-pull # or: pipx install canvas-pull
90
+ ```
91
+
92
+ Try it without installing: `uvx canvas-pull --help`. Python 3.11+ on macOS, Linux or Windows.
93
+ To get the latest unreleased code: `uv tool install git+https://github.com/gxxxr-111/canvas-pull`.
94
+
95
+ ## Quick start
96
+
97
+ 1. In Canvas, open **Account → Settings → New Access Token** and copy the token.
98
+ Give it an expiry date. Never share it.
99
+ 2. Run:
100
+
101
+ ```bash
102
+ canvas-pull init # Canvas URL (e.g. oc.sjtu.edu.cn), save folder, token
103
+ canvas-pull courses # check which courses will be synced
104
+ canvas-pull sync
105
+ ```
106
+
107
+ ## Commands
108
+
109
+ | Command | What it does |
110
+ | --- | --- |
111
+ | `canvas-pull init` | Set Canvas URL, save folder and token (token goes to the keychain). |
112
+ | `canvas-pull courses [--all] [--completed]` | List courses that would be synced. |
113
+ | `canvas-pull status [-c COURSE] [--completed]` | Show what changed on Canvas. Downloads nothing. |
114
+ | `canvas-pull sync [-n] [-c COURSE] [--completed] [--refresh] [--git] [--notify]` | Download changes. `-n` is a dry run. `-c` takes a course ID or part of its name and can be repeated. `--refresh` rescans archived completed courses. `--notify` shows a desktop notification. |
115
+ | `canvas-pull log [-n 10]` | Show recent syncs. |
116
+ | `canvas-pull log --since 7d` | Merged summary since `7d`, `24h`, `2w`, `today` or a date. |
117
+ | `canvas-pull schedule enable [--at 08:00 ...] [--every 6h]` | Sync automatically (launchd on macOS, systemd timer on Linux). Default: daily 09:00. |
118
+ | `canvas-pull schedule status \| run \| disable` | Show the schedule and its log, trigger it now, or remove it. |
119
+ | `canvas-pull config` | Show settings in use. |
120
+
121
+ ## Configuration
122
+
123
+ `init` writes a config file (path shown by `canvas-pull config`). You can edit it:
124
+
125
+ ```toml
126
+ website = "https://oc.sjtu.edu.cn"
127
+ save_dir = "/Users/me/Canvas"
128
+ concurrency = 4 # parallel downloads
129
+ scan_concurrency = 8 # parallel API requests while scanning (max 16)
130
+ keep_history = true # keep replaced versions in .canvas-pull/history
131
+ git = false # commit after every sync
132
+ exclude = ["PE", "12345"] # course IDs or name fragments to skip
133
+ include_completed = false # also sync concluded courses
134
+ sources = ["files"] # default; add any of the sources below
135
+ ```
136
+
137
+ Available sources: `files`, `modules`, `syllabus`, `assignments`, `pages`, `announcements`,
138
+ `discussions`. With the default, a course that hides its Files tab (or leaves it empty) falls
139
+ back to Modules automatically. To grab everything:
140
+
141
+ ```toml
142
+ sources = ["files", "modules", "syllabus", "assignments", "pages", "announcements", "discussions"]
143
+ ```
144
+
145
+ Environment variables override the file: `CANVAS_URL`, `CANVAS_TOKEN`, `CANVAS_SAVE_DIR`. A
146
+ `.env` file in the current folder is also read (v1 keys `WEBSITE`, `ACCESS_TOKEN`, `SAVE_DIR`
147
+ still work).
148
+
149
+ When a file appears in several places it keeps the path of the first source in that list, so a
150
+ file in the Files tab stays where it is even if an assignment links to it. Files found elsewhere
151
+ go under `Modules/<module>/`, `Assignments/<name>/`, `Pages/<title>/`, `Announcements/<title>/`,
152
+ `Discussions/<title>/` or `Syllabus/`.
153
+
154
+ ### Automatic sync
155
+
156
+ ```bash
157
+ canvas-pull schedule enable --at 08:00 --at 20:00 # or: --every 6h
158
+ canvas-pull schedule run # try it once now
159
+ canvas-pull schedule status # schedule + last log lines
160
+ ```
161
+
162
+ The job runs `canvas-pull sync --notify` from the folder where you enabled it. A Mac that was
163
+ asleep at the scheduled time catches up when it wakes. Install with `uv tool install` first so
164
+ the job points at a stable path.
165
+
166
+ On Windows, `schedule` is not available yet: create a daily task in Task Scheduler that runs
167
+ `canvas-pull sync`. Desktop notifications are macOS and Linux only for now.
168
+
169
+ ## How change detection works
170
+
171
+ Each synced file is recorded in `<save_dir>/.canvas-pull/manifest.json` with its Canvas ID,
172
+ path, size, last-modified time and SHA-256. On every run canvas-pull compares that record with
173
+ Canvas metadata:
174
+
175
+ | Canvas says | Result |
176
+ | --- | --- |
177
+ | ID not seen before | **new**: download |
178
+ | Size or modified time changed | **modified**: download; if the bytes are identical, nothing is reported |
179
+ | Same content, different name/folder | **moved**: renamed locally, no download |
180
+ | ID gone from a course that was read successfully | **deleted**: reported once; local copy kept |
181
+ | Recorded but missing on disk | **restored**: downloaded again |
182
+
183
+ Content-level diffs of PDF/PPTX/DOCX are out of scope for now.
184
+
185
+ ## Tested schools
186
+
187
+ canvas-pull uses the standard Canvas REST API, so it should work with any Canvas site where
188
+ students can create access tokens.
189
+
190
+ | School | Canvas site | Status |
191
+ | --- | --- | --- |
192
+ | Shanghai Jiao Tong University (上海交通大学) | `oc.sjtu.edu.cn` | ✅ Works |
193
+
194
+ Tried it at your school? Please open a
195
+ [school report](https://github.com/gxxxr-111/canvas-pull/issues/new?template=school_report.yml),
196
+ whether it worked or not.
197
+
198
+ ## FAQ
199
+
200
+ **My school doesn't let students create access tokens.**
201
+ That isn't supported yet. Please file a school report so we know which schools need another
202
+ sign-in method.
203
+
204
+ **What does "N locked file(s) not yet available" mean?**
205
+ The instructor has locked those files (until a date, or behind a module requirement). They are
206
+ downloaded automatically once they unlock.
207
+
208
+ **Where did the old version of a changed file go?**
209
+ To `<save_dir>/.canvas-pull/history/<timestamp>/`, keeping its folder structure. Turn this off
210
+ with `keep_history = false`.
211
+
212
+ **I deleted a downloaded file by accident.**
213
+ Run `canvas-pull sync`; it is downloaded again and reported as *restored*.
214
+
215
+ **Can canvas-pull change anything on Canvas?**
216
+ No. It only sends read (`GET`) requests.
217
+
218
+ **My token expired.**
219
+ Create a new one in Canvas and run `canvas-pull init` again. Scheduled syncs show a notification
220
+ when the token is rejected.
221
+
222
+ **How do I skip a course?**
223
+ Add its ID or part of its name to `exclude` in the config file (`canvas-pull courses` shows IDs).
224
+
225
+ ## Privacy and security
226
+
227
+ - The token is kept in the OS keychain and is only sent to your Canvas site. It is not forwarded
228
+ when a download redirects to file storage.
229
+ - No telemetry and no third-party services.
230
+ - See [SECURITY.md](SECURITY.md) for details and how to report a vulnerability.
231
+
232
+ ## Related projects
233
+
234
+ Other Canvas download tools you may also like:
235
+
236
+ - [skyzh/canvas_grab](https://github.com/skyzh/canvas_grab): sync script with a GUI, popular at SJTU.
237
+ - [perslev/CanvasSync](https://github.com/perslev/CanvasSync): syncs modules, assignments and files.
238
+ - [aik2mlj/canvas-downloader](https://github.com/aik2mlj/canvas-downloader): fast downloader written in Rust.
239
+ - [BrkBuilds/Canvas-Downloader](https://github.com/BrkBuilds/Canvas-Downloader): desktop app for Windows and macOS.
240
+ - [Beebeeoii/lominus](https://github.com/Beebeeoii/lominus): file sync for NUS Canvas.
241
+
242
+ canvas-pull focuses on the command line and automation: it tracks files by ID, reports exactly
243
+ what changed (including renames), keeps old versions, and can run unattended.
244
+
245
+ ## Contributing
246
+
247
+ Bug reports, school reports and pull requests are welcome. See
248
+ [CONTRIBUTING.md](CONTRIBUTING.md). Tests run against an in-memory fake Canvas, so no account is
249
+ needed:
250
+
251
+ ```bash
252
+ uv sync && uv run pytest
253
+ ```
254
+
255
+ Changes are listed in [CHANGELOG.md](CHANGELOG.md). Licensed under [MIT](LICENSE).
256
+
257
+ ---
258
+
259
+ ## 中文说明
260
+
261
+ 一条命令同步所有**正在进行的** Canvas 课程文件。之后每次运行只下载有变化的文件,并列出老师**新增、修改、改名/移动、删除**了哪些文件。
262
+
263
+ ```bash
264
+ uv tool install canvas-pull # 或 pipx install canvas-pull
265
+ canvas-pull init # 填学校 Canvas 域名(如 oc.sjtu.edu.cn)、保存目录、Access Token
266
+ canvas-pull status # 只看变化,不下载
267
+ canvas-pull sync # 下载变化
268
+ canvas-pull log --since 7d # 本周汇总
269
+ canvas-pull schedule enable --at 08:00 # 每天早上自动同步,有变化时弹通知
270
+ ```
271
+
272
+ - Token 在 Canvas 的「账户 → 设置 → 新建访问许可证」生成,`init` 会把它存进系统钥匙串。
273
+ - 文件被修改时,旧版本会移到 `.canvas-pull/history/`;Canvas 上删除的文件本地不会删除。
274
+ - 默认只同步「文件」栏目;课程隐藏了文件栏目时,会自动改从「模块」里找。
275
+ - 想要更多来源(课程大纲、作业、页面、公告、讨论里的附件和链接),在配置文件里设置 `sources`。
276
+ - `--completed` 可以把已结业的课程也一起归档下来。已结业的课只完整扫描一次,之后自动跳过(`--refresh` 强制重扫)。
277
+ - 多门课并行扫描,32 门课大约 10 秒扫完。
278
+ - `sync --git` 会在保存目录里自动提交一次 git,方便用 `git log` 回看。
279
+ - 只读取 Canvas,不会修改任何内容;没有遥测,数据不离开你的电脑。
280
+ - 在你的学校试过了?欢迎提交一个 [School report](https://github.com/gxxxr-111/canvas-pull/issues/new?template=school_report.yml),无论成功与否。