Skip to main content

A comprehensive Python client for the EcoleDirecte API with MFA support.

Project description

ecoledirecte-py-client

PyPI version Python Support License: GPL v3

A comprehensive Python client library for the EcoleDirecte API, providing easy access to student data, grades, homework, schedules, and more. Built with async/await support and robust Multi-Factor Authentication (MFA) handling.

Features

  • ๐Ÿ” Dual Account Support: Seamlessly handles both Student and Family (Parent) accounts
  • ๐Ÿ›ก๏ธ MFA/CNED Support: Integrated handling of Multi-Factor Authentication challenges
  • ๐Ÿค– Smart MFA: Automatically caches and reuses known MFA answers for frictionless future logins
  • โšก Async/Await: Built on modern async Python with httpx for efficient API calls
  • ๐Ÿ”’ Secure by Default: Credential management via environment variables
  • ๐Ÿ“ฆ Zero Configuration: Works out of the box with minimal setup
  • ๐ŸŽฏ Type Hints: Full type annotations for better IDE support and code quality
  • โœ… Well Tested: Comprehensive test suite with pytest

Installation

Install from PyPI using pip:

pip install ecoledirecte-py-client

Or using uv (recommended):

uv add ecoledirecte-py-client

Quick Start

Basic Usage (Student Account)

import asyncio
from ecoledirecte_py_client import Client

async def main():
    # Create client and login
    client = Client()
    session = await client.login("username", "password")
    
    # Fetch student data
    grades = await session.get_grades()
    homework = await session.get_homework()
    messages = await session.get_messages()
    
    print(f"Retrieved {len(grades)} grades")
    
    # Always close the client
    await client.close()

if __name__ == "__main__":
    asyncio.run(main())

Family Account with Multiple Students

from ecoledirecte_py_client import Client, Family

async def main():
    client = Client()
    session = await client.login("parent_username", "parent_password")
    
    # Check if it's a family account
    if isinstance(session, Family):
        print(f"Found {len(session.students)} students")
        
        # Access each student
        for student in session.students:
            print(f"\n--- Data for {student.name} ---")
            grades = await student.get_grades()
            homework = await student.get_homework()
            print(f"Grades: {len(grades)}")
    
    await client.close()

asyncio.run(main())

Handling MFA Challenges

The recommended way to handle MFA is using the mfa_callback parameter:

import asyncio
from ecoledirecte_py_client import Client

def mfa_callback(question: str, choices: list[str]) -> str:
    """Interactive MFA callback."""
    print(f"MFA Required: {question}")
    for idx, choice in enumerate(choices):
        print(f"  {idx}: {choice}")
    selection = int(input("Select option: "))
    return choices[selection]

async def main():
    # Pass the callback to handle MFA automatically
    client = Client(mfa_callback=mfa_callback)
    session = await client.login("username", "password")
    print("Login successful!")
    await client.close()

asyncio.run(main())

Alternatively, you can use the built-in console callback:

from ecoledirecte_py_client import Client, default_console_callback

client = Client(mfa_callback=default_console_callback)

Or handle MFA manually with try/except:

from ecoledirecte_py_client import Client, MFARequiredError

async def main():
    client = Client()
    
    try:
        session = await client.login("username", "password")
    except MFARequiredError as e:
        print(f"MFA Required: {e.question}")
        for idx, option in enumerate(e.propositions):
            print(f"  {idx}: {option}")
        
        choice = int(input("Select option: "))
        answer = e.propositions[choice]
        session = await client.submit_mfa(answer)
    
    await client.close()

asyncio.run(main())

Documentation

Configuration

Client Options

The Client constructor accepts the following optional parameters:

from ecoledirecte_py_client import Client

client = Client(
    device_file="device.json",   # Path to device token cache (None to disable)
    qcm_file="qcm.json",         # Path to MFA answer cache (None to disable)
    mfa_callback=my_callback,    # Callback for interactive MFA (see below)
)

Environment Variables

Create a .env file in your project root:

ECOLEDIRECTE_USERNAME=your_username
ECOLEDIRECTE_PASSWORD=your_password

Then load it in your application:

from dotenv import load_dotenv
import os

load_dotenv()

username = os.getenv("ECOLEDIRECTE_USERNAME")
password = os.getenv("ECOLEDIRECTE_PASSWORD")

MFA Configuration

The library automatically manages MFA answers in a qcm.json file. When you successfully answer an MFA challenge, it's cached for future logins:

{
  "Quelle est votre ville de rรฉsidence ?": ["PARIS"],
  "Quel est le niveau scolaire de <prรฉnom> ?": ["QUATRIEMES"]
}

Device tokens are cached in device.json to avoid repeated MFA challenges on trusted devices.

See docs/mfa.md for detailed MFA handling strategies.

Available Methods

Student Class

# Retrieve grades (all or by quarter)
grades = await student.get_grades()
grades_q1 = await student.get_grades(quarter=1)

# Get homework assignments
homework = await student.get_homework()
homework_sorted = await student.get_homework(sort_by_due_date=True)

# Fetch schedule for date range
schedule = await student.get_schedule("2024-01-01", "2024-01-31")
schedule = await student.get_schedule("2024-01-01", "2024-01-31", sort_by_date=False)

# Access messages
messages = await student.get_messages()  # received by default
messages = await student.get_messages(message_type="sent")
messages = await student.get_messages(message_type="all")

Family Class

# Access list of students
for student in family.students:
    print(student.name, student.id)
    
# Each student has the same methods as Student class
grades = await family.students[0].get_grades()

Examples

Check the examples/ directory for complete, runnable examples:

# Run the demo with your credentials
uv run --env-file .env examples/demo.py

Project Structure

ecoledirecte-py-client/
โ”œโ”€โ”€ src/ecoledirecte_py_client/  # Core library code
โ”‚   โ”œโ”€โ”€ client.py                # Main Client class
โ”‚   โ”œโ”€โ”€ student.py               # Student account methods
โ”‚   โ”œโ”€โ”€ family.py                # Family account methods
โ”‚   โ”œโ”€โ”€ mfa.py                   # MFA handling utilities
โ”‚   โ”œโ”€โ”€ exceptions.py            # Custom exceptions
โ”‚   โ”œโ”€โ”€ models/                  # Pydantic models
โ”‚   โ”‚   โ”œโ”€โ”€ auth.py
โ”‚   โ”‚   โ”œโ”€โ”€ grades.py
โ”‚   โ”‚   โ”œโ”€โ”€ homework.py
โ”‚   โ”‚   โ”œโ”€โ”€ messages.py
โ”‚   โ”‚   โ””โ”€โ”€ schedule.py
โ”‚   โ””โ”€โ”€ managers/                # API managers
โ”‚       โ”œโ”€โ”€ grades_manager.py
โ”‚       โ”œโ”€โ”€ homework_manager.py
โ”‚       โ”œโ”€โ”€ messages_manager.py
โ”‚       โ””โ”€โ”€ schedule_manager.py
โ”œโ”€โ”€ examples/                    # Usage examples
โ”‚   โ””โ”€โ”€ demo.py                  # Complete demo script
โ”œโ”€โ”€ tests/                       # Test suite
โ”œโ”€โ”€ docs/                        # Documentation
โ””โ”€โ”€ qcm.json.example            # Example MFA cache file

Development

Setting Up Development Environment

# Clone the repository
git clone https://github.com/ngombert/ecoledirecte-py-client.git
cd ecoledirecte-py-client

# Install dependencies with uv
uv sync

# Run tests
uv run pytest

# Run tests with coverage
uv run pytest --cov=src/ecoledirecte_py_client

See docs/contributing.md for detailed contribution guidelines.

Important Notes

  • Rate Limiting: Be respectful of the EcoleDirecte API. Avoid excessive requests.
  • Credentials Security: Never commit .env files or qcm.json with real data to version control.
  • MFA Answers: The qcm.json file contains answers specific to your account. Keep it private.
  • Unofficial API: This library uses an unofficial API that may change without notice.

License

This project is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). See the LICENSE file for details.

Contributing

Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.

Support

Acknowledgments

This library is inspired by and references other EcoleDirecte API implementations. Special thanks to the EcoleDirecte community for reverse-engineering efforts.


Disclaimer: This is an unofficial client library and is not affiliated with or endorsed by EcoleDirecte. Use at your own risk.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ecoledirecte_py_client-0.2.1.tar.gz (104.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ecoledirecte_py_client-0.2.1-py3-none-any.whl (41.7 kB view details)

Uploaded Python 3

File details

Details for the file ecoledirecte_py_client-0.2.1.tar.gz.

File metadata

  • Download URL: ecoledirecte_py_client-0.2.1.tar.gz
  • Upload date:
  • Size: 104.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ecoledirecte_py_client-0.2.1.tar.gz
Algorithm Hash digest
SHA256 2eab44279c9eaea2c7be85e52561cd91906c2de6aabd49638f786b673c1d6eeb
MD5 785318dbed3615252fb28a524c37f566
BLAKE2b-256 6e91d2cf54d0e135bcef7d7d343fb5f18e7349eb8ea9d3b528b168bcf01cdad8

See more details on using hashes here.

File details

Details for the file ecoledirecte_py_client-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for ecoledirecte_py_client-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 984695996b9597696fc83f0a91accc100ad0d2d9675386a1e7cc634596ef69d2
MD5 4c57f25c61026c8ff9dc268ba922fbc8
BLAKE2b-256 99c41c885d8a736075b17177684017fb653f773ee1aca79c0604eed9c135ede6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page