diff --git a/todo/migrations/0004_backfill_user_role_data.py b/todo/migrations/0004_backfill_user_role_data.py new file mode 100644 index 00000000..05edb770 --- /dev/null +++ b/todo/migrations/0004_backfill_user_role_data.py @@ -0,0 +1,138 @@ +# Generated by Django 5.1.5 on 2025-09-23 07:25 + +from django.db import migrations +import logging +import sys +from todo.repositories.user_role_repository import UserRoleRepository +from todo.repositories.team_repository import TeamRepository, UserTeamDetailsRepository +from todo.constants.role import RoleScope, RoleName +from datetime import datetime, timezone +from todo.services.enhanced_dual_write_service import EnhancedDualWriteService + +logger = logging.getLogger(__name__) + + +def fix_team_roles(apps, schema_editor): + logger.info("\n--- Starting Team Roles Fix Script ---") + if "test" in sys.argv: + logger.info("\n--- Skipping Team Roles Fix Script in test environment ---") + return + teams_data = TeamRepository.get_collection().find({"is_deleted": False}) + roles_scope = RoleScope.TEAM.value + + roles_ensured = 0 + roles_deactivated = 0 + dual_write_service = EnhancedDualWriteService() + + roles_to_assign = [] + roles_to_remove = [] + postgres_operations = [] + + for team in teams_data: + team_id = str(team["_id"]) + team_owner = team["created_by"] + team_members = list(UserTeamDetailsRepository.get_by_team_id(team_id)) + for member in team_members: + member_id = str(member.user_id) + if member_id == team_owner: + member_roles = UserRoleRepository.get_user_roles(member_id, roles_scope, team_id) + existing_roles = [role.role_name for role in member_roles] + for role in (RoleName.OWNER.value, RoleName.ADMIN.value, RoleName.MEMBER.value): + if role not in existing_roles: + roles_to_assign.append( + { + "user_id": member_id, + "role_name": role, + "scope": roles_scope, + "team_id": team_id, + "is_active": True, + "created_at": datetime.now(timezone.utc), + "created_by": "system", + } + ) + else: + member_roles = UserRoleRepository.get_user_roles(member_id, roles_scope, team_id) + has_member_role = False + for role in member_roles: + if role.role_name == RoleName.MEMBER.value: + has_member_role = True + else: + roles_to_remove.append( + { + "mongo_id": role.id, + "user_id": member_id, + "role_name": role.role_name, + "scope": roles_scope, + "team_id": role.team_id, + "is_active": role.is_active, + "created_by": role.created_by, + "created_at": role.created_at, + } + ) + if not has_member_role: + roles_to_assign.append( + { + "user_id": member_id, + "role_name": RoleName.MEMBER.value, + "scope": roles_scope, + "team_id": team_id, + "is_active": True, + "created_at": datetime.now(timezone.utc), + "created_by": "system", + } + ) + if roles_to_assign: + result = UserRoleRepository.get_collection().insert_many(roles_to_assign) + roles_ensured = len(result.inserted_ids) + logger.info(f"Successfully inserted {roles_ensured} new roles.") + + for role_data, mongo_id in zip(roles_to_assign, result.inserted_ids): + postgres_operations.append( + { + "operation": "create", + "collection_name": "user_roles", + "mongo_id": str(mongo_id), + "data": role_data, + } + ) + + if roles_to_remove: + for role in roles_to_remove: + mongo_id = str(role["mongo_id"]) + postgres_operations.append( + { + "operation": "update", + "collection_name": "user_roles", + "mongo_id": mongo_id, + "data": { + "user_id": role["user_id"], + "role_name": role["role_name"], + "scope": role["scope"], + "team_id": role["team_id"], + "is_active": False, + "created_at": role["created_at"], + "created_by": role["created_by"], + }, + } + ) + role_ids_to_remove = [roles["mongo_id"] for roles in roles_to_remove] + result = UserRoleRepository.get_collection().update_many( + {"_id": {"$in": role_ids_to_remove}}, {"$set": {"is_active": False}} + ) + roles_deactivated = result.modified_count + logger.info(f"Successfully deactivated {roles_deactivated} roles.") + if postgres_operations: + logger.info(f"\nStarting sync of {len(postgres_operations)} operations to PostgreSQL...") + success = dual_write_service.batch_operations(postgres_operations) + if success: + logger.info("PostgreSQL sync completed successfully.") + else: + logger.warning("PostgreSQL sync encountered errors. Check logs for details.") + + +class Migration(migrations.Migration): + dependencies = [ + ("todo", "0003_alter_postgresuserrole_unique_together_and_more"), + ] + + operations = [migrations.RunPython(fix_team_roles, migrations.RunPython.noop)]