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.
Files changed (30) hide show
  1. {moodle_study_kit-0.2.2/src/moodle_study_kit.egg-info → moodle_study_kit-0.2.4}/PKG-INFO +2 -4
  2. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/pyproject.toml +3 -4
  3. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/__init__.py +1 -1
  4. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/cli.py +4 -2
  5. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/materials.py +29 -4
  6. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/mcp_server.py +3 -2
  7. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4/src/moodle_study_kit.egg-info}/PKG-INFO +2 -4
  8. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/requires.txt +1 -4
  9. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_materials.py +21 -0
  10. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/LICENSE +0 -0
  11. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/MANIFEST.in +0 -0
  12. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/README.md +0 -0
  13. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/setup.cfg +0 -0
  14. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/banner.py +0 -0
  15. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/client.py +0 -0
  16. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/config.py +0 -0
  17. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/deadlines.py +0 -0
  18. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/extraction.py +0 -0
  19. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit/summaries.py +0 -0
  20. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/SOURCES.txt +0 -0
  21. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/dependency_links.txt +0 -0
  22. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/entry_points.txt +0 -0
  23. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/src/moodle_study_kit.egg-info/top_level.txt +0 -0
  24. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_banner.py +0 -0
  25. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_cli.py +0 -0
  26. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_client.py +0 -0
  27. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_config.py +0 -0
  28. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_deadlines.py +0 -0
  29. {moodle_study_kit-0.2.2 → moodle_study_kit-0.2.4}/tests/test_extraction.py +0 -0
  30. {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.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.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", "pymupdf>=1.23"]
26
+ dev = ["pytest>=7.0"]
28
27
 
29
28
  [project.scripts]
30
29
  moodle-study = "moodle_study_kit.cli:main"
@@ -1,6 +1,6 @@
1
1
  """moodle-study-kit — Read-only Moodle toolkit."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.2.4"
4
4
 
5
5
  from moodle_study_kit.client import (
6
6
  ALLOWED_FUNCTIONS,
@@ -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(sections, course_id, args.week)
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
- return {
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(course_id, section, module, file_info)
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(course_id, section, module)
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
- sections = _get_client().get_course_contents(course_id)
96
- materials = get_section_materials(sections, course_id, week)
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.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
@@ -1,12 +1,9 @@
1
1
  platformdirs>=2.10
2
+ pymupdf>=1.23
2
3
  rich>=13.0
3
4
 
4
5
  [dev]
5
6
  pytest>=7.0
6
- pymupdf>=1.23
7
7
 
8
8
  [mcp]
9
9
  mcp>=1.0
10
-
11
- [pdf]
12
- pymupdf>=1.23
@@ -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