@peopl-health/nexus 5.69.0-dev.12622 → 5.69.0-dev.12623

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ const { getMessages } = require('../../services/messageService');
6
6
  const { DEFAULT_TIMEZONE, isWithinHourWindow } = require('../../utils/scheduleUtils');
7
7
  const { logger } = require('../../utils/logger');
8
8
  const { botIsPaused } = require('../../helpers/pausedThreadHelper');
9
+ const { Thread } = require('../../models/threadModel');
9
10
  const { PATIENT_CONTACT_WINDOW } = require('./contingencyDispatchService');
10
11
  const { hasPendingReminderForPatient } = require('./clinicalAirtableService');
11
12
  const { WriteClaimStore } = require('./writeClaimStore');
@@ -53,13 +54,6 @@ const AUDIT = {
53
54
  const WEEKLY_SOURCE = 'weekly';
54
55
  const SCOPE_SOURCE = 'patient_scope';
55
56
 
56
- const PATIENT_TABLE = 'estado_general';
57
- const PATIENT_F = {
58
- whatsapp: 'whatsapp_id',
59
- firstName: 'first_name',
60
- disableFollowUp: 'desactivar_seguimiento_48hrs',
61
- };
62
-
63
57
  const sweepClaims = new WriteClaimStore({ namespace: 'outreach_sweep', ttlMs: 8 * 24 * 60 * 60 * 1000 });
64
58
 
65
59
  const UNDELIVERED_STATUSES = ['failed', 'undelivered'];
@@ -216,32 +210,18 @@ function buildCandidates(rows, { now, cadence = 'daily', mode = 'continuity' })
216
210
  return { candidates, targetDays, weekday, cadence, mode };
217
211
  }
218
212
 
219
- function buildScopeCandidates({ patientScope, patientRows, excludedCodes }) {
220
- const patients = new Map(patientScope.filter((code) => !excludedCodes.has(code))
221
- .map((code) => [code, { names: new Map(), recordId: null, followUpDisabled: false }]));
222
- for (const row of patientRows) {
223
- const patient = patients.get(String(first(row[PATIENT_F.whatsapp]) || '').trim());
224
- if (!patient) continue;
225
- const name = String(first(row[PATIENT_F.firstName]) || '').trim();
226
- if (name) patient.names.set(name.toLowerCase().replace(/\s+/g, ' '), name);
227
- patient.recordId = patient.recordId || row._recordId;
228
- if (row[PATIENT_F.disableFollowUp] === true) patient.followUpDisabled = true;
229
- }
230
-
213
+ function buildScopeCandidates({ threads, excludedCodes }) {
231
214
  const candidates = [];
232
- for (const [code, patient] of patients) {
233
- if (patient.names.size !== 1) {
234
- logger.info('[outreachSweep] scope candidate skipped — no single patient name', { recordId: patient.recordId });
235
- continue;
236
- }
215
+ for (const thread of new Map(threads.map((t) => [t.code, t])).values()) {
216
+ const name = String(thread.nombre || '').trim().split(/\s+/)[0];
217
+ if (excludedCodes.has(thread.code) || !name) continue;
237
218
  candidates.push({
238
219
  source: SCOPE_SOURCE,
239
- patientId: null,
240
- recordId: patient.recordId,
241
- code,
242
- name: [...patient.names.values()][0],
220
+ patientId: thread.patient_id || null,
221
+ recordId: String(thread._id),
222
+ code: thread.code,
223
+ name,
243
224
  reason: 'no_triage_history',
244
- followUpDisabled: patient.followUpDisabled,
245
225
  rows: [],
246
226
  brief: {
247
227
  reason: 'no hay un triaje contestado que sirva como ancla clínica',
@@ -256,15 +236,15 @@ function buildScopeCandidates({ patientScope, patientRows, excludedCodes }) {
256
236
 
257
237
  async function loadScopeCandidates({ now, viewRows, patientScope }) {
258
238
  const weekStart = startOfLocalWeek(now).toISOString();
259
- const [patientRows, weekRows] = await Promise.all([
260
- airtableService.getRecordByFilter(Symptoms_ID, PATIENT_TABLE, 'TRUE()', null, Object.values(PATIENT_F), true),
239
+ const [threads, weekRows] = await Promise.all([
240
+ Thread.find({ code: { $in: patientScope } }, { code: 1, nombre: 1, patient_id: 1 }).lean(),
261
241
  airtableService.getRecordByFilter(
262
242
  Symptoms_ID, TABLE, `NOT(IS_BEFORE({${F.createdAt}}, DATETIME_PARSE("${weekStart}")))`, null, [F.whatsapp],
263
243
  ),
264
244
  ]);
265
245
  const excludedCodes = new Set([...viewRows, ...(weekRows || [])]
266
246
  .map((row) => String(first(row[F.whatsapp]) || '').trim()));
267
- return buildScopeCandidates({ patientScope, patientRows: patientRows || [], excludedCodes });
247
+ return buildScopeCandidates({ threads: threads || [], excludedCodes });
268
248
  }
269
249
 
270
250
  function sweepClaimKey(candidate, now) {
@@ -272,7 +252,6 @@ function sweepClaimKey(candidate, now) {
272
252
  }
273
253
 
274
254
  function opsOptedOut(candidate, { now }) {
275
- if (candidate.followUpDisabled) return 'patient_follow_up_disabled';
276
255
  const weekStart = startOfLocalWeek(now);
277
256
  for (const row of candidate.rows) {
278
257
  if (first(row[F.disableFollowUp]) === true) return 'patient_follow_up_disabled';
@@ -307,6 +286,7 @@ async function rejectionReason(candidate, {
307
286
  } = {}) {
308
287
  const opsReason = opsOptedOut(candidate, { now });
309
288
  if (opsReason) return opsReason;
289
+ if (await Thread.exists({ code: candidate.code, outreachOptOut: true })) return 'patient_follow_up_disabled';
310
290
  if (await botIsPaused(candidate.code, { label: '[outreachSweep]' })) return 'bot_paused';
311
291
  if (!includePreviouslySwept) {
312
292
  const sweptReason = candidate.source === SCOPE_SOURCE
@@ -649,7 +629,6 @@ module.exports = {
649
629
  mondayTriageStillPending,
650
630
  VIEW,
651
631
  F,
652
- PATIENT_F,
653
632
  AUDIT,
654
633
  sweepClaims,
655
634
  };
@@ -10,6 +10,7 @@ const threadSchema = new mongoose.Schema({
10
10
  run_id: { type: String, default: null },
11
11
  nombre: { type: String, default: null },
12
12
  stopped: { type: Boolean, default: false },
13
+ outreachOptOut: { type: Boolean, default: false },
13
14
  pausedAt: { type: Date, default: null },
14
15
  doNotAlertThrough: { type: Date, default: null },
15
16
  review: { type: Boolean, default: false },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "5.69.0-dev.12622",
3
+ "version": "5.69.0-dev.12623",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",