from django.db import transaction from django.utils import timezone from content.models import ContentInteraction, ContentItem from math_life.models import ( MathBTIAssessment, MathBTIResult, MathIdentity, Story, StoryRun, StoryVersion, ) from math_life.services import score_mathbti from progression.models import Card, UserCard from progression.services import initialize_math_identity from .models import VisitorMigration def migration_summary(payload): mathbti = 1 if isinstance(payload.get("mathbti"), dict) else 0 return { "mathbti_results": mathbti, "story_saves": min(len(payload.get("story_saves", [])), 20), "favorites": min(len(payload.get("favorites", [])), 200), "cards": min(len(payload.get("cards", [])), 100), } @transaction.atomic def apply_visitor_migration(user): migration = VisitorMigration.objects.select_for_update().get(user=user) if migration.status == VisitorMigration.Status.APPLIED: return migration payload = migration.payload mathbti = payload.get("mathbti") if isinstance(mathbti, dict): assessment = MathBTIAssessment.objects.filter( version=mathbti.get("version"), is_published=True, ).first() if assessment: code, scores = score_mathbti(assessment.definition, mathbti.get("answers", [])) identity = MathIdentity.objects.get(code=code) MathBTIResult.objects.create( user=user, assessment=assessment, identity=identity, answers=mathbti["answers"], axis_scores=scores, ) initialize_math_identity(user, identity) for item in payload.get("story_saves", [])[:20]: if not isinstance(item, dict): continue story = Story.objects.filter(slug=item.get("story_slug")).first() if story is None: continue version = StoryVersion.objects.filter( story=story, version=item.get("version"), is_published=True, ).first() node_id = item.get("current_node") if version is None or node_id not in version.content.get("nodes", {}): continue StoryRun.objects.get_or_create( user=user, story_version=version, status=StoryRun.Status.ACTIVE, defaults={ "current_node": node_id, "state": item.get("state", {}) if isinstance(item.get("state"), dict) else {}, }, ) favorites = ContentItem.objects.filter( slug__in=payload.get("favorites", [])[:200], is_published=True, ) ContentInteraction.objects.bulk_create( [ ContentInteraction( user=user, content=item, action=ContentInteraction.Action.FAVORITE, ) for item in favorites ], ignore_conflicts=True, ) cards = Card.objects.filter(slug__in=payload.get("cards", [])[:100]) UserCard.objects.bulk_create( [UserCard(user=user, card=card, source="visitor_migration") for card in cards], ignore_conflicts=True, ) migration.status = VisitorMigration.Status.APPLIED migration.applied_at = timezone.now() migration.save(update_fields=["status", "applied_at"]) return migration