CineDev commited on
Commit
09e5add
·
verified ·
1 Parent(s): a44678f

Update api/views.py

Browse files
Files changed (1) hide show
  1. api/views.py +9 -51
api/views.py CHANGED
@@ -7,16 +7,9 @@ from .models import Participant, Match, Whitelist
7
 
8
  # --- LOGIC HELPER ---
9
  def calculate_similarity(p1_data, p2_data):
10
- """
11
- Advanced Matching Logic:
12
- - Starts with 30% Base Compatibility.
13
- - Remaining 70% is distributed among the 15 questions.
14
- """
15
  base_score = 30.0
16
  total_questions = 15
17
  matches_found = 0
18
-
19
- # Calculate points per question (approx 4.66%)
20
  points_per_match = 70.0 / total_questions
21
 
22
  print(f"--- COMPARING {p1_data.get('name', 'User A')} vs {p2_data.get('name', 'User B')} ---")
@@ -25,41 +18,26 @@ def calculate_similarity(p1_data, p2_data):
25
  key = f'q{i}'
26
  val1 = p1_data.get(key)
27
  val2 = p2_data.get(key)
28
-
29
- # Check for exact match (A==A, B==B)
30
  if val1 and val2 and val1 == val2:
31
  matches_found += 1
32
 
33
  final_score = base_score + (matches_found * points_per_match)
34
- print(f"Matches: {matches_found}/{total_questions} | Final Score: {round(final_score, 1)}%")
35
-
36
  return min(round(final_score, 1), 100.0)
37
 
38
  def run_matching_logic():
39
- """
40
- The Core Matching Algorithm (Thread-Safe Version).
41
- Uses transaction.atomic() to ensure no double-booking of users.
42
- """
43
  matches_created = []
44
-
45
- # Lock the database rows to prevent concurrent matches
46
  with transaction.atomic():
47
- # Fetch only users who are strictly NOT matched yet
48
- # select_for_update() locks these rows until the transaction finishes
49
  unmatched_pool = list(
50
  Participant.objects.select_for_update()
51
  .filter(is_matched=False)
52
- .order_by('created_at') # Order ensures consistent locking
53
  )
54
-
55
  while len(unmatched_pool) >= 2:
56
  person_a = unmatched_pool.pop(0)
57
-
58
  best_partner = None
59
  best_score = -1
60
  best_partner_index = -1
61
 
62
- # Compare person_a against everyone else waiting
63
  for i, candidate in enumerate(unmatched_pool):
64
  score = calculate_similarity(person_a.quiz_data, candidate.quiz_data)
65
  if score > best_score:
@@ -68,46 +46,34 @@ def run_matching_logic():
68
  best_partner_index = i
69
 
70
  if best_partner:
71
- # Double check status (redundant but safe)
72
- if person_a.is_matched or best_partner.is_matched:
73
- continue
74
-
75
  Match.objects.create(
76
- participant_1=person_a,
77
- participant_2=best_partner,
78
- compatibility_score=best_score
79
  )
80
-
81
- # Mark both as matched
82
  person_a.is_matched = True
83
  best_partner.is_matched = True
84
  person_a.save()
85
  best_partner.save()
86
-
87
- # Remove partner from the pool so they aren't matched again
88
  unmatched_pool.pop(best_partner_index)
89
  matches_created.append(f"{person_a.name} & {best_partner.name} ({best_score}%)")
90
-
91
  return matches_created
92
 
93
  # --- ENDPOINTS ---
94
 
95
  @api_view(['POST'])
96
  def verify_user(request):
97
- """
98
- Checks if Email exists in Whitelist AND if they are already registered.
99
- """
100
  email_input = request.data.get('email', '').strip().lower()
101
 
102
- # 1. CHECK IF ALREADY REGISTERED (Redirect Logic)
103
  if Participant.objects.filter(email__iexact=email_input).exists():
104
  return Response({
105
  'success': True,
106
- 'status': 'registered', # Special flag for frontend
107
  'message': 'User already registered. Redirecting...'
108
  })
109
 
110
- # 2. CHECK WHITELIST (Supabase Table)
111
  is_allowed = Whitelist.objects.filter(email__iexact=email_input).exists()
112
 
113
  if is_allowed:
@@ -120,39 +86,31 @@ def register_participant(request):
120
  data = request.data
121
  email = data.get('email')
122
 
123
- # Security: Double check whitelist before saving
124
  if not Whitelist.objects.filter(email__iexact=email).exists():
125
  return Response({'success': False, 'message': 'Email removed from whitelist.'}, status=403)
126
 
127
  if Participant.objects.filter(email=email).exists():
128
  return Response({'success': False, 'message': 'Already registered.'}, status=400)
129
 
130
- # Save User
131
  participant = Participant.objects.create(
132
  name=data.get('name'),
133
  email=email,
134
  student_id=data.get('student_id'),
135
  role=data.get('role', 'fullstack'),
136
- # Save only question answers (q1, q2...)
137
  quiz_data={k: v for k, v in data.items() if k.startswith('q')},
138
  is_matched=False
139
  )
140
-
141
- # Run Matching Immediately
142
  run_matching_logic()
143
-
144
  return Response({'success': True, 'id': participant.id})
145
 
146
  @api_view(['GET'])
147
  def get_my_match(request):
148
  email = request.GET.get('email')
149
- if not email:
150
- return Response({'success': False, 'message': 'Email required'}, status=400)
151
  try:
152
  me = Participant.objects.get(email=email)
153
  response_data = {
154
- 'success': True,
155
- 'match_found': False,
156
  'participant': {'name': me.name, 'email': me.email, 'student_id': me.student_id}
157
  }
158
  if me.is_matched:
 
7
 
8
  # --- LOGIC HELPER ---
9
  def calculate_similarity(p1_data, p2_data):
 
 
 
 
 
10
  base_score = 30.0
11
  total_questions = 15
12
  matches_found = 0
 
 
13
  points_per_match = 70.0 / total_questions
14
 
15
  print(f"--- COMPARING {p1_data.get('name', 'User A')} vs {p2_data.get('name', 'User B')} ---")
 
18
  key = f'q{i}'
19
  val1 = p1_data.get(key)
20
  val2 = p2_data.get(key)
 
 
21
  if val1 and val2 and val1 == val2:
22
  matches_found += 1
23
 
24
  final_score = base_score + (matches_found * points_per_match)
 
 
25
  return min(round(final_score, 1), 100.0)
26
 
27
  def run_matching_logic():
 
 
 
 
28
  matches_created = []
 
 
29
  with transaction.atomic():
 
 
30
  unmatched_pool = list(
31
  Participant.objects.select_for_update()
32
  .filter(is_matched=False)
33
+ .order_by('created_at')
34
  )
 
35
  while len(unmatched_pool) >= 2:
36
  person_a = unmatched_pool.pop(0)
 
37
  best_partner = None
38
  best_score = -1
39
  best_partner_index = -1
40
 
 
41
  for i, candidate in enumerate(unmatched_pool):
42
  score = calculate_similarity(person_a.quiz_data, candidate.quiz_data)
43
  if score > best_score:
 
46
  best_partner_index = i
47
 
48
  if best_partner:
49
+ if person_a.is_matched or best_partner.is_matched: continue
50
+
 
 
51
  Match.objects.create(
52
+ participant_1=person_a, participant_2=best_partner, compatibility_score=best_score
 
 
53
  )
 
 
54
  person_a.is_matched = True
55
  best_partner.is_matched = True
56
  person_a.save()
57
  best_partner.save()
 
 
58
  unmatched_pool.pop(best_partner_index)
59
  matches_created.append(f"{person_a.name} & {best_partner.name} ({best_score}%)")
 
60
  return matches_created
61
 
62
  # --- ENDPOINTS ---
63
 
64
  @api_view(['POST'])
65
  def verify_user(request):
 
 
 
66
  email_input = request.data.get('email', '').strip().lower()
67
 
68
+ # 1. IF ALREADY REGISTERED -> SEND SPECIAL SUCCESS STATUS (NOT ERROR)
69
  if Participant.objects.filter(email__iexact=email_input).exists():
70
  return Response({
71
  'success': True,
72
+ 'status': 'registered', # <--- Front-end looks for this!
73
  'message': 'User already registered. Redirecting...'
74
  })
75
 
76
+ # 2. CHECK WHITELIST
77
  is_allowed = Whitelist.objects.filter(email__iexact=email_input).exists()
78
 
79
  if is_allowed:
 
86
  data = request.data
87
  email = data.get('email')
88
 
 
89
  if not Whitelist.objects.filter(email__iexact=email).exists():
90
  return Response({'success': False, 'message': 'Email removed from whitelist.'}, status=403)
91
 
92
  if Participant.objects.filter(email=email).exists():
93
  return Response({'success': False, 'message': 'Already registered.'}, status=400)
94
 
 
95
  participant = Participant.objects.create(
96
  name=data.get('name'),
97
  email=email,
98
  student_id=data.get('student_id'),
99
  role=data.get('role', 'fullstack'),
 
100
  quiz_data={k: v for k, v in data.items() if k.startswith('q')},
101
  is_matched=False
102
  )
 
 
103
  run_matching_logic()
 
104
  return Response({'success': True, 'id': participant.id})
105
 
106
  @api_view(['GET'])
107
  def get_my_match(request):
108
  email = request.GET.get('email')
109
+ if not email: return Response({'success': False, 'message': 'Email required'}, status=400)
 
110
  try:
111
  me = Participant.objects.get(email=email)
112
  response_data = {
113
+ 'success': True, 'match_found': False,
 
114
  'participant': {'name': me.name, 'email': me.email, 'student_id': me.student_id}
115
  }
116
  if me.is_matched: