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
18 changes: 18 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,21 @@ The project uses Docker Compose for local development with services:
- Test configuration uses separate settings (`pycon.settings.test`)
- Ruff handles both linting and formatting for Python code
- Biome handles linting and formatting for JavaScript/TypeScript

## Local Development

**IMPORTANT**: When running locally, all Python/Django commands must run inside Docker. The local virtual environment will not work.

Use `docker exec pycon-backend-1` (without `-t` flag for non-interactive/script usage, with `-it` for interactive terminal).

- **Start services**: `docker-compose up` (starts all services)
- **Run tests**: `docker exec pycon-backend-1 uv run pytest -l -s -vvv`
- **Single test**: `docker exec pycon-backend-1 uv run pytest path/to/test_file.py::test_function -l -s -vvv`
- **Lint/format**: `docker exec pycon-backend-1 uv run ruff check` and `docker exec pycon-backend-1 uv run ruff format`
- **Type checking**: `docker exec pycon-backend-1 uv run mypy .`
- **Django management**: `docker exec pycon-backend-1 uv run python manage.py <command>`
- **Migrations**: `docker exec pycon-backend-1 uv run python manage.py makemigrations` and `docker exec pycon-backend-1 uv run python manage.py migrate`

**Troubleshooting**: If the backend container is not working:
1. Restart container: `docker restart pycon-backend-1`
2. If dependencies changed: Remove `backend/.venv` and rebuild with `docker-compose build --no-cache && docker-compose up`
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 5.1.4 on 2026-01-28

from django.db import migrations, models


def convert_custom_deadlines_to_grants_waiting_list_update(apps, schema_editor):
"""
Convert existing custom deadlines with name containing
'Update Grants in Waiting List' to the new grants_waiting_list_update type.
"""
Deadline = apps.get_model("conferences", "Deadline")

# Update custom deadlines that have "Update Grants in Waiting List" in their name
Deadline.objects.filter(
type="custom", name__contains={"en": "Update Grants in Waiting List"}
).update(type="grants_waiting_list_update")


def revert_grants_waiting_list_update_to_custom(apps, schema_editor):
"""
Revert grants_waiting_list_update deadlines back to custom type.
"""
Deadline = apps.get_model("conferences", "Deadline")
Deadline.objects.filter(type="grants_waiting_list_update").update(type="custom")


class Migration(migrations.Migration):

dependencies = [
("conferences", "0056_conference_max_proposals"),
]

operations = [
migrations.AlterField(
model_name="deadline",
name="type",
field=models.CharField(
choices=[
("cfp", "Call for proposal"),
("voting", "Voting"),
("refund", "Ticket refund"),
("grants", "Grants"),
("badge_preview", "Badge preview"),
("invitation_letter_request", "Invitation letter request"),
("grants_waiting_list_update", "Grants waiting list update"),
("custom", "Custom deadline"),
],
max_length=256,
verbose_name="type",
),
),
migrations.RunPython(
convert_custom_deadlines_to_grants_waiting_list_update,
revert_grants_waiting_list_update_to_custom,
),
]
1 change: 1 addition & 0 deletions backend/conferences/models/deadline.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Deadline(TimeFramedModel):
("grants", _("Grants")),
("badge_preview", _("Badge preview")),
("invitation_letter_request", _("Invitation letter request")),
("grants_waiting_list_update", _("Grants waiting list update")),
("custom", _("Custom deadline")),
)

Expand Down
10 changes: 9 additions & 1 deletion backend/grants/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,17 @@ def _send_grant_waiting_list_email(grant_id, template_identifier):
reply_url = urljoin(settings.FRONTEND_URL, "/grants/reply/")

deadline = grant.conference.deadlines.filter(
type="custom", name__contains={"en": "Update Grants in Waiting List"}
type="grants_waiting_list_update"
).first()

if not deadline:
logger.error(
f"No grants_waiting_list_update deadline found for conference {grant.conference}"
)
raise ValueError(
f"Conference {grant.conference.code} missing grants_waiting_list_update deadline"
)

_new_send_grant_email(
template_identifier=template_identifier,
grant=grant,
Expand Down
20 changes: 10 additions & 10 deletions backend/grants/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,7 @@ def test_send_grant_reply_waiting_list_email(settings, sent_emails):
DeadlineFactory(
start=datetime(2023, 3, 1, 23, 59, tzinfo=timezone.utc),
conference=conference,
type="custom",
name={
"en": "Update Grants in Waiting List",
"it": "Update Grants in Waiting List",
},
type="grants_waiting_list_update",
)
grant = GrantFactory(conference=conference, user=user)

Expand Down Expand Up @@ -432,11 +428,7 @@ def test_send_grant_reply_waiting_list_update_email(settings, sent_emails):
DeadlineFactory(
conference=grant.conference,
start=datetime(2023, 3, 1, 23, 59, tzinfo=timezone.utc),
type="custom",
name={
"en": "Update Grants in Waiting List",
"it": "Update Grants in Waiting List",
},
type="grants_waiting_list_update",
)
conference_name = grant.conference.name.localize("en")

Expand Down Expand Up @@ -466,3 +458,11 @@ def test_send_grant_reply_waiting_list_update_email(settings, sent_emails):
assert sent_email.placeholders["conference_name"] == conference_name
assert sent_email.placeholders["grants_update_deadline"] == "1 March 2023"
assert sent_email.placeholders["reply_url"] == "https://pycon.it/grants/reply/"


def test_send_grant_waiting_list_email_missing_deadline():
grant = GrantFactory()
# No deadline created

with pytest.raises(ValueError, match="missing grants_waiting_list_update deadline"):
send_grant_reply_waiting_list_email(grant_id=grant.id)
Loading