@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({
|
|
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,
|
|
233
|
-
|
|
234
|
-
|
|
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:
|
|
241
|
-
code,
|
|
242
|
-
name
|
|
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 [
|
|
260
|
-
|
|
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({
|
|
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 },
|