edutools-moodle 0.3.3
Python package for Moodle API interactions in educational contexts
Requires Python: >=3.8

# Edutools Moodle

[![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![PyPI version](https://img.shields.io/badge/pypi-0.3.1-blue.svg)](https://pypi.org/project/edutools-moodle/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Moodle](https://img.shields.io/badge/moodle-3.9+-orange.svg)](https://moodle.org)

Python package for interacting with Moodle Web Services API in educational contexts.

**Version 0.3.1** - Added groupings management methods for better group organization.

## Installation

### From source (development mode)

```bash
git clone https://github.com/najasoft/edutools-moodle.git
cd edutools-moodle
pip install -e .
```

### From PyPI (when published)

```bash
pip install edutools-moodle
```

## Quick Start

```python
from edutools_moodle import MoodleAPI

# Initialize the API client
moodle = MoodleAPI(
    moodle_url="https://your-moodle-instance.com",
    token="your_webservice_token"
)

# Get user's enrolled courses
courses = moodle.courses.get_user_courses()

# Get enrolled students in a course
users = moodle.courses.get_enrolled_users(course_id=123)

# Get course groups
groups = moodle.groups.get_course_groups(course_id=123)

# Get assignments for a course
assignments = moodle.assignments.get_assignments(course_id=123)

# Get submissions for an assignment
submissions = moodle.grades.get_submissions(assignment_id=456)
```

## Features

- ✅ **Courses Management**: Get courses, enrollments, contents, search (11 functions)
- ✅ **Groups Management**: Create, update, delete groups; manage members, groupings, and cohorts (22 functions) - **Enhanced in 0.3.1**
- ✅ **Users Management**: Create users, check existence, enroll in courses, send notifications (7 functions)
- ✅ **Assignments Management**: Retrieve assignments and submissions with optimized data (4 functions)
- ✅ **Grades Management**: Add, update, and retrieve grades efficiently (6 functions)
- ✅ **Robust Error Handling**: Comprehensive validation and detailed error messages
- ✅ **Performance Optimized**: Reduced response payloads for faster operations
- ✅ **Well Documented**: Complete API reference with 765 functions documented

## What's New in 0.3.1

- 📦 **Enhanced Groupings Support**: New methods for managing groupings
  - `get_course_groupings()` - Get all groupings in a course
  - `get_grouping_by_name()` - Find grouping by name for filtering
  - Better support for organizing groups into categories

## What's New in 0.3.0

- 🎓 **New MoodleCourses Module**: Comprehensive course management with 11 methods
  - Get user courses and enrollments
  - Search and filter courses
  - Get course contents and modules
  - Manage course categories
- 📋 **Permission Documentation**: Complete list of required Moodle web services (PERMISSIONS.md)
- ✅ **Fully Tested**: 18 automated tests + 10 exploratory tests for the new module

See [CHANGELOG.md](CHANGELOG.md) for detailed changes.

## Requirements

- Python 3.8+
- **Moodle 3.9+** (recommended, see [version compatibility](MOODLE_VERSIONS.md))
- Moodle instance with Web Services enabled
- Valid Moodle API token

## Configuration

### Generating a Moodle Token

1. Log in to Moodle as administrator
2. Go to Site Administration → Plugins → Web Services → Manage Tokens
3. Create a new token for your user
4. Copy the token string

### Checking Moodle Version

```python
from edutools_moodle import MoodleAPI

moodle = MoodleAPI("https://your-moodle.com", "token")

# Get site information
info = moodle.get_site_info()
print(f"Moodle version: {info['release']}")
print(f"Site name: {info['sitename']}")

# Check if version is compatible
if moodle.check_moodle_version("3.9"):
    print("✅ Moodle version is compatible")
else:
    print("⚠️ Moodle 3.9+ recommended")
```

## API Reference

### MoodleAPI (Facade)

Main class providing unified access to all modules.

#### `__init__(moodle_url: str, token: str, timeout: int = 30, logger: Optional[Logger] = None)`

Initialize the Moodle API client.

**Parameters:**
- `moodle_url` - Base URL of Moodle instance
- `token` - Web service token for authentication
- `timeout` - Request timeout in seconds (default: 30)
- `logger` - Optional logger instance (if not provided, uses default logging)

**Modules:**
- `moodle.groups` - Groups, groupings, and cohorts
- `moodle.users` - User management
- `moodle.assignments` - Assignments and submissions
- `moodle.grades` - Grade management

#### `get_site_info() -> dict`
Get Moodle site information including version.

#### `check_moodle_version(min_version: str) -> bool`
Check if Moodle version meets minimum requirement.

**Modules:**
- `moodle.courses` - **NEW in 0.3.0**: Course and enrollment management
- `moodle.groups` - Groups, groupings, and cohorts
- `moodle.users` - User management
- `moodle.assignments` - Assignments and submissions
- `moodle.grades` - Grade management

### Courses Module (`moodle.courses`) - **NEW in 0.3.0**

#### `get_user_courses(user_id: Optional[int] = None) -> list`
Get courses enrolled by a user. If user_id is None, gets courses for the authenticated user.

**Example:**
```python
courses = moodle.courses.get_user_courses()
for course in courses:
    print(f"{course['fullname']} (ID: {course['id']})")
```

#### `get_enrolled_users(course_id: int, options: Optional[list] = None) -> list`
Get all users enrolled in a course.

**Options:**
- `onlyactive`: Only active enrollments
- `userfields`: Additional user fields to include
- `withcapability`: Filter by capability

**Example:**
```python
# Get only active students
options = [{'name': 'onlyactive', 'value': 1}]
users = moodle.courses.get_enrolled_users(123, options=options)
```

#### `get_course_by_field(field: str, value: any) -> list`
Get courses by specific field (id, shortname, idnumber, category).

**Example:**
```python
courses = moodle.courses.get_course_by_field('shortname', 'CS101')
```

#### `get_categories(criteria: Optional[list] = None, add_subcategories: bool = True) -> list`
Get all course categories.

#### `get_course_contents(course_id: int, options: Optional[list] = None) -> list`
Get complete course contents (sections, modules, resources).

**Example:**
```python
contents = moodle.courses.get_course_contents(123)
for section in contents:
    print(f"Section: {section['name']}")
    for module in section.get('modules', []):
        print(f"  - {module['name']} ({module['modname']})")
```

#### `get_recent_courses(user_id: Optional[int] = None, limit: int = 10) -> list`
Get recently accessed courses for a user.

#### `search_courses(search: str, page: int = 0, perpage: int = 20) -> dict`
Search courses by name or summary.

**Example:**
```python
result = moodle.courses.search_courses('Python', page=0, perpage=10)
for course in result['courses']:
    print(course['fullname'])
```

#### `get_enrolled_users_by_capability(course_id: int, capability: str) -> list`
Get users enrolled in a course filtered by capability.

**Example:**
```python
# Get all students who can submit assignments
students = moodle.courses.get_enrolled_users_by_capability(
    course_id=123,
    capability='mod/assign:submit'
)
```

#### `get_course_modules(course_id: int) -> list`
Get all activity modules in a course (assignments, quizzes, forums, etc.).

#### `get_course_by_id(course_id: int) -> Optional[dict]`
Get a single course by ID.

### Groups Module (`moodle.groups`)

#### `get_course_groups(course_id: int) -> list`
Get all groups in a course.

#### `add_user_to_group(group_id: int, user_id: int) -> dict`
Add a user to a group.

#### `remove_user_from_group(group_id: int, user_id: int) -> dict`
Remove a user from a group.

#### `create_group(course_id: int, group_name: str, description: str = "") -> dict`
Create a new group in a course.

#### `is_user_in_cohort(user_id: int, cohort_name: str) -> bool`
Check if a user belongs to a specific cohort.

#### `enroll_user_in_cohort(user_id: int, cohort_name: str) -> bool`
Enroll a user in a cohort.

### Users Module (`moodle.users`)

#### `create_user(username, password, firstname, lastname, email, ...) -> int`
Create a new user account.

#### `check_username_exists(username: str) -> bool`
Check if username already exists.

#### `enroll_user_in_course(course_id, user_id, role_id=5) -> bool`
Enroll a user in a course.

#### `send_notification(user_id, subject, message) -> bool`
Send a notification to a user.

### Assignments Module (`moodle.assignments`)

#### `get_assignments(course_ids: list) -> list`
Get all assignments in specified courses (optimized: returns 11 essential fields).

#### `get_assignment_id_by_cmid(cmid: int, course_id: int) -> int`
Get assignment ID from course module ID.

#### `get_user_submission(assignment_id: int, user_id: int) -> dict`
Get submission details for a specific user (optimized: returns 8 essential fields).

### Grades Module (`moodle.grades`)

#### `get_grades(course_id: int, group_id: int = None, user_id: int = None) -> list`
Get grades for a course, optionally filtered by group or user (optimized: 10 fields per item).

#### `add_grade(assignment_id: int, user_id: int, grade: float, ...) -> dict`
Add a grade for a user's assignment submission.

#### `update_grade(assignment_id: int, user_id: int, grade: float, ...) -> dict`
Update an existing grade.

#### `get_grades_for_assignment(assignment_id: int, user_ids: list = None) -> list`
Get all grades for a specific assignment.

#### `get_course_grades(course_id: int, user_id: int) -> str`
Get grades table HTML for a specific user (requires user_id in v0.2.0).

## Documentation

- **[CHANGELOG.md](CHANGELOG.md)** - Version history and migration guide
- **[docs/api/](docs/api/)** - Complete Moodle API reference (119 modules, 765 functions)
- **[MOODLE_VERSIONS.md](MOODLE_VERSIONS.md)** - Moodle version compatibility

## Important Notes

### Breaking Changes in 0.2.0

1. `get_course_grades()` now requires `user_id` parameter
2. Removed `get_assignment_submissions()` - use `get_user_submission()` instead  
3. Removed `get_grade_items()` - use `get_grades()` instead

See [CHANGELOG.md](CHANGELOG.md) for complete migration guide.

## Development

### Running Tests

```bash
pip install pytest
pytest tests/
```

### Project Structure

```
edutools-moodle/
├── edutools_moodle/
│   ├── __init__.py
│   ├── base.py           # Base API client
│   ├── api.py            # Main MoodleAPI class
│   ├── groups.py         # Groups management
│   ├── assignments.py    # Assignments handling
│   └── grades.py         # Grades management
├── tests/
│   ├── __init__.py
│   └── test_api.py
├── setup.py
├── requirements.txt
└── README.md
```

## License

MIT License - see LICENSE file for details

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

For issues and questions, please open an issue on GitHub.