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
32 changes: 30 additions & 2 deletions futurex_openedx_extensions/helpers/signals.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Signals for the futurex_openedx_extensions app"""
from __future__ import annotations

import logging
from typing import Any

from common.djangoapps.student.models import CourseAccessRole
from common.djangoapps.student.models import CourseAccessRole, UserProfile
from django.core.cache import cache
from django.db.models.signals import post_delete, post_save
from django.db.models.signals import post_delete, post_save, pre_save
from django.dispatch import receiver

from futurex_openedx_extensions.helpers import constants as cs
Expand All @@ -17,6 +18,8 @@
)
from futurex_openedx_extensions.helpers.tenants import get_all_tenant_ids, get_all_tenants_info

logger = logging.getLogger(__name__)


@receiver(post_save, sender=CourseAccessRole)
def refresh_course_access_role_cache_on_save(
Expand Down Expand Up @@ -90,3 +93,28 @@ def refresh_tenant_info_cache_on_delete_template_asset(
template_tenant_id = get_all_tenants_info()['template_tenant']['tenant_id']
if template_tenant_id and instance.tenant_id == template_tenant_id:
invalidate_cache()


@receiver(pre_save, sender=UserProfile)
def fix_profile_gender_issues(
sender: Any, instance: UserProfile, **kwargs: Any, # pylint: disable=unused-argument
) -> None:
"""Receiver to fix profile.gender values when a user profile is saved"""
values_map = {
'male': 'm',
'female': 'f',
}
if instance.gender not in ['m', 'f', '']:
user_id_str = f'id: {str(instance.user_id)}' if instance.user_id else f'username: {instance.username}'
try:
original_value = instance.gender
new_value = values_map.get(original_value.lower(), '')
instance.gender = new_value
logging.warning(
'UserProfile.gender updated for user %s from "%s" to "%s"',
user_id_str,
original_value,
new_value,
)
except Exception as exc:
logger.error('Error updating gender for user %s: %s', user_id_str, str(exc))
49 changes: 48 additions & 1 deletion tests/test_helpers/test_signals.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Tests for the signals module of the helpers app"""

import logging
from unittest.mock import patch

import pytest
from common.djangoapps.student.models import CourseAccessRole
from common.djangoapps.student.models import CourseAccessRole, UserProfile
from django.core.cache import cache

from futurex_openedx_extensions.helpers import constants as cs
Expand Down Expand Up @@ -166,3 +168,48 @@ def test_refresh_tenant_info_cache_on_delete_template_asset(
mock_invalidate.assert_called_once()
else:
mock_invalidate.assert_not_called()


@pytest.mark.django_db
@pytest.mark.parametrize(
'original, expected, expect_warning',
[
('male', 'm', True),
('Male', 'm', True),
('female', 'f', True),
('FEMALE', 'f', True),
('m', 'm', False),
('f', 'f', False),
('', '', False),
('M', '', True),
('other', '', True),
],
)
def test_fix_profile_gender_issues_on_create(original, expected, expect_warning, caplog):
"""Verify that creating a UserProfile normalizes gender and logs when changed."""
caplog.set_level(logging.WARNING)
user_id = 10
profile = UserProfile.objects.create(user_id=user_id, gender=original)
profile.refresh_from_db()
assert profile.gender == expected

if expect_warning:
assert any(
(str(original) in rec.getMessage() and str(expected) in rec.getMessage())
for rec in caplog.records
)
else:
assert not any('UserProfile.gender updated for user' in rec.getMessage() for rec in caplog.records)


@pytest.mark.django_db
def test_fix_profile_gender_issues_on_update(caplog):
"""Verify that updating an existing UserProfile triggers normalization on save."""
caplog.set_level(logging.WARNING)
user_id = 11
profile = UserProfile.objects.create(user_id=user_id, gender='')
profile.gender = 'Male'
profile.save()
profile.refresh_from_db()
assert profile.gender == 'm'
assert any('UserProfile.gender updated for user' in rec.getMessage() for rec in caplog.records)