Abhises commited on
Commit
82ec9ac
·
1 Parent(s): a4778f4

edited match making

Browse files
Files changed (1) hide show
  1. src/jobs/matcher.js +72 -28
src/jobs/matcher.js CHANGED
@@ -6,7 +6,6 @@ const runWeeklyMatching = async () => {
6
  console.log(`[${new Date().toISOString()}] Running automated connection matching algorithm...`);
7
  try {
8
  // Fetch workspaces that belong ONLY to non-admin users
9
- // A workspace has teamMembers, we check if they are owned by an ADMIN
10
  const workspaces = await prisma.workspace.findMany({
11
  where: {
12
  teamMembers: {
@@ -32,8 +31,33 @@ const runWeeklyMatching = async () => {
32
  if (settings && settings.matchAmount) matchAmount = settings.matchAmount;
33
  } catch(e) {}
34
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  for (const ws of workspaces) {
36
- // Find all existing interactions (both giving and receiving) to prevent ANY duplicate or reciprocal matching
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  const allInteractions = await prisma.exchangeThread.findMany({
38
  where: {
39
  OR: [
@@ -43,24 +67,29 @@ const runWeeklyMatching = async () => {
43
  }
44
  });
45
 
46
- const interactedIds = allInteractions.map(t =>
47
- t.giverWorkspaceId === ws.id ? t.receiverWorkspaceId : t.giverWorkspaceId
48
- );
49
-
50
- // Exclude self and anyone already interacted with
51
- const excludes = [...new Set([...interactedIds, ws.id])];
 
 
 
 
 
 
 
 
 
 
52
 
53
  let poolExhaustedWarningSent = false;
54
-
55
- // Count current active (NEW/PENDING) giving threads to avoid spamming
56
- const activeGivingCount = allInteractions.filter(t =>
57
- t.giverWorkspaceId === ws.id && (t.status === 'PENDING' || t.stage === 'NEW')
58
- ).length;
59
 
60
- const neededGiving = Math.max(0, matchAmount - activeGivingCount);
61
 
62
  if (neededGiving > 0) {
63
- const potentialReceivers = workspaces.filter(w => !excludes.includes(w.id));
64
  if (potentialReceivers.length === 0 && !poolExhaustedWarningSent) {
65
  wsManager.sendNotification(ws.id, {
66
  type: 'system',
@@ -70,7 +99,13 @@ const runWeeklyMatching = async () => {
70
  poolExhaustedWarningSent = true;
71
  }
72
 
73
- const receivers = potentialReceivers.sort(() => 0.5 - Math.random()).slice(0, neededGiving);
 
 
 
 
 
 
74
 
75
  for (const rec of receivers) {
76
  const thread = await prisma.exchangeThread.create({
@@ -83,8 +118,12 @@ const runWeeklyMatching = async () => {
83
  include: { giverWorkspace: true, receiverWorkspace: true }
84
  });
85
 
86
- // Add to excludes so we don't pick them again in the receiving loop
87
- excludes.push(rec.id);
 
 
 
 
88
 
89
  wsManager.sendNotification(ws.id, {
90
  type: 'new_thread',
@@ -104,15 +143,10 @@ const runWeeklyMatching = async () => {
104
  }
105
  }
106
 
107
- // Count current active (NEW/PENDING) receiving threads to avoid spamming
108
- const activeReceivingCount = allInteractions.filter(t =>
109
- t.receiverWorkspaceId === ws.id && (t.status === 'PENDING' || t.stage === 'NEW')
110
- ).length;
111
-
112
- const neededReceiving = Math.max(0, matchAmount - activeReceivingCount);
113
 
114
  if (neededReceiving > 0) {
115
- const potentialGivers = workspaces.filter(w => !excludes.includes(w.id));
116
  if (potentialGivers.length === 0 && !poolExhaustedWarningSent) {
117
  wsManager.sendNotification(ws.id, {
118
  type: 'system',
@@ -122,7 +156,13 @@ const runWeeklyMatching = async () => {
122
  poolExhaustedWarningSent = true;
123
  }
124
 
125
- const givers = potentialGivers.sort(() => 0.5 - Math.random()).slice(0, neededReceiving);
 
 
 
 
 
 
126
 
127
  for (const giv of givers) {
128
  const thread = await prisma.exchangeThread.create({
@@ -135,8 +175,12 @@ const runWeeklyMatching = async () => {
135
  include: { giverWorkspace: true, receiverWorkspace: true }
136
  });
137
 
138
- // Add to excludes
139
- excludes.push(giv.id);
 
 
 
 
140
 
141
  wsManager.sendNotification(giv.id, {
142
  type: 'new_thread',
 
6
  console.log(`[${new Date().toISOString()}] Running automated connection matching algorithm...`);
7
  try {
8
  // Fetch workspaces that belong ONLY to non-admin users
 
9
  const workspaces = await prisma.workspace.findMany({
10
  where: {
11
  teamMembers: {
 
31
  if (settings && settings.matchAmount) matchAmount = settings.matchAmount;
32
  } catch(e) {}
33
 
34
+ // Pre-calculate active giving and receiving counts for all workspaces for fairness weighting
35
+ const allActiveThreads = await prisma.exchangeThread.findMany({
36
+ where: {
37
+ OR: [
38
+ { status: 'PENDING' },
39
+ { stage: 'NEW' }
40
+ ]
41
+ }
42
+ });
43
+
44
+ const activeGivingCounts = {};
45
+ const activeReceivingCounts = {};
46
  for (const ws of workspaces) {
47
+ activeGivingCounts[ws.id] = 0;
48
+ activeReceivingCounts[ws.id] = 0;
49
+ }
50
+ for (const t of allActiveThreads) {
51
+ if (activeGivingCounts[t.giverWorkspaceId] !== undefined) {
52
+ activeGivingCounts[t.giverWorkspaceId]++;
53
+ }
54
+ if (activeReceivingCounts[t.receiverWorkspaceId] !== undefined) {
55
+ activeReceivingCounts[t.receiverWorkspaceId]++;
56
+ }
57
+ }
58
+
59
+ for (const ws of workspaces) {
60
+ // Find all existing interactions for this workspace
61
  const allInteractions = await prisma.exchangeThread.findMany({
62
  where: {
63
  OR: [
 
67
  }
68
  });
69
 
70
+ const excludedReceivers = new Set([ws.id]);
71
+ const excludedGivers = new Set([ws.id]);
72
+
73
+ for (const t of allInteractions) {
74
+ if (t.giverWorkspaceId === ws.id) {
75
+ // ws already gave to them, don't try again
76
+ excludedReceivers.add(t.receiverWorkspaceId);
77
+ // Block reverse direction (reciprocal) only if it wasn't rejected
78
+ if (t.status !== 'REJECTED') excludedGivers.add(t.receiverWorkspaceId);
79
+ } else {
80
+ // someone already gave to ws, they can't give to ws again
81
+ excludedGivers.add(t.giverWorkspaceId);
82
+ // Block reverse direction (reciprocal) only if it wasn't rejected
83
+ if (t.status !== 'REJECTED') excludedReceivers.add(t.giverWorkspaceId);
84
+ }
85
+ }
86
 
87
  let poolExhaustedWarningSent = false;
 
 
 
 
 
88
 
89
+ const neededGiving = Math.max(0, matchAmount - activeGivingCounts[ws.id]);
90
 
91
  if (neededGiving > 0) {
92
+ const potentialReceivers = workspaces.filter(w => !excludedReceivers.has(w.id));
93
  if (potentialReceivers.length === 0 && !poolExhaustedWarningSent) {
94
  wsManager.sendNotification(ws.id, {
95
  type: 'system',
 
99
  poolExhaustedWarningSent = true;
100
  }
101
 
102
+ const receivers = potentialReceivers
103
+ .sort((a, b) => {
104
+ const diff = activeReceivingCounts[a.id] - activeReceivingCounts[b.id];
105
+ if (diff === 0) return 0.5 - Math.random(); // tie-breaker
106
+ return diff; // prefer receivers with lowest activeReceivingCounts
107
+ })
108
+ .slice(0, neededGiving);
109
 
110
  for (const rec of receivers) {
111
  const thread = await prisma.exchangeThread.create({
 
118
  include: { giverWorkspace: true, receiverWorkspace: true }
119
  });
120
 
121
+ // Block them in both directions for the rest of this execution since it's active now
122
+ excludedReceivers.add(rec.id);
123
+ excludedGivers.add(rec.id);
124
+
125
+ activeGivingCounts[ws.id]++;
126
+ activeReceivingCounts[rec.id]++;
127
 
128
  wsManager.sendNotification(ws.id, {
129
  type: 'new_thread',
 
143
  }
144
  }
145
 
146
+ const neededReceiving = Math.max(0, matchAmount - activeReceivingCounts[ws.id]);
 
 
 
 
 
147
 
148
  if (neededReceiving > 0) {
149
+ const potentialGivers = workspaces.filter(w => !excludedGivers.has(w.id));
150
  if (potentialGivers.length === 0 && !poolExhaustedWarningSent) {
151
  wsManager.sendNotification(ws.id, {
152
  type: 'system',
 
156
  poolExhaustedWarningSent = true;
157
  }
158
 
159
+ const givers = potentialGivers
160
+ .sort((a, b) => {
161
+ const diff = activeGivingCounts[a.id] - activeGivingCounts[b.id];
162
+ if (diff === 0) return 0.5 - Math.random(); // tie-breaker
163
+ return diff; // prefer givers with lowest activeGivingCounts
164
+ })
165
+ .slice(0, neededReceiving);
166
 
167
  for (const giv of givers) {
168
  const thread = await prisma.exchangeThread.create({
 
175
  include: { giverWorkspace: true, receiverWorkspace: true }
176
  });
177
 
178
+ // Block them in both directions
179
+ excludedGivers.add(giv.id);
180
+ excludedReceivers.add(giv.id);
181
+
182
+ activeReceivingCounts[ws.id]++;
183
+ activeGivingCounts[giv.id]++;
184
 
185
  wsManager.sendNotification(giv.id, {
186
  type: 'new_thread',