Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:

defaults:
run:
working-directory: "./backend"
working-directory: "./platform-plugin-sample"

jobs:
run_tests:
Expand Down Expand Up @@ -46,4 +46,4 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
fail_ci_if_error: true
working-directory: "./backend"
working-directory: "./platform-plugin-sample"
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,22 @@ jobs:
git_committer_name: "github-actions"
git_committer_email: "actions@users.noreply.github.com"
changelog: "false"
directory: './backend'
directory: './platform-plugin-sample'

- name: Publish | Upload to GitHub Release Assets
uses: python-semantic-release/publish-action@v10.5.3
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.OPENEDX_SEMANTIC_RELEASE_GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}
directory: './backend'
directory: './platform-plugin-sample'

- name: Upload | Backend Distribution Artifacts
uses: actions/upload-artifact@v4
if: steps.release.outputs.released == 'true'
with:
name: backend-distribution-artifacts
path: backend/dist
path: platform-plugin-sample/dist
if-no-files-found: error

- name: Build | Tutor Plugin
Expand Down Expand Up @@ -102,12 +102,12 @@ jobs:
id: artifact-download
with:
name: backend-distribution-artifacts
path: backend/dist
path: platform-plugin-sample/dist

- name: Publish to PyPi
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: backend/dist
packages-dir: platform-plugin-sample/dist
user: __token__
password: ${{ secrets.PYPI_UPLOAD_TOKEN }}

Expand Down
24 changes: 12 additions & 12 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This is a **sample plugin repository** that demonstrates all major Open edX plug
- **Target Audience**: Developers new to Open edX plugin development

**Repository Structure:**
- `backend/` - Django app plugin with models, APIs, events, and filters
- `platform-plugin-sample/` - Django app plugin with models, APIs, events, and filters
- `frontend/` - React component for MFE slot customization
- `tutor/` - Tutor plugin for easy deployment
- Each directory has comprehensive README.md files with TOCs
Expand All @@ -25,21 +25,21 @@ This is a **sample plugin repository** that demonstrates all major Open edX plug
- Keep examples realistic but not overly complex

**Key Files and Their Relationships:**
- `backend/sample_plugin/apps.py` - Plugin registration and Django integration
- `backend/sample_plugin/signals.py` - Open edX Events handlers
- `backend/sample_plugin/pipeline.py` - Open edX Filters implementation
- `backend/sample_plugin/models.py` - CourseArchiveStatus model (business logic)
- `backend/sample_plugin/views.py` - REST API endpoints consumed by frontend
- `platform-plugin-sample/openedx_sample_plugin/apps.py` - Plugin registration and Django integration
- `platform-plugin-sample/openedx_sample_plugin/signals.py` - Open edX Events handlers
- `platform-plugin-sample/openedx_sample_plugin/pipeline.py` - Open edX Filters implementation
- `platform-plugin-sample/openedx_sample_plugin/models.py` - CourseArchiveStatus model (business logic)
- `platform-plugin-sample/openedx_sample_plugin/views.py` - REST API endpoints consumed by frontend
- `frontend/src/plugin.jsx` - React component that replaces course list slot
- `tutor/sample_plugin.py` - Deployment configuration (currently basic template)
- `tutor/openedx_sample_plugin.py` - Deployment configuration (currently basic template)

## Build/Lint/Test Commands
- Make sure to set the following so that test output is not too verbose: `export PYTEST_ADDOPTS="--disable-warnings --no-header --tb=short"`
- Backend testing: `cd backend && pytest` or `cd backend && make test`
- Run a single test: `cd backend && pytest tests/test_models.py::test_placeholder`
- Quality checks: `cd backend && make quality`
- Install requirements: `cd backend && make requirements`
- Compile requirements: `cd backend && make compile-requirements`
- Backend testing: `cd platform-plugin-sample && pytest` or `cd platform-plugin-sample && make test`
- Run a single test: `cd platform-plugin-sample && pytest tests/test_models.py::test_placeholder`
- Quality checks: `cd platform-plugin-sample && make quality`
- Install requirements: `cd platform-plugin-sample && make requirements`
- Compile requirements: `cd platform-plugin-sample && make compile-requirements`

## Code Style Guidelines
- Python: Follow PEP 8 with max line length of 120
Expand Down
38 changes: 21 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ This sample plugin showcases the **Open edX Hooks Extension Framework**, which a

| Plugin Type | What It Does | Official Documentation | Sample Code | When To Use |
|-------------|--------------|------------------------|-------------|-------------|
| **Django App Plugin** | Add models, APIs, views, and business logic | [How to create a plugin app](https://docs.openedx.org/projects/edx-django-utils/en/latest/plugins/how_tos/how_to_create_a_plugin_app.html) | [`backend/`](./backend/) | Adding new functionality, APIs, or data models |
| **Events (Signals)** | React to platform events | [Open edX Events Guide](https://docs.openedx.org/projects/openedx-events/en/latest/) | [`backend/sample_plugin/signals.py`](./backend/sample_plugin/signals.py) | Integrating with external systems, audit logging |
| **Filters** | Modify platform behavior | [Using Open edX Filters](https://docs.openedx.org/projects/openedx-filters/en/latest/how-tos/using-filters.html) | [`backend/sample_plugin/pipeline.py`](./backend/sample_plugin/pipeline.py) | Customizing business logic, URL redirects |
| **Django App Plugin** | Add models, APIs, views, and business logic | [How to create a plugin app](https://docs.openedx.org/projects/edx-django-utils/en/latest/plugins/how_tos/how_to_create_a_plugin_app.html) | [`platform-plugin-sample/`](./platform-plugin-sample/) | Adding new functionality, APIs, or data models |
| **Events (Signals)** | React to platform events | [Open edX Events Guide](https://docs.openedx.org/projects/openedx-events/en/latest/) | [`platform-plugin-sample/openedx_sample_plugin/signals.py`](./platform-plugin-sample/openedx_sample_plugin/signals.py) | Integrating with external systems, audit logging |
| **Filters** | Modify platform behavior | [Using Open edX Filters](https://docs.openedx.org/projects/openedx-filters/en/latest/how-tos/using-filters.html) | [`platform-plugin-sample/openedx_sample_plugin/pipeline.py`](./platform-plugin-sample/openedx_sample_plugin/pipeline.py) | Customizing business logic, URL redirects |
| **Frontend Slots** | Customize MFE interfaces | [Frontend Plugin Slots](https://docs.openedx.org/en/latest/site_ops/how-tos/use-frontend-plugin-slots.html) | [`frontend/`](./frontend/) | UI customization, adding new components |
| **Brand Packages** | Customize theming | [Open edX Brand Package Interface](https://github.com/openedx/brand-openedx) | [`brand/`](./brand/) | UI theming |
| **Tutor Plugin** | Deploy plugins easily | [Tutor Plugin Development](https://docs.tutor.edly.io/) | [`tutor/`](./tutor/) | Simplified deployment and configuration |
Expand All @@ -46,9 +46,13 @@ This sample plugin showcases the **Open edX Hooks Extension Framework**, which a
### Option 1: Development with Tutor (Recommended)

```bash
# Backend plugin setup
tutor mounts add "$PWD/backend"
tutor dev launch # Rebuilds image, runs migrations, reboots containers.
# Bind-mount backend source into Tutor image and containers.
# Tutor automatically recognizes the `platform-plugin-*` prefix and knows to
# treat it as an openedx-platform plugin.
tutor mounts add "$PWD/platform-plugin-sample"

# Rebuild image, run migrations, reboot containers:
tutor dev launch

# Frontend Plugin Setup (for learner-dashboard MFE development)
npm install $PWD/frontend
Expand All @@ -60,7 +64,7 @@ npm start

```bash
# In your edx-platform directory
pip install -e /path/to/sample-plugin/backend
pip install -e /path/to/sample-plugin/platform-plugin-sample

# Enable Learner Dashboard MFE
# Go to http://localhost:18000/admin/waffle/flag/
Expand All @@ -87,9 +91,9 @@ python manage.py lms migrate
Use the table above to identify which type of plugin matches your needs. You can combine multiple types in one plugin.

### 3. Study the Sample Code
- **Backend**: Start with [`backend/sample_plugin/apps.py`](./backend/sample_plugin/apps.py) to understand plugin registration
- **Events**: Examine [`backend/sample_plugin/signals.py`](./backend/sample_plugin/signals.py) for event handling patterns
- **Filters**: Review [`backend/sample_plugin/pipeline.py`](./backend/sample_plugin/pipeline.py) for behavior modification
- **Backend**: Start with [`platform-plugin-sample/openedx_sample_plugin/apps.py`](./platform-plugin-sample/openedx_sample_plugin/apps.py) to understand plugin registration
- **Events**: Examine [`platform-plugin-sample/openedx_sample_plugin/signals.py`](./platform-plugin-sample/openedx_sample_plugin/signals.py) for event handling patterns
- **Filters**: Review [`platform-plugin-sample/openedx_sample_plugin/pipeline.py`](./platform-plugin-sample/openedx_sample_plugin/pipeline.py) for behavior modification
- **Frontend**: Explore [`frontend/src/plugin.jsx`](./frontend/src/plugin.jsx) for UI customization

### 4. Run This Sample
Expand All @@ -103,9 +107,9 @@ Each directory contains detailed README.md files with adaptation guidance.
```
sample-plugin/
├── README.md # This file - overview and quick start
├── backend/
├── platform-plugin-sample/
│ ├── README.md # Backend plugin detailed guide
│ ├── sample_plugin/
│ ├── openedx_sample_plugin/
│ │ ├── apps.py # Django plugin configuration
│ │ ├── models.py # Database models example
│ │ ├── views.py # REST API endpoints
Expand All @@ -122,7 +126,7 @@ sample-plugin/
│ └── package.json # NPM package configuration
└── tutor/
├── README.md # Tutor deployment guide
└── sample_plugin.py # Tutor plugin configuration
└── openedx_sample_plugin.py # Tutor plugin configuration
```

## Development Workflows
Expand All @@ -135,10 +139,10 @@ sample-plugin/
- Add API endpoints in `views.py`
- Implement event handlers in `signals.py`
- Create filters in `pipeline.py`
3. **Testing**: `cd backend && make test`
4. **Quality**: `cd backend && make quality`
3. **Testing**: `cd platform-plugin-sample && make test`
4. **Quality**: `cd platform-plugin-sample && make quality`

**Detailed Guide**: See [`backend/README.md`](./backend/README.md)
**Detailed Guide**: See [`platform-plugin-sample/README.md`](./platform-plugin-sample/README.md)

### Frontend Plugin Development

Expand All @@ -164,7 +168,7 @@ This sample shows how backend and frontend plugins work together:
### Backend + Frontend Integration

```python
# backend/sample_plugin/views.py - Provides API
# platform-plugin-sample/openedx_sample_plugin/views.py - Provides API
class CourseArchiveStatusViewSet(viewsets.ModelViewSet):
# API implementation
```
Expand Down
2 changes: 1 addition & 1 deletion frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ if (response.data && Array.isArray(response.data)) {
### Prerequisites

1. **MFE Setup**: Have a learner dashboard MFE running locally
2. **Backend Plugin**: Install the backend plugin (see [`../backend/README.md`](../backend/README.md))
2. **Backend Plugin**: Install the backend plugin (see [`../platform-plugin-sample/README.md`](../platform-plugin-sample/README.md))
3. **Node.js**: Version 16+ with npm or yarn

### Local Development Setup
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
58 changes: 29 additions & 29 deletions backend/README.md → platform-plugin-sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ This backend plugin demonstrates the **Open edX Django App Plugin** pattern, whi

## Django App Plugin Configuration

**File**: [`sample_plugin/apps.py`](./sample_plugin/apps.py)
**File**: [`openedx_sample_plugin/apps.py`](./openedx_sample_plugin/apps.py)

### Plugin Registration

The `SamplePluginConfig` class configures this app as an edx-platform plugin:

```python
class SamplePluginConfig(AppConfig):
name = "sample_plugin"
name = "openedx_sample_plugin"
plugin_app = {
"url_config": {
# Register URLs for both LMS and CMS
"lms.djangoapp": {
PluginURLs.NAMESPACE: "sample_plugin",
PluginURLs.NAMESPACE: "openedx_sample_plugin",
PluginURLs.REGEX: r"^sample-plugin/",
PluginURLs.RELATIVE_PATH: "urls",
},
Expand All @@ -74,21 +74,21 @@ class SamplePluginConfig(AppConfig):

### Entry Points Configuration

In [`pyproject.toml`](./backend/pyproject.toml), the plugin registers itself with edx-platform:
In [`pyproject.toml`](./pyproject.toml), the plugin registers itself with edx-platform:

```python
[project.entry-points."lms.djangoapp"]
sample_plugin = "sample_plugin.apps:SamplePluginConfig"
openedx_sample_plugin = "openedx_sample_plugin.apps:SamplePluginConfig"

[project.entry-points."cms.djangoapp"]
sample_plugin = "sample_plugin.apps:SamplePluginConfig"
openedx_sample_plugin = "openedx_sample_plugin.apps:SamplePluginConfig"
```

**Why this works**: The platform automatically discovers and loads any Django app registered in these entry points.

## Models & Database

**File**: [`sample_plugin/models.py`](./sample_plugin/models.py)
**File**: [`openedx_sample_plugin/models.py`](./openedx_sample_plugin/models.py)
**Official Docs**: [OEP-49: Django App Patterns](https://docs.openedx.org/projects/openedx-proposals/en/latest/best-practices/oep-0049-django-app-patterns.html)

### CourseArchiveStatus Model
Expand All @@ -112,12 +112,12 @@ class CourseArchiveStatus(models.Model):

```bash
# After modifying models.py
cd backend
python manage.py makemigrations sample_plugin
cd platform-plugin-sample
python manage.py makemigrations openedx_sample_plugin
python manage.py migrate
```

**Migration files**: Generated in [`sample_plugin/migrations/`](./sample_plugin/migrations/)
**Migration files**: Generated in [`openedx_sample_plugin/migrations/`](./openedx_sample_plugin/migrations/)

### PII Annotations

Expand All @@ -130,8 +130,8 @@ The model includes PII documentation:

## API Endpoints

**File**: [`sample_plugin/views.py`](./sample_plugin/views.py)
**URLs**: [`sample_plugin/urls.py`](./sample_plugin/urls.py)
**File**: [`openedx_sample_plugin/views.py`](./openedx_sample_plugin/views.py)
**URLs**: [`openedx_sample_plugin/urls.py`](./openedx_sample_plugin/urls.py)

### REST API Implementation

Expand Down Expand Up @@ -179,7 +179,7 @@ def perform_create(self, serializer):

## Events & Signals

**File**: [`sample_plugin/signals.py`](./sample_plugin/signals.py)
**File**: [`openedx_sample_plugin/signals.py`](./openedx_sample_plugin/signals.py)
**Official Docs**: [Open edX Events Guide](https://docs.openedx.org/projects/openedx-events/en/latest/)

### Event Handler Example
Expand Down Expand Up @@ -221,7 +221,7 @@ def log_course_info_changed(signal, sender, catalog_info: CourseCatalogData, **k

### Signal Handler Registration

Handlers are automatically registered via the `ready()` method in [`apps.py`](./sample_plugin/apps.py):
Handlers are automatically registered via the `ready()` method in [`apps.py`](./openedx_sample_plugin/apps.py):

```python
def ready(self):
Expand All @@ -238,7 +238,7 @@ def ready(self):

## Filters & Pipeline Steps

**File**: [`sample_plugin/pipeline.py`](./sample_plugin/pipeline.py)
**File**: [`openedx_sample_plugin/pipeline.py`](./openedx_sample_plugin/pipeline.py)
**Official Docs**: [Using Open edX Filters](https://docs.openedx.org/projects/openedx-filters/en/latest/how-tos/using-filters.html)

### Filter Implementation
Expand Down Expand Up @@ -292,7 +292,7 @@ Filters must be registered in Django settings. This happens automatically via th

## Settings Configuration

**Files**: [`sample_plugin/settings/`](./sample_plugin/settings/)
**Files**: [`openedx_sample_plugin/settings/`](./openedx_sample_plugin/settings/)

### Settings Structure

Expand Down Expand Up @@ -321,7 +321,7 @@ def plugin_settings(settings):
settings.OPEN_EDX_FILTERS_CONFIG = {
"org.openedx.learning.course.about.render.started.v1": {
"pipeline": [
"sample_plugin.pipeline.ChangeCourseAboutPageUrl"
"openedx_sample_plugin.pipeline.ChangeCourseAboutPageUrl"
],
"fail_silently": False,
}
Expand Down Expand Up @@ -368,7 +368,7 @@ tutor dev restart lms

```bash
# In your edx-platform directory
pip install -e /path/to/sample-plugin/backend
pip install -e /path/to/sample-plugin/platform-plugin-sample

# Run migrations
python manage.py lms migrate
Expand All @@ -380,7 +380,7 @@ python manage.py cms migrate
1. **Check Installation**:
```bash
python manage.py lms shell
>>> from sample_plugin.models import CourseArchiveStatus
>>> from openedx_sample_plugin.models import CourseArchiveStatus
>>> print("Plugin installed successfully!")
```

Expand All @@ -393,7 +393,7 @@ python manage.py cms migrate
### Running Tests

```bash
cd backend
cd platform-plugin-sample

# Install test dependencies
make requirements
Expand All @@ -420,7 +420,7 @@ make test-coverage
**Model Testing Pattern:**
```python
from django.test import TestCase
from sample_plugin.models import CourseArchiveStatus
from openedx_sample_plugin.models import CourseArchiveStatus

class TestCourseArchiveStatus(TestCase):
def test_create_archive_status(self):
Expand Down Expand Up @@ -449,9 +449,9 @@ class TestCourseArchiveStatusAPI(APITestCase):
make quality

# Individual tools
pylint sample_plugin/
isort --check-only sample_plugin/
black --check sample_plugin/
pylint openedx_sample_plugin/
isort --check-only openedx_sample_plugin/
black --check openedx_sample_plugin/
```

## Integration Examples
Expand Down Expand Up @@ -502,11 +502,11 @@ class ChangeCourseAboutPageUrl(PipelineStep):

### For Your Use Case

1. **Models**: Modify [`models.py`](./sample_plugin/models.py) for your data structure
2. **APIs**: Update [`views.py`](./sample_plugin/views.py) and [`serializers.py`](./sample_plugin/serializers.py)
3. **Events**: Change event handlers in [`signals.py`](./sample_plugin/signals.py)
4. **Filters**: Implement your business logic in [`pipeline.py`](./sample_plugin/pipeline.py)
5. **Settings**: Configure plugin behavior in [`settings/`](./sample_plugin/settings/)
1. **Models**: Modify [`models.py`](./openedx_sample_plugin/models.py) for your data structure
2. **APIs**: Update [`views.py`](./openedx_sample_plugin/views.py) and [`serializers.py`](./openedx_sample_plugin/serializers.py)
3. **Events**: Change event handlers in [`signals.py`](./openedx_sample_plugin/signals.py)
4. **Filters**: Implement your business logic in [`pipeline.py`](./openedx_sample_plugin/pipeline.py)
5. **Settings**: Configure plugin behavior in [`settings/`](./openedx_sample_plugin/settings/)

### Plugin Development Checklist

Expand Down
File renamed without changes.
File renamed without changes.
Loading
Loading