Skip to content
Merged
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
21 changes: 21 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build/Lint/Test Commands
- 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`

## Code Style Guidelines
- Python: Follow PEP 8 with max line length of 120
- Use isort for import sorting
- Document classes and functions with docstrings
- Type hints are encouraged but not required
- Error handling should use appropriate exceptions with descriptive messages
- Use pytest for testing, with descriptive test function names
- Frontend uses React and follows standard JSX conventions

Always run `make quality` before creating a PR to ensure consistent code style.
4 changes: 3 additions & 1 deletion backend/requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
-c constraints.txt

Django # Web application framework

djangorestframework # REST API framework
django-filter # Filtering for DRF
edx-opaque-keys # Open edX CourseKeyField

openedx-atlas
21 changes: 21 additions & 0 deletions backend/requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,28 @@ django==4.2.20
# via
# -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt
# -r requirements/base.in
# django-filter
# djangorestframework
django-filter==25.1
# via -r requirements/base.in
djangorestframework==3.16.0
# via -r requirements/base.in
dnspython==2.7.0
# via pymongo
edx-opaque-keys==2.12.0
# via -r requirements/base.in
openedx-atlas==0.7.0
# via -r requirements/base.in
pbr==6.1.1
# via stevedore
pymongo==4.12.0
# via edx-opaque-keys
sqlparse==0.5.3
# via django
stevedore==5.4.1
# via edx-opaque-keys
typing-extensions==4.13.2
# via edx-opaque-keys

# The following packages are considered to be unsafe in a requirements file:
# setuptools
21 changes: 21 additions & 0 deletions backend/requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,23 @@ django==4.2.20
# via
# -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt
# -r requirements/quality.txt
# django-filter
# djangorestframework
# edx-i18n-tools
django-filter==25.1
# via -r requirements/quality.txt
djangorestframework==3.16.0
# via -r requirements/quality.txt
dnspython==2.7.0
# via
# -r requirements/quality.txt
# pymongo
edx-i18n-tools==1.7.0
# via -r requirements/dev.in
edx-lint==5.6.0
# via -r requirements/quality.txt
edx-opaque-keys==2.12.0
# via -r requirements/quality.txt
filelock==3.18.0
# via
# -r requirements/ci.txt
Expand Down Expand Up @@ -162,6 +174,10 @@ pylint-plugin-utils==0.8.2
# -r requirements/quality.txt
# pylint-celery
# pylint-django
pymongo==4.12.0
# via
# -r requirements/quality.txt
# edx-opaque-keys
pyproject-api==1.9.0
# via
# -r requirements/ci.txt
Expand Down Expand Up @@ -205,6 +221,7 @@ stevedore==5.4.1
# via
# -r requirements/quality.txt
# code-annotations
# edx-opaque-keys
text-unidecode==1.3
# via
# -r requirements/quality.txt
Expand All @@ -215,6 +232,10 @@ tomlkit==0.13.2
# pylint
tox==4.25.0
# via -r requirements/ci.txt
typing-extensions==4.13.2
# via
# -r requirements/quality.txt
# edx-opaque-keys
virtualenv==20.30.0
# via
# -r requirements/ci.txt
Expand Down
19 changes: 19 additions & 0 deletions backend/requirements/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ django==4.2.20
# via
# -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt
# -r requirements/test.txt
# django-filter
# djangorestframework
django-filter==25.1
# via -r requirements/test.txt
djangorestframework==3.16.0
# via -r requirements/test.txt
dnspython==2.7.0
# via
# -r requirements/test.txt
# pymongo
doc8==1.1.2
# via -r requirements/doc.in
docutils==0.21.2
Expand All @@ -51,6 +61,8 @@ docutils==0.21.2
# readme-renderer
# restructuredtext-lint
# sphinx
edx-opaque-keys==2.12.0
# via -r requirements/test.txt
id==1.5.0
# via twine
idna==3.10
Expand Down Expand Up @@ -122,6 +134,10 @@ pygments==2.19.1
# readme-renderer
# rich
# sphinx
pymongo==4.12.0
# via
# -r requirements/test.txt
# edx-opaque-keys
pyproject-hooks==1.2.0
# via build
pytest==8.3.5
Expand Down Expand Up @@ -193,6 +209,7 @@ stevedore==5.4.1
# -r requirements/test.txt
# code-annotations
# doc8
# edx-opaque-keys
text-unidecode==1.3
# via
# -r requirements/test.txt
Expand All @@ -201,7 +218,9 @@ twine==6.1.0
# via -r requirements/doc.in
typing-extensions==4.13.2
# via
# -r requirements/test.txt
# beautifulsoup4
# edx-opaque-keys
# pydata-sphinx-theme
urllib3==2.2.3
# via
Expand Down
21 changes: 21 additions & 0 deletions backend/requirements/quality.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,20 @@ django==4.2.20
# via
# -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt
# -r requirements/test.txt
# django-filter
# djangorestframework
django-filter==25.1
# via -r requirements/test.txt
djangorestframework==3.16.0
# via -r requirements/test.txt
dnspython==2.7.0
# via
# -r requirements/test.txt
# pymongo
edx-lint==5.6.0
# via -r requirements/quality.in
edx-opaque-keys==2.12.0
# via -r requirements/test.txt
iniconfig==2.1.0
# via
# -r requirements/test.txt
Expand Down Expand Up @@ -88,6 +100,10 @@ pylint-plugin-utils==0.8.2
# via
# pylint-celery
# pylint-django
pymongo==4.12.0
# via
# -r requirements/test.txt
# edx-opaque-keys
pytest==8.3.5
# via
# -r requirements/test.txt
Expand Down Expand Up @@ -117,12 +133,17 @@ stevedore==5.4.1
# via
# -r requirements/test.txt
# code-annotations
# edx-opaque-keys
text-unidecode==1.3
# via
# -r requirements/test.txt
# python-slugify
tomlkit==0.13.2
# via pylint
typing-extensions==4.13.2
# via
# -r requirements/test.txt
# edx-opaque-keys

# The following packages are considered to be unsafe in a requirements file:
# setuptools
29 changes: 27 additions & 2 deletions backend/requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ coverage[toml]==7.8.0
# via
# -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt
# -r requirements/base.txt
# django-filter
# djangorestframework
django-filter==25.1
# via -r requirements/base.txt
djangorestframework==3.16.0
# via -r requirements/base.txt
dnspython==2.7.0
# via
# -r requirements/base.txt
# pymongo
edx-opaque-keys==2.12.0
# via -r requirements/base.txt
iniconfig==2.1.0
# via pytest
jinja2==3.1.6
Expand All @@ -28,9 +40,15 @@ openedx-atlas==0.7.0
packaging==24.2
# via pytest
pbr==6.1.1
# via stevedore
# via
# -r requirements/base.txt
# stevedore
pluggy==1.5.0
# via pytest
pymongo==4.12.0
# via
# -r requirements/base.txt
# edx-opaque-keys
pytest==8.3.5
# via
# pytest-cov
Expand All @@ -48,9 +66,16 @@ sqlparse==0.5.3
# -r requirements/base.txt
# django
stevedore==5.4.1
# via code-annotations
# via
# -r requirements/base.txt
# code-annotations
# edx-opaque-keys
text-unidecode==1.3
# via python-slugify
typing-extensions==4.13.2
# via
# -r requirements/base.txt
# edx-opaque-keys

# The following packages are considered to be unsafe in a requirements file:
# setuptools
64 changes: 64 additions & 0 deletions backend/sample_plugin/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,67 @@
"""
Database models for sample_plugin.
"""
from django.contrib.auth import get_user_model
from django.db import models
from opaque_keys.edx.django.models import CourseKeyField


class CourseArchiveStatus(models.Model):
"""
Model to track the archive status of a course.

Stores information about whether a course has been archived and when it was archived.

.. no_pii: This model does not store PII directly, only references to users via foreign keys.
"""

course_id = CourseKeyField(
max_length=255,
db_index=True,
help_text="The unique identifier for the course."
)

user = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
related_name="course_archive_statuses",
help_text="The user who this archive status is for."
)

is_archived = models.BooleanField(
default=False,
db_index=True, # Add index for performance on this frequently filtered field
help_text="Whether the course is archived."
)

archive_date = models.DateTimeField(
null=True,
blank=True,
help_text="The date and time when the course was archived."
)

created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
"""
Return a string representation of the course archive status.
"""
# pylint: disable=no-member
return f"{self.course_id} - {self.user.username} - {'Archived' if self.is_archived else 'Not Archived'}"

class Meta:
"""
Meta options for the CourseArchiveStatus model.
"""

verbose_name = "Course Archive Status"
verbose_name_plural = "Course Archive Statuses"
ordering = ["-updated_at"]
# Ensure combination of course_id and user is unique
constraints = [
models.UniqueConstraint(
fields=['course_id', 'user'],
name='unique_user_course_archive_status'
)
]
29 changes: 29 additions & 0 deletions backend/sample_plugin/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Serializers for the sample_plugin app.
"""
from rest_framework import serializers

from sample_plugin.models import CourseArchiveStatus


class CourseArchiveStatusSerializer(serializers.ModelSerializer):
"""
Serializer for the CourseArchiveStatus model.
"""

class Meta:
"""
Meta class for CourseArchiveStatusSerializer.
"""

model = CourseArchiveStatus
fields = [
'id',
'course_id',
'user',
'is_archived',
'archive_date',
'created_at',
'updated_at',
]
read_only_fields = ['id', 'created_at', 'updated_at', 'archive_date']
14 changes: 10 additions & 4 deletions backend/sample_plugin/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
"""
URLs for sample_plugin.
"""
from django.urls import re_path # pylint: disable=unused-import
from django.views.generic import TemplateView # pylint: disable=unused-import
from django.urls import include, path
from rest_framework.routers import DefaultRouter

from sample_plugin.views import CourseArchiveStatusViewSet

# Create a router and register our viewsets with it
router = DefaultRouter()
router.register(r'course-archive-status', CourseArchiveStatusViewSet, basename='course-archive-status')

# The API URLs are now determined automatically by the router
urlpatterns = [
# TODO: Fill in URL patterns and views here.
# re_path(r'', TemplateView.as_view(template_name="sample_plugin/base.html")),
path('api/v1/', include(router.urls)),
]
Loading