09. Feature: scraper/descarga de materiales desde Moodle
Objetivo
Descubrir y descargar recursos relevantes de cursos de Moodle a un directorio temporal local (en /tmp) para su posterior procesamiento por el pipeline.
Entradas
moodle_url(string)username/password(strings)course_ids(list[string]) — actualmente requeridocourse_slug_map(dict opcionalcourse_id -> slug) para forzar nombres de directorio estables.
Salidas
list[pathlib.Path]apuntando a archivos descargados en un árbol temporal:/tmp/kdef-moodle-<random>/<curso>/<seccion>/<archivo>
API de módulo
Código: scripts/scraper.py
download_course_materials(...) -> list[Path](entrypoint)list_course_resources(session, moodle_url, course_id) -> list[dict]download_file(session, url, dest_dir, filename=None) -> Path
Tipos soportados
El scraper filtra por extensión (ver SUPPORTED_EXTENSIONS):
.pdf,.docx,.pptx,.txt,.md
Los demás tipos pueden descartarse temprano, salvo links de actividades Moodle que luego redirijan a un tipo soportado.
Descubrimiento de recursos (alto nivel)
Para cada course_id:
- GET
.../course/view.php?id=<course_id> - Determina
course_titledesde elh1de la página. - Determina
course_dir_name:- si existe
course_slug_map[course_id]→ usarlo, - si no → slugify del
course_title.
- si existe
- Construye lista de recursos:
- recursos visibles en la portada,
- y recursos dentro de tiles/secciones del curso (Moodle Tiles).
- Para cada recurso:
- si es un link externo:
- si es YouTube → crea un placeholder
.md, - si no → se ignora (ver 10 — links externos).
- si es YouTube → crea un placeholder
- si es un archivo soportado → lo descarga con streaming a disco.
- si es un link externo:
Estructura del árbol temporal
- Directorio base:
tempfile.mkdtemp(prefix="kdef-moodle-"). - Directorio de sección:
_section_dirname(section_title):- si detecta rango “dd/mm al dd/mm” devuelve
dd-mm-a-dd-mm, - si no, aplica slugify.
- si detecta rango “dd/mm al dd/mm” devuelve
Placeholders para links externos
Si un recurso es un link externo, solo se generan placeholders para YouTube (comportamiento actual). El resto se ignora para no poblar el garden con links irrelevantes.
- Código:
_write_link_placeholder()+_is_youtube_url(). - Se agrega ese
.mda la lista de archivos “descargados” y el pipeline lo copia como salida sin pasar por el LLM (usakdef_skip: true).
Para el criterio/razón de esta decisión, ver 10 — links externos.
Limitaciones actuales
- Si
course_idsesNoneo vacío, el scraper loguea warning y retorna lista vacía (no implementa aún el “descubrimiento de cursos inscriptos”). - Esto impacta el modo dry-run del pipeline (ver 14 — orquestador del pipeline).