Skip to content

Commit

Permalink
feat: Added management command to update UIDs
Browse files Browse the repository at this point in the history
  • Loading branch information
zamanafzal committed Dec 11, 2024
1 parent 1ed53c2 commit 1de298a
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ Unreleased
----------
* nothing unreleased

[5.4.2]
--------
* feat: Added a management command to update the Social Auth UID's for an enterprise.

[5.4.1]
--------
* fix: The default enrollment ``learner_status`` view now considers intended courses
Expand Down
2 changes: 1 addition & 1 deletion enterprise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Your project description goes here.
"""

__version__ = "5.4.1"
__version__ = "5.4.2"
165 changes: 165 additions & 0 deletions enterprise/management/commands/update_enterprise_social_auth_uids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""
Django management command to update the social auth records UID
"""

import csv
import logging

Check warning on line 6 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L5-L6

Added lines #L5 - L6 were not covered by tests

from django.core.exceptions import ValidationError
from django.core.management.base import BaseCommand
from django.db import transaction

Check warning on line 10 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L8-L10

Added lines #L8 - L10 were not covered by tests

try:
from social_django.models import UserSocialAuth
except ImportError:
UserSocialAuth = None

Check warning on line 15 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L12-L15

Added lines #L12 - L15 were not covered by tests

logger = logging.getLogger(__name__)

Check warning on line 17 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L17

Added line #L17 was not covered by tests


class CSVUpdateError(Exception):

Check warning on line 20 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L20

Added line #L20 was not covered by tests
"""Custom exception for CSV update process."""
pass # pylint: disable=unnecessary-pass

Check warning on line 22 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L22

Added line #L22 was not covered by tests


class Command(BaseCommand):

Check warning on line 25 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L25

Added line #L25 was not covered by tests
"""
Update the enterprise related social auth records UID to the new one.
Example usage:
./manage.py lms update_enterprise_social_auth_uids csv_file_path
./manage.py lms update_enterprise_social_auth_uids csv_file_path --old-prefix="slug:" --new-prefix="slug:x|{}@xyz"
./manage.py lms update_enterprise_social_auth_uids csv_file_path --no-dry-run
"""

help = 'Records update from CSV with console logging'

Check warning on line 36 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L36

Added line #L36 was not covered by tests

def add_arguments(self, parser):
parser.add_argument('csv_file', type=str, help='Path to the CSV file')
parser.add_argument(

Check warning on line 40 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L38-L40

Added lines #L38 - L40 were not covered by tests
'--old_prefix',
type=str,
default=None,
help='Optional old prefix for old UID. If not provided, uses CSV value.'
)
parser.add_argument(

Check warning on line 46 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L46

Added line #L46 was not covered by tests
'--new_prefix',
type=str,
default=None,
help='Optional new prefix for new UID. If not provided, uses CSV value.'
)
parser.add_argument(

Check warning on line 52 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L52

Added line #L52 was not covered by tests
'--no-dry-run',
action='store_false',
dest='dry_run',
default=True,
help='Actually save changes instead of simulating'
)

def handle(self, *args, **options):
logger.info("Command has started...")
csv_path = options['csv_file']
dry_run = options['dry_run']
old_prefix = options['old_prefix']
new_prefix = options['new_prefix']

Check warning on line 65 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L60-L65

Added lines #L60 - L65 were not covered by tests

total_processed = 0
total_updated = 0
total_errors = 0

Check warning on line 69 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L67-L69

Added lines #L67 - L69 were not covered by tests

try:
with open(csv_path, 'r') as csvfile:
reader = csv.DictReader(csvfile)

Check warning on line 73 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L71-L73

Added lines #L71 - L73 were not covered by tests

for row_num, row in enumerate(reader, start=1):
total_processed += 1

Check warning on line 76 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L76

Added line #L76 was not covered by tests

try:
with transaction.atomic():

Check warning on line 79 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L78-L79

Added lines #L78 - L79 were not covered by tests
if self.update_record(row, dry_run, old_prefix, new_prefix):
total_updated += 1

Check warning on line 81 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L81

Added line #L81 was not covered by tests

except Exception as row_error: # pylint: disable=broad-except
total_errors += 1
error_msg = f"Row {row_num} update failed: {row} - Error: {str(row_error)}"
logger.error(error_msg, exc_info=True)

Check warning on line 86 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L83-L86

Added lines #L83 - L86 were not covered by tests

summary_msg = (

Check warning on line 88 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L88

Added line #L88 was not covered by tests
f"CSV Update Summary:\n"
f"Total Records Processed: {total_processed}\n"
f"Records Successfully Updated: {total_updated}\n"
f"Errors Encountered: {total_errors}\n"
f"Dry Run Mode: {'Enabled' if dry_run else 'Disabled'}"
)
logger.info(summary_msg)
except IOError as io_error:
logger.critical(f"File I/O error: {str(io_error)}")

Check warning on line 97 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L95-L97

Added lines #L95 - L97 were not covered by tests

except Exception as e: # pylint: disable=broad-except
logger.critical(f"Critical error in CSV processing: {str(e)}")

Check warning on line 100 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L99-L100

Added lines #L99 - L100 were not covered by tests

def update_record(self, row, dry_run=True, old_prefix=None, new_prefix=None):

Check warning on line 102 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L102

Added line #L102 was not covered by tests
"""
Update a single record, applying optional prefixes to UIDs if provided.
Args:
row (dict): CSV row data
dry_run (bool): Whether to simulate or actually save changes
old_prefix (str): Prefix to apply to the old UID
new_prefix (str): Prefix to apply to the new UID
Returns:
bool: Whether the update was successful
"""
try:
old_uid = row.get('old-uid')
new_uid = row.get('new-uid')

Check warning on line 117 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L115-L117

Added lines #L115 - L117 were not covered by tests

# Validating that both values are present
if not old_uid or not new_uid:
raise CSVUpdateError("Missing required UID fields")

Check warning on line 121 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L121

Added line #L121 was not covered by tests

# Construct dynamic UIDs
old_uid_with_prefix = f'{old_prefix}{old_uid}' if old_prefix else old_uid
new_uid_with_prefix = (

Check warning on line 125 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L124-L125

Added lines #L124 - L125 were not covered by tests
new_prefix.format(new_uid) if new_prefix and '{}' in new_prefix
else f"{new_prefix}{new_uid}" if new_prefix
else new_uid
)

instance_with_old_uid = UserSocialAuth.objects.filter(uid=old_uid_with_prefix).first()

Check warning on line 131 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L131

Added line #L131 was not covered by tests

if not instance_with_old_uid:
raise CSVUpdateError(f"No record found with old UID {old_uid_with_prefix}")

Check warning on line 134 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L134

Added line #L134 was not covered by tests

instance_with_new_uid = UserSocialAuth.objects.filter(uid=new_uid_with_prefix).first()

Check warning on line 136 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L136

Added line #L136 was not covered by tests
if instance_with_new_uid:
log_entry = f"Warning: Existing record with new UID {new_uid_with_prefix} is deleting."
logger.info(log_entry)

Check warning on line 139 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L138-L139

Added lines #L138 - L139 were not covered by tests
if not dry_run:
instance_with_new_uid.delete()

Check warning on line 141 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L141

Added line #L141 was not covered by tests

if not dry_run:
instance_with_old_uid.uid = new_uid_with_prefix
instance_with_old_uid.save()

Check warning on line 145 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L144-L145

Added lines #L144 - L145 were not covered by tests

log_entry = f"Successfully updated record: Old UID {old_uid_with_prefix} → New UID {new_uid_with_prefix}"
logger.info(log_entry)

Check warning on line 148 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L147-L148

Added lines #L147 - L148 were not covered by tests

return True

Check warning on line 150 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L150

Added line #L150 was not covered by tests

except ValidationError as ve:
error_msg = f"Validation error: {ve}"
logger.error(error_msg)
raise

Check warning on line 155 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L152-L155

Added lines #L152 - L155 were not covered by tests

except CSVUpdateError as update_error:
error_msg = f"Update processing error: {update_error}"
logger.error(error_msg)
raise

Check warning on line 160 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L157-L160

Added lines #L157 - L160 were not covered by tests

except Exception as e:
error_msg = f"Unexpected error during record update: {e}"
logger.error(error_msg, exc_info=True)
raise

Check warning on line 165 in enterprise/management/commands/update_enterprise_social_auth_uids.py

View check run for this annotation

Codecov / codecov/patch

enterprise/management/commands/update_enterprise_social_auth_uids.py#L162-L165

Added lines #L162 - L165 were not covered by tests

0 comments on commit 1de298a

Please sign in to comment.