moodle-study-kit 0.2.2__tar.gz → 0.2.4__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.
- {moodle_study_kit-0.2.2/src/moodle_study_kit.egg-info → moodle_study_kit-0.2.4}/PKG-INFO +2 -4
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/pyproject.toml +3 -4
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/__init__.py +1 -1
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/cli.py +4 -2
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/materials.py +29 -4
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/mcp_server.py +3 -2
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4/src/moodle_study_kit.egg-info}/PKG-INFO +2 -4
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/requires.txt +1 -4
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_materials.py +21 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/LICENSE +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/MANIFEST.in +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/README.md +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/setup.cfg +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/banner.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/client.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/config.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/deadlines.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/extraction.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/summaries.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/SOURCES.txt +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/dependency_links.txt +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/entry_points.txt +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/top_level.txt +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_banner.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_cli.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_client.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_config.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_deadlines.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_extraction.py +0 -0
- {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_summaries.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moodle-study-kit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Read-only Moodle toolkit for agents, MCP servers, and local scripts
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Keywords: moodle,lms,education,study,mcp
|
|
@@ -15,14 +15,12 @@ Requires-Python: >=3.10
|
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: platformdirs>=2.10
|
|
18
|
+
Requires-Dist: pymupdf>=1.23
|
|
18
19
|
Requires-Dist: rich>=13.0
|
|
19
|
-
Provides-Extra: pdf
|
|
20
|
-
Requires-Dist: pymupdf>=1.23; extra == "pdf"
|
|
21
20
|
Provides-Extra: mcp
|
|
22
21
|
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
23
22
|
Provides-Extra: dev
|
|
24
23
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
-
Requires-Dist: pymupdf>=1.23; extra == "dev"
|
|
26
24
|
Dynamic: license-file
|
|
27
25
|
|
|
28
26
|
# moodle-study-kit
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "moodle-study-kit"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "Read-only Moodle toolkit for agents, MCP servers, and local scripts"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
license = "MIT"
|
|
@@ -19,12 +19,11 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python :: 3.13",
|
|
20
20
|
"Topic :: Education",
|
|
21
21
|
]
|
|
22
|
-
dependencies = ["platformdirs>=2.10", "rich>=13.0"]
|
|
22
|
+
dependencies = ["platformdirs>=2.10", "pymupdf>=1.23", "rich>=13.0"]
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
|
-
pdf = ["pymupdf>=1.23"]
|
|
26
25
|
mcp = ["mcp>=1.0"]
|
|
27
|
-
dev = ["pytest>=7.0"
|
|
26
|
+
dev = ["pytest>=7.0"]
|
|
28
27
|
|
|
29
28
|
[project.scripts]
|
|
30
29
|
moodle-study = "moodle_study_kit.cli:main"
|
|
@@ -412,9 +412,11 @@ def cmd_materials(args: argparse.Namespace) -> None:
|
|
|
412
412
|
sections = client.get_course_contents(course_id)
|
|
413
413
|
|
|
414
414
|
if args.week is not None:
|
|
415
|
-
materials = get_section_materials(
|
|
415
|
+
materials = get_section_materials(
|
|
416
|
+
sections, course_id, args.week, base_url=client.base_url,
|
|
417
|
+
)
|
|
416
418
|
else:
|
|
417
|
-
materials = extract_materials(sections, course_id)
|
|
419
|
+
materials = extract_materials(sections, course_id, base_url=client.base_url)
|
|
418
420
|
|
|
419
421
|
if args.json:
|
|
420
422
|
print(json.dumps(materials, indent=2))
|
|
@@ -74,12 +74,17 @@ def _build_material(
|
|
|
74
74
|
section: dict,
|
|
75
75
|
module: dict,
|
|
76
76
|
file_info: dict | None = None,
|
|
77
|
+
*,
|
|
78
|
+
base_url: str = "",
|
|
77
79
|
) -> dict:
|
|
78
80
|
"""Build one structured material record.
|
|
79
81
|
|
|
80
82
|
If *file_info* is provided the record describes a specific file
|
|
81
83
|
inside a module's ``contents`` array. Otherwise it describes the
|
|
82
84
|
module itself (e.g. a URL resource with no file list).
|
|
85
|
+
|
|
86
|
+
When *base_url* is given, a ``view_url`` key is added pointing to
|
|
87
|
+
the human-readable Moodle page for the module.
|
|
83
88
|
"""
|
|
84
89
|
module_id = module.get("id", 0)
|
|
85
90
|
|
|
@@ -98,7 +103,7 @@ def _build_material(
|
|
|
98
103
|
time_modified = module.get("added", 0)
|
|
99
104
|
dedupe_key = f"{module_id}:"
|
|
100
105
|
|
|
101
|
-
|
|
106
|
+
record = {
|
|
102
107
|
"course_id": course_id,
|
|
103
108
|
"section_id": section.get("id", 0),
|
|
104
109
|
"section_name": section.get("name", ""),
|
|
@@ -114,6 +119,14 @@ def _build_material(
|
|
|
114
119
|
"dedupe_key": dedupe_key,
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
if base_url:
|
|
123
|
+
modname = module.get("modname", "")
|
|
124
|
+
record["view_url"] = (
|
|
125
|
+
f"{base_url}/mod/{modname}/view.php?id={module_id}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return record
|
|
129
|
+
|
|
117
130
|
|
|
118
131
|
# ── Extraction helpers ───────────────────────────────────────────
|
|
119
132
|
|
|
@@ -122,6 +135,7 @@ def extract_materials(
|
|
|
122
135
|
sections: list[dict],
|
|
123
136
|
course_id: int,
|
|
124
137
|
*,
|
|
138
|
+
base_url: str = "",
|
|
125
139
|
dedupe: bool = True,
|
|
126
140
|
) -> list[dict]:
|
|
127
141
|
"""Extract material records from Moodle course sections.
|
|
@@ -132,6 +146,10 @@ def extract_materials(
|
|
|
132
146
|
Raw sections from ``core_course_get_contents``.
|
|
133
147
|
course_id : int
|
|
134
148
|
Included in every record for cross-course context.
|
|
149
|
+
base_url : str
|
|
150
|
+
Moodle site root (e.g. ``https://moodle.example.com``).
|
|
151
|
+
When provided, each record includes a ``view_url`` pointing
|
|
152
|
+
to the human-readable module page.
|
|
135
153
|
dedupe : bool
|
|
136
154
|
Suppress duplicates sharing the same ``dedupe_key``.
|
|
137
155
|
|
|
@@ -154,7 +172,10 @@ def extract_materials(
|
|
|
154
172
|
if file_info.get("type", "") not in ("file", "url"):
|
|
155
173
|
continue
|
|
156
174
|
|
|
157
|
-
record = _build_material(
|
|
175
|
+
record = _build_material(
|
|
176
|
+
course_id, section, module, file_info,
|
|
177
|
+
base_url=base_url,
|
|
178
|
+
)
|
|
158
179
|
|
|
159
180
|
if dedupe and record["dedupe_key"] in seen:
|
|
160
181
|
continue
|
|
@@ -163,7 +184,10 @@ def extract_materials(
|
|
|
163
184
|
|
|
164
185
|
elif module.get("modname", "") in ("url", "page"):
|
|
165
186
|
# Module has no contents array but is itself a link or page.
|
|
166
|
-
record = _build_material(
|
|
187
|
+
record = _build_material(
|
|
188
|
+
course_id, section, module,
|
|
189
|
+
base_url=base_url,
|
|
190
|
+
)
|
|
167
191
|
|
|
168
192
|
if dedupe and record["dedupe_key"] in seen:
|
|
169
193
|
continue
|
|
@@ -178,6 +202,7 @@ def get_section_materials(
|
|
|
178
202
|
course_id: int,
|
|
179
203
|
week: int | str,
|
|
180
204
|
*,
|
|
205
|
+
base_url: str = "",
|
|
181
206
|
dedupe: bool = True,
|
|
182
207
|
) -> list[dict]:
|
|
183
208
|
"""Get materials for sections matching *week*.
|
|
@@ -185,7 +210,7 @@ def get_section_materials(
|
|
|
185
210
|
Combines :func:`match_section` filtering with :func:`extract_materials`.
|
|
186
211
|
"""
|
|
187
212
|
matching = [s for s in sections if match_section(s, week)]
|
|
188
|
-
return extract_materials(matching, course_id, dedupe=dedupe)
|
|
213
|
+
return extract_materials(matching, course_id, base_url=base_url, dedupe=dedupe)
|
|
189
214
|
|
|
190
215
|
|
|
191
216
|
def search_course_content(
|
|
@@ -92,8 +92,9 @@ def get_week_materials(course_id: int, week: str) -> str:
|
|
|
92
92
|
(e.g. "Revision"). Numeric matching is based on explicit week-like
|
|
93
93
|
labels in section names, not Moodle's raw section slot number.
|
|
94
94
|
"""
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
client = _get_client()
|
|
96
|
+
sections = client.get_course_contents(course_id)
|
|
97
|
+
materials = get_section_materials(sections, course_id, week, base_url=client.base_url)
|
|
97
98
|
return json.dumps(materials, indent=2)
|
|
98
99
|
|
|
99
100
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moodle-study-kit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Read-only Moodle toolkit for agents, MCP servers, and local scripts
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Keywords: moodle,lms,education,study,mcp
|
|
@@ -15,14 +15,12 @@ Requires-Python: >=3.10
|
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: platformdirs>=2.10
|
|
18
|
+
Requires-Dist: pymupdf>=1.23
|
|
18
19
|
Requires-Dist: rich>=13.0
|
|
19
|
-
Provides-Extra: pdf
|
|
20
|
-
Requires-Dist: pymupdf>=1.23; extra == "pdf"
|
|
21
20
|
Provides-Extra: mcp
|
|
22
21
|
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
23
22
|
Provides-Extra: dev
|
|
24
23
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
-
Requires-Dist: pymupdf>=1.23; extra == "dev"
|
|
26
24
|
Dynamic: license-file
|
|
27
25
|
|
|
28
26
|
# moodle-study-kit
|
|
@@ -251,6 +251,22 @@ class TestExtractMaterials:
|
|
|
251
251
|
assert rec["mime_type"] == "application/pdf"
|
|
252
252
|
assert rec["dedupe_key"] == "1010:week1_slides.pdf"
|
|
253
253
|
|
|
254
|
+
def test_no_view_url_without_base_url(self):
|
|
255
|
+
materials = extract_materials(SAMPLE_SECTIONS, COURSE_ID)
|
|
256
|
+
assert "view_url" not in materials[0]
|
|
257
|
+
|
|
258
|
+
def test_view_url_with_base_url(self):
|
|
259
|
+
base = "https://moodle.example.com"
|
|
260
|
+
materials = extract_materials(SAMPLE_SECTIONS, COURSE_ID, base_url=base)
|
|
261
|
+
rec = next(m for m in materials if m["file_name"] == "week1_slides.pdf")
|
|
262
|
+
assert rec["view_url"] == f"{base}/mod/resource/view.php?id=1010"
|
|
263
|
+
|
|
264
|
+
def test_view_url_for_url_module(self):
|
|
265
|
+
base = "https://moodle.example.com"
|
|
266
|
+
materials = extract_materials(SAMPLE_SECTIONS, COURSE_ID, base_url=base)
|
|
267
|
+
url_rec = next(m for m in materials if m["module_name"] == "Useful Links")
|
|
268
|
+
assert url_rec["view_url"] == f"{base}/mod/url/view.php?id=1002"
|
|
269
|
+
|
|
254
270
|
def test_dedupe_suppresses_duplicates(self):
|
|
255
271
|
"""Same module+filename appearing twice should be deduped."""
|
|
256
272
|
dup_file = _make_file("dup.pdf")
|
|
@@ -323,6 +339,11 @@ class TestGetSectionMaterials:
|
|
|
323
339
|
assert "Week 10: Revision" not in names_1
|
|
324
340
|
assert "Week 10: Revision" in names_10
|
|
325
341
|
|
|
342
|
+
def test_base_url_propagated(self):
|
|
343
|
+
base = "https://moodle.example.com"
|
|
344
|
+
mats = get_section_materials(SAMPLE_SECTIONS, COURSE_ID, 1, base_url=base)
|
|
345
|
+
assert all("view_url" in m for m in mats)
|
|
346
|
+
|
|
326
347
|
|
|
327
348
|
# ── search_course_content ────────────────────────────────────────
|
|
328
349
|
|
|
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
|
{moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|