CineDev's picture
Update api/views.py
9f85773 verified
from django.db import transaction
from django.db.models import Q
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Participant, Match
# --- LOGIC HELPER ---
def calculate_similarity(p1_data, p2_data):
base_score = 30.0
total_questions = 15
matches_found = 0
points_per_match = 70.0 / total_questions
for i in range(1, total_questions + 1):
key = f'q{i}'
val1 = p1_data.get(key)
val2 = p2_data.get(key)
if val1 and val2 and val1 == val2:
matches_found += 1
final_score = base_score + (matches_found * points_per_match)
return min(round(final_score, 1), 100.0)
def run_matching_logic():
matches_created = []
with transaction.atomic():
unmatched_pool = list(
Participant.objects.select_for_update()
.filter(is_matched=False)
.order_by('created_at')
)
while len(unmatched_pool) >= 2:
person_a = unmatched_pool.pop(0)
best_partner = None
best_score = -1
best_partner_index = -1
for i, candidate in enumerate(unmatched_pool):
score = calculate_similarity(person_a.quiz_data, candidate.quiz_data)
if score > best_score:
best_score = score
best_partner = candidate
best_partner_index = i
if best_partner:
if person_a.is_matched or best_partner.is_matched: continue
Match.objects.create(
participant_1=person_a, participant_2=best_partner, compatibility_score=best_score
)
person_a.is_matched = True
best_partner.is_matched = True
person_a.save()
best_partner.save()
unmatched_pool.pop(best_partner_index)
matches_created.append(f"{person_a.name} & {best_partner.name} ({best_score}%)")
return matches_created
# --- ENDPOINTS ---
@api_view(['POST'])
def verify_user(request):
email_input = request.data.get('email', '').strip().lower()
# ONLY CHECK: IS THE USER ALREADY REGISTERED?
if Participant.objects.filter(email__iexact=email_input).exists():
return Response({
'success': True,
'status': 'registered',
'message': 'User already registered.'
})
# If not registered, they are allowed to proceed
return Response({'success': True, 'status': 'new', 'message': 'Verified'})
@api_view(['POST'])
def register_participant(request):
data = request.data
email = data.get('email', '').strip().lower()
# Prevent double registration
if Participant.objects.filter(email__iexact=email).exists():
return Response({'success': False, 'message': 'Already registered.'}, status=400)
participant = Participant.objects.create(
name=data.get('name'),
email=email,
student_id=data.get('student_id'),
role=data.get('role', 'fullstack'),
quiz_data={k: v for k, v in data.items() if k.startswith('q')},
is_matched=False
)
run_matching_logic()
return Response({'success': True, 'id': participant.id})
@api_view(['POST'])
def register_duo(request):
data = request.data
p1_data = data.get('p1', {})
p2_data = data.get('p2', {})
email1 = p1_data.get('email', '').strip().lower()
email2 = p2_data.get('email', '').strip().lower()
# 1. Validation
if not email1 or not email2:
return Response({'success': False, 'message': 'Both emails required'}, status=400)
# 2. Check Existence (Prevent Re-registration)
if Participant.objects.filter(email__iexact=email1).exists() or \
Participant.objects.filter(email__iexact=email2).exists():
return Response({'success': False, 'message': 'One or both users already registered'}, status=400)
try:
with transaction.atomic():
# Create P1
p1 = Participant.objects.create(
name=p1_data.get('name'),
email=email1,
student_id=p1_data.get('student_id'),
role="duo",
quiz_data={k: v for k, v in p1_data.items() if k.startswith('q')},
is_matched=True
)
# Create P2
p2 = Participant.objects.create(
name=p2_data.get('name'),
email=email2,
student_id=p2_data.get('student_id'),
role="duo",
quiz_data={k: v for k, v in p2_data.items() if k.startswith('q')},
is_matched=True
)
score = calculate_similarity(p1.quiz_data, p2.quiz_data)
Match.objects.create(
participant_1=p1,
participant_2=p2,
compatibility_score=score
)
return Response({'success': True, 'score': score})
except Exception as e:
print(f"Duo Register Error: {e}")
return Response({'success': False, 'message': 'Registration Error'}, status=500)
@api_view(['GET'])
def get_my_match(request):
email = request.GET.get('email')
if not email: return Response({'success': False, 'message': 'Email required'}, status=400)
try:
me = Participant.objects.get(email=email)
response_data = {
'success': True, 'match_found': False,
'participant': {'name': me.name, 'email': me.email, 'student_id': me.student_id}
}
if me.is_matched:
match = Match.objects.filter(Q(participant_1=me) | Q(participant_2=me)).first()
if match:
partner = match.participant_2 if match.participant_1 == me else match.participant_1
response_data['match_found'] = True
response_data['compatibility_percentage'] = match.compatibility_score
response_data['partner'] = {'name': partner.name, 'email': partner.email, 'student_id': partner.student_id}
return Response(response_data)
except Participant.DoesNotExist:
return Response({'success': False, 'message': 'User not found'}, status=404)
@api_view(['POST'])
def trigger_matching(request):
matches = run_matching_logic()
return Response({'success': True, 'matches_generated': matches})
@api_view(['GET'])
def get_participants(request):
participants = Participant.objects.all().order_by('-created_at')
data = []
for p in participants:
data.append({
'id': p.id,
'name': p.name,
'email': p.email,
'role_display': p.role,
'preferred_language': p.quiz_data.get('preferred_language', 'N/A'),
'is_matched': p.is_matched
})
return Response({'success': True, 'participants': data})
@api_view(['GET'])
def get_matches(request):
matches = Match.objects.all().order_by('-compatibility_score')
data = []
for m in matches:
data.append({
'participant1': {'name': m.participant_1.name},
'participant2': {'name': m.participant_2.name},
'compatibility_percentage': m.compatibility_score
})
return Response({'success': True, 'matches': data})