hvp-shared 13.3.0 → 13.4.1
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.
|
@@ -13,10 +13,21 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { AppointmentBranch, AppointmentStandard } from "../contracts/google-calendar/responses";
|
|
15
15
|
/**
|
|
16
|
-
* Multi-factor appointment standard
|
|
16
|
+
* Multi-factor appointment standard v2.
|
|
17
|
+
*
|
|
18
|
+
* Ten dimensions covering everything a recepción needs to capture to make an
|
|
19
|
+
* appointment actionable: branch, title format, pet identity (name/age/breed),
|
|
20
|
+
* tutor identity (name/phone), motive, and scheduling provenance (who and when).
|
|
17
21
|
*
|
|
18
22
|
* Weights sum to 100. An event is "compliant" when its score >= 80.
|
|
19
23
|
*/
|
|
24
|
+
export declare const STANDARD_V2: AppointmentStandard;
|
|
25
|
+
/**
|
|
26
|
+
* Backward-compatible alias for existing code/tests that imported `STANDARD_V1`.
|
|
27
|
+
* Always points to the current standard.
|
|
28
|
+
*
|
|
29
|
+
* @deprecated Use `STANDARD_V2` (or whatever the current version is) explicitly.
|
|
30
|
+
*/
|
|
20
31
|
export declare const STANDARD_V1: AppointmentStandard;
|
|
21
32
|
/**
|
|
22
33
|
* Google Calendar colorId values mapped to HVP branches.
|
|
@@ -107,6 +118,32 @@ export declare const PHONE_REGEX: RegExp;
|
|
|
107
118
|
* "AGENDADO POR SLR 02/12/25"
|
|
108
119
|
*/
|
|
109
120
|
export declare const SCHEDULER_REGEX: RegExp;
|
|
121
|
+
/**
|
|
122
|
+
* Captures the "agendado por" col_code AND the date annotation that often follows it.
|
|
123
|
+
*
|
|
124
|
+
* "AGENDO XZA 16 MAYO" → groups: XZA, "16 MAYO"
|
|
125
|
+
* "AG YMP 14.05.26" → groups: YMP, "14.05.26"
|
|
126
|
+
* "AGENDADA POR SLR 02/12/25" → groups: SLR, "02/12/25"
|
|
127
|
+
* "AGENDADO POR SLR" → groups: SLR, undefined
|
|
128
|
+
*/
|
|
129
|
+
export declare const SCHEDULER_WITH_DATE_REGEX: RegExp;
|
|
130
|
+
/**
|
|
131
|
+
* Captures pet age expressions in Spanish from descriptions.
|
|
132
|
+
*
|
|
133
|
+
* "YORKIE 4 MESES" → "4 MESES"
|
|
134
|
+
* "11 MESES, COCKER SPANIEL" → "11 MESES"
|
|
135
|
+
* "1 año y 4 meses" → "1 año y 4 meses"
|
|
136
|
+
* "8 semanas" → "8 semanas"
|
|
137
|
+
* "2 AÑOS" → "2 AÑOS"
|
|
138
|
+
*/
|
|
139
|
+
export declare const PET_AGE_REGEX: RegExp;
|
|
140
|
+
/**
|
|
141
|
+
* Common dog/cat breeds observed in HVP descriptions. Used by the parser to
|
|
142
|
+
* detect a breed mention. Match is case-insensitive whole-word.
|
|
143
|
+
*
|
|
144
|
+
* NOT exhaustive — extend as new breeds appear in real data.
|
|
145
|
+
*/
|
|
146
|
+
export declare const KNOWN_BREEDS: readonly string[];
|
|
110
147
|
/**
|
|
111
148
|
* Keys used in Google Calendar `extendedProperties.private` for events created
|
|
112
149
|
* from HVP. Phase 2 writes these; Phase 1 reads them for full attribution.
|
|
@@ -13,25 +13,90 @@
|
|
|
13
13
|
* runs remain reproducible.
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.HVP_EVENT_PROP_KEYS = exports.SCHEDULER_REGEX = exports.PHONE_REGEX = exports.QVET_ID_REGEX = exports.CANCELLED_TITLE_REGEX = exports.REMINDER_TITLE_REGEX = exports.PREFERRED_VET_REGEX = exports.EXCLUDED_TITLE_PREFIXES = exports.SERVICE_CODE_SYNONYMS = exports.CANONICAL_SERVICE_CODES = exports.EXCLUDED_COLOR_IDS = exports.CANCELLED_COLOR_ID = exports.COLOR_ID_TO_BRANCH = exports.BRANCH_COLOR_IDS = exports.STANDARD_V1 = void 0;
|
|
16
|
+
exports.HVP_EVENT_PROP_KEYS = exports.KNOWN_BREEDS = exports.PET_AGE_REGEX = exports.SCHEDULER_WITH_DATE_REGEX = exports.SCHEDULER_REGEX = exports.PHONE_REGEX = exports.QVET_ID_REGEX = exports.CANCELLED_TITLE_REGEX = exports.REMINDER_TITLE_REGEX = exports.PREFERRED_VET_REGEX = exports.EXCLUDED_TITLE_PREFIXES = exports.SERVICE_CODE_SYNONYMS = exports.CANONICAL_SERVICE_CODES = exports.EXCLUDED_COLOR_IDS = exports.CANCELLED_COLOR_ID = exports.COLOR_ID_TO_BRANCH = exports.BRANCH_COLOR_IDS = exports.STANDARD_V1 = exports.STANDARD_V2 = void 0;
|
|
17
17
|
// ─── Standard v1 ─────────────────────────────────────────────────────────────
|
|
18
18
|
/**
|
|
19
|
-
* Multi-factor appointment standard
|
|
19
|
+
* Multi-factor appointment standard v2.
|
|
20
|
+
*
|
|
21
|
+
* Ten dimensions covering everything a recepción needs to capture to make an
|
|
22
|
+
* appointment actionable: branch, title format, pet identity (name/age/breed),
|
|
23
|
+
* tutor identity (name/phone), motive, and scheduling provenance (who and when).
|
|
20
24
|
*
|
|
21
25
|
* Weights sum to 100. An event is "compliant" when its score >= 80.
|
|
22
26
|
*/
|
|
23
|
-
exports.
|
|
24
|
-
version: "
|
|
27
|
+
exports.STANDARD_V2 = {
|
|
28
|
+
version: "2.0",
|
|
25
29
|
minimumCompliantScore: 80,
|
|
26
30
|
dimensions: [
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
{
|
|
32
|
+
id: "branch",
|
|
33
|
+
weight: 10,
|
|
34
|
+
label: "Sucursal",
|
|
35
|
+
description: 'Sucursal detectada por colorId del evento: 11 (Tomato/rojo) = Urban, 9 (Blueberry/azul) = Harbor, 3 (Grape/morado) = Montejo. Si el evento usa el color por defecto, falla esta dimensión. Atención: hay tonos parecidos en GCal — fíjate en el colorId exacto en el drawer si dudas.',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "title",
|
|
39
|
+
weight: 10,
|
|
40
|
+
label: "Título bien formado",
|
|
41
|
+
description: 'El título empieza con un código de servicio reconocible del diccionario: C (consulta), SC (sin cita), CS (consulta seguimiento), V/VAC (vacuna), CX (cirugía), CERT (certificado), USG, RX, ECO, OVH, TM (toma de muestras), AP/APL/AM (aplicación), D (desparasitación), etc.',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "pet_name",
|
|
45
|
+
weight: 10,
|
|
46
|
+
label: "Nombre de la mascota",
|
|
47
|
+
description: 'Nombre de la mascota detectable en el título (después del código de servicio y antes de las iniciales del vet). Ej: "C. MILO RGL*" → mascota = MILO.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "pet_age",
|
|
51
|
+
weight: 8,
|
|
52
|
+
label: "Edad de la mascota",
|
|
53
|
+
description: 'Edad anotada en la descripción: "4 meses", "1 año y 4 meses", "8 semanas", "2 años". Importante para vacunación, dosis y diagnóstico — sin edad, no se puede protocolizar.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "pet_breed",
|
|
57
|
+
weight: 8,
|
|
58
|
+
label: "Raza de la mascota",
|
|
59
|
+
description: 'Raza detectada en la descripción contra un diccionario de razas comunes: Yorkie, Shih Tzu, Cocker Spaniel, French Bulldog, Chihuahua, Schnauzer, Golden Retriever, Labrador, Persa, Siamés, mestizo, etc. Útil para protocolos clínicos y dosificación.',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "owner_name",
|
|
63
|
+
weight: 12,
|
|
64
|
+
label: "Nombre del tutor",
|
|
65
|
+
description: 'Nombre del cliente/tutor. Auto-pasa si hay Q{id} en la descripción (cliente linkeado a QVET) o si el teléfono matchea un cliente QVET existente. Si no, busca nombre completo del tutor en la descripción (mínimo 2 palabras, mayúsculas/minúsculas con letras).',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "owner_phone",
|
|
69
|
+
weight: 12,
|
|
70
|
+
label: "Teléfono del tutor",
|
|
71
|
+
description: 'Teléfono del tutor en cualquier formato común (con o sin +52, con o sin separadores). Auto-pasa si hay Q{id} (sabemos el teléfono del registro QVET). Necesario para confirmar la cita y avisar de cambios.',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "context",
|
|
75
|
+
weight: 10,
|
|
76
|
+
label: "Motivo / contexto",
|
|
77
|
+
description: 'La descripción tiene contexto clínico relevante (motivo de la visita, síntomas, antecedentes) más allá del nombre y teléfono. Mínimo 20 caracteres tras descontar nombre, teléfono y Q{id}.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "scheduler",
|
|
81
|
+
weight: 10,
|
|
82
|
+
label: "Iniciales de quien agendó",
|
|
83
|
+
description: 'Iniciales (col_code) del colaborador que agendó la cita, dentro de la descripción. Formatos válidos: "AGENDO XZA", "AG YMP", "AGENDADO POR SLR". Permite reportar productividad por persona.',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "scheduling_date",
|
|
87
|
+
weight: 10,
|
|
88
|
+
label: "Fecha en que se agendó",
|
|
89
|
+
description: 'Fecha de la anotación de quién agendó, junto a sus iniciales. Ejemplos: "AGENDO XZA 16 MAYO", "AG YMP 14.05.26", "AGENDADO POR SLR 02/12/25". Permite medir el lead time entre cuándo se agendó y cuándo es la cita.',
|
|
90
|
+
},
|
|
33
91
|
],
|
|
34
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Backward-compatible alias for existing code/tests that imported `STANDARD_V1`.
|
|
95
|
+
* Always points to the current standard.
|
|
96
|
+
*
|
|
97
|
+
* @deprecated Use `STANDARD_V2` (or whatever the current version is) explicitly.
|
|
98
|
+
*/
|
|
99
|
+
exports.STANDARD_V1 = exports.STANDARD_V2;
|
|
35
100
|
// ─── ColorId → Branch mapping ────────────────────────────────────────────────
|
|
36
101
|
/**
|
|
37
102
|
* Google Calendar colorId values mapped to HVP branches.
|
|
@@ -243,6 +308,108 @@ exports.PHONE_REGEX = /(?:\+?52\s?1?\s?)?(?:\d{3}[\s-]?\d{3}[\s-]?\d{4}|\d{10})/
|
|
|
243
308
|
* "AGENDADO POR SLR 02/12/25"
|
|
244
309
|
*/
|
|
245
310
|
exports.SCHEDULER_REGEX = /\b(?:AG|AGENDO|AGENDAD[OA]\s+POR)\s+([A-Z]{2,4})\b/i;
|
|
311
|
+
/**
|
|
312
|
+
* Captures the "agendado por" col_code AND the date annotation that often follows it.
|
|
313
|
+
*
|
|
314
|
+
* "AGENDO XZA 16 MAYO" → groups: XZA, "16 MAYO"
|
|
315
|
+
* "AG YMP 14.05.26" → groups: YMP, "14.05.26"
|
|
316
|
+
* "AGENDADA POR SLR 02/12/25" → groups: SLR, "02/12/25"
|
|
317
|
+
* "AGENDADO POR SLR" → groups: SLR, undefined
|
|
318
|
+
*/
|
|
319
|
+
exports.SCHEDULER_WITH_DATE_REGEX = /\b(?:AG|AGENDO|AGENDAD[OA]\s+POR)\s+([A-Z]{2,4})(?:\s+(\d{1,2}[./\-]\d{1,2}(?:[./\-]\d{2,4})?|\d{1,2}\s+(?:ENE|FEB|MAR|ABR|MAY|JUN|JUL|AGO|SEP|OCT|NOV|DIC|ENERO|FEBRERO|MARZO|ABRIL|MAYO|JUNIO|JULIO|AGOSTO|SEPTIEMBRE|OCTUBRE|NOVIEMBRE|DICIEMBRE)))?\b/i;
|
|
320
|
+
// ─── Pet age detection ───────────────────────────────────────────────────────
|
|
321
|
+
/**
|
|
322
|
+
* Captures pet age expressions in Spanish from descriptions.
|
|
323
|
+
*
|
|
324
|
+
* "YORKIE 4 MESES" → "4 MESES"
|
|
325
|
+
* "11 MESES, COCKER SPANIEL" → "11 MESES"
|
|
326
|
+
* "1 año y 4 meses" → "1 año y 4 meses"
|
|
327
|
+
* "8 semanas" → "8 semanas"
|
|
328
|
+
* "2 AÑOS" → "2 AÑOS"
|
|
329
|
+
*/
|
|
330
|
+
exports.PET_AGE_REGEX = /\b\d+\s*(?:años?|añoss?|meses?|semanas?|días?|años?\s+y\s+\d+\s+meses?)\b/i;
|
|
331
|
+
// ─── Pet breed detection ────────────────────────────────────────────────────
|
|
332
|
+
/**
|
|
333
|
+
* Common dog/cat breeds observed in HVP descriptions. Used by the parser to
|
|
334
|
+
* detect a breed mention. Match is case-insensitive whole-word.
|
|
335
|
+
*
|
|
336
|
+
* NOT exhaustive — extend as new breeds appear in real data.
|
|
337
|
+
*/
|
|
338
|
+
exports.KNOWN_BREEDS = [
|
|
339
|
+
// Perros
|
|
340
|
+
"yorkie",
|
|
341
|
+
"yorkshire",
|
|
342
|
+
"shih tzu",
|
|
343
|
+
"shihtzu",
|
|
344
|
+
"schnauzer",
|
|
345
|
+
"schnauzer mini",
|
|
346
|
+
"cocker spaniel",
|
|
347
|
+
"cocker",
|
|
348
|
+
"golden retriever",
|
|
349
|
+
"golden",
|
|
350
|
+
"labrador",
|
|
351
|
+
"labrador retriever",
|
|
352
|
+
"chihuahua",
|
|
353
|
+
"bulldog",
|
|
354
|
+
"bulldog frances",
|
|
355
|
+
"bulldog francés",
|
|
356
|
+
"bulldog ingles",
|
|
357
|
+
"bulldog inglés",
|
|
358
|
+
"french bulldog",
|
|
359
|
+
"poodle",
|
|
360
|
+
"french poodle",
|
|
361
|
+
"caniche",
|
|
362
|
+
"bichon",
|
|
363
|
+
"bichón",
|
|
364
|
+
"bichon maltes",
|
|
365
|
+
"maltes",
|
|
366
|
+
"maltés",
|
|
367
|
+
"border collie",
|
|
368
|
+
"border",
|
|
369
|
+
"pug",
|
|
370
|
+
"carlino",
|
|
371
|
+
"pastor aleman",
|
|
372
|
+
"pastor alemán",
|
|
373
|
+
"pastor",
|
|
374
|
+
"husky",
|
|
375
|
+
"husky siberiano",
|
|
376
|
+
"boxer",
|
|
377
|
+
"boxer aleman",
|
|
378
|
+
"pitbull",
|
|
379
|
+
"pit bull",
|
|
380
|
+
"american bully",
|
|
381
|
+
"doberman",
|
|
382
|
+
"rottweiler",
|
|
383
|
+
"dachshund",
|
|
384
|
+
"salchicha",
|
|
385
|
+
"beagle",
|
|
386
|
+
"dalmata",
|
|
387
|
+
"dalmata",
|
|
388
|
+
"san bernardo",
|
|
389
|
+
"akita",
|
|
390
|
+
"shar pei",
|
|
391
|
+
"sharpei",
|
|
392
|
+
"mestizo",
|
|
393
|
+
"criollo",
|
|
394
|
+
"xolo",
|
|
395
|
+
"xoloitzcuintle",
|
|
396
|
+
"chow chow",
|
|
397
|
+
// Gatos
|
|
398
|
+
"persa",
|
|
399
|
+
"siames",
|
|
400
|
+
"siamés",
|
|
401
|
+
"british shorthair",
|
|
402
|
+
"maine coon",
|
|
403
|
+
"ragdoll",
|
|
404
|
+
"bengala",
|
|
405
|
+
"esfinge",
|
|
406
|
+
"sphynx",
|
|
407
|
+
"mestizo",
|
|
408
|
+
"domestic shorthair",
|
|
409
|
+
"domestic short hair",
|
|
410
|
+
"domestico",
|
|
411
|
+
"doméstico",
|
|
412
|
+
];
|
|
246
413
|
// ─── HVP-created extendedProperties keys ─────────────────────────────────────
|
|
247
414
|
/**
|
|
248
415
|
* Keys used in Google Calendar `extendedProperties.private` for events created
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const google_calendar_constants_1 = require("./google-calendar.constants");
|
|
4
|
-
describe("
|
|
4
|
+
describe("STANDARD_V2", () => {
|
|
5
5
|
it("has weights that sum to 100", () => {
|
|
6
|
-
const total = google_calendar_constants_1.
|
|
6
|
+
const total = google_calendar_constants_1.STANDARD_V2.dimensions.reduce((sum, d) => sum + d.weight, 0);
|
|
7
7
|
expect(total).toBe(100);
|
|
8
8
|
});
|
|
9
|
-
it("declares
|
|
10
|
-
const ids = google_calendar_constants_1.
|
|
11
|
-
expect(ids).toEqual([
|
|
9
|
+
it("declares the ten expected dimensions", () => {
|
|
10
|
+
const ids = google_calendar_constants_1.STANDARD_V2.dimensions.map((d) => d.id).sort();
|
|
11
|
+
expect(ids).toEqual([
|
|
12
|
+
"branch",
|
|
13
|
+
"context",
|
|
14
|
+
"owner_name",
|
|
15
|
+
"owner_phone",
|
|
16
|
+
"pet_age",
|
|
17
|
+
"pet_breed",
|
|
18
|
+
"pet_name",
|
|
19
|
+
"scheduler",
|
|
20
|
+
"scheduling_date",
|
|
21
|
+
"title",
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
it("every dimension has a label and a description (used by the dashboard tooltip)", () => {
|
|
25
|
+
for (const dim of google_calendar_constants_1.STANDARD_V2.dimensions) {
|
|
26
|
+
expect(dim.label.length).toBeGreaterThan(0);
|
|
27
|
+
expect(dim.description.length).toBeGreaterThan(0);
|
|
28
|
+
}
|
|
12
29
|
});
|
|
13
30
|
it("has a sensible compliance threshold", () => {
|
|
14
|
-
expect(google_calendar_constants_1.
|
|
15
|
-
expect(google_calendar_constants_1.
|
|
31
|
+
expect(google_calendar_constants_1.STANDARD_V2.minimumCompliantScore).toBeGreaterThanOrEqual(50);
|
|
32
|
+
expect(google_calendar_constants_1.STANDARD_V2.minimumCompliantScore).toBeLessThanOrEqual(100);
|
|
16
33
|
});
|
|
17
|
-
it("
|
|
18
|
-
expect(google_calendar_constants_1.
|
|
34
|
+
it("uses semver-like version 2.x", () => {
|
|
35
|
+
expect(google_calendar_constants_1.STANDARD_V2.version).toMatch(/^2\.\d+$/);
|
|
19
36
|
});
|
|
20
37
|
});
|
|
21
38
|
describe("Branch ↔ colorId mapping", () => {
|
|
@@ -112,4 +129,50 @@ describe("Description regexes", () => {
|
|
|
112
129
|
expect(match?.[1]?.toUpperCase()).toBe(expected);
|
|
113
130
|
}
|
|
114
131
|
});
|
|
132
|
+
it("SCHEDULER_WITH_DATE_REGEX captures both col_code and date annotation", () => {
|
|
133
|
+
const cases = [
|
|
134
|
+
["AGENDO XZA 16 MAYO", "XZA", "16 MAYO"],
|
|
135
|
+
["AG YMP 14.05.26", "YMP", "14.05.26"],
|
|
136
|
+
["AGENDADA POR SLR 02/12/25", "SLR", "02/12/25"],
|
|
137
|
+
["AGENDADO POR SCP", "SCP", undefined],
|
|
138
|
+
];
|
|
139
|
+
for (const [text, codeExp, dateExp] of cases) {
|
|
140
|
+
const match = text.match(google_calendar_constants_1.SCHEDULER_WITH_DATE_REGEX);
|
|
141
|
+
expect(match?.[1]?.toUpperCase()).toBe(codeExp);
|
|
142
|
+
if (dateExp) {
|
|
143
|
+
expect(match?.[2]?.toUpperCase()).toBe(dateExp.toUpperCase());
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
expect(match?.[2]).toBeUndefined();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe("PET_AGE_REGEX", () => {
|
|
152
|
+
it("detects age in months / years / weeks", () => {
|
|
153
|
+
expect("YORKIE 4 MESES".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]?.toUpperCase()).toBe("4 MESES");
|
|
154
|
+
expect("11 MESES, COCKER SPANIEL".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]?.toUpperCase()).toBe("11 MESES");
|
|
155
|
+
expect("Shih tzu 1 año y 4 meses".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]?.toLowerCase()).toContain("1 año");
|
|
156
|
+
expect("8 semanas".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]).toBe("8 semanas");
|
|
157
|
+
expect("2 AÑOS".match(google_calendar_constants_1.PET_AGE_REGEX)?.[0]).toBe("2 AÑOS");
|
|
158
|
+
});
|
|
159
|
+
it("does not match arbitrary numbers", () => {
|
|
160
|
+
expect("Peso: 8.7 Kg".match(google_calendar_constants_1.PET_AGE_REGEX)).toBeNull();
|
|
161
|
+
expect("Cytopoint 30MG".match(google_calendar_constants_1.PET_AGE_REGEX)).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe("KNOWN_BREEDS", () => {
|
|
165
|
+
it("includes common breeds observed in the discovery data", () => {
|
|
166
|
+
const lower = google_calendar_constants_1.KNOWN_BREEDS.map((b) => b.toLowerCase());
|
|
167
|
+
expect(lower).toContain("yorkie");
|
|
168
|
+
expect(lower).toContain("shih tzu");
|
|
169
|
+
expect(lower).toContain("cocker spaniel");
|
|
170
|
+
expect(lower).toContain("french bulldog");
|
|
171
|
+
expect(lower).toContain("chihuahua");
|
|
172
|
+
});
|
|
173
|
+
it("includes both dog and cat breeds", () => {
|
|
174
|
+
const lower = google_calendar_constants_1.KNOWN_BREEDS.map((b) => b.toLowerCase());
|
|
175
|
+
expect(lower.some((b) => b.includes("persa"))).toBe(true);
|
|
176
|
+
expect(lower.some((b) => b.includes("maine"))).toBe(true);
|
|
177
|
+
});
|
|
115
178
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* One of the six dimensions of the appointment standard.
|
|
9
9
|
*/
|
|
10
|
-
export type AppointmentStandardDimensionId = 'branch' | '
|
|
10
|
+
export type AppointmentStandardDimensionId = 'branch' | 'title' | 'pet_name' | 'pet_age' | 'pet_breed' | 'owner_name' | 'owner_phone' | 'context' | 'scheduler' | 'scheduling_date';
|
|
11
11
|
/**
|
|
12
12
|
* One of the three branches.
|
|
13
13
|
*/
|
|
@@ -28,6 +28,8 @@ export interface AppointmentStandard {
|
|
|
28
28
|
weight: number;
|
|
29
29
|
/** Display label in Spanish. */
|
|
30
30
|
label: string;
|
|
31
|
+
/** One-sentence explanation of what the dimension evaluates (shown as a tooltip in the frontend). */
|
|
32
|
+
description: string;
|
|
31
33
|
}>;
|
|
32
34
|
/** Threshold above which an event is considered "compliant". */
|
|
33
35
|
minimumCompliantScore: number;
|
|
@@ -71,10 +73,18 @@ export interface DiagnosticResult {
|
|
|
71
73
|
inferredService: string | null;
|
|
72
74
|
/** Inferred pet name from the title. */
|
|
73
75
|
inferredPetName: string | null;
|
|
76
|
+
/** Pet age detected from description (e.g. "4 meses", "1 año"). Null if not found. */
|
|
77
|
+
inferredPetAge: string | null;
|
|
78
|
+
/** Pet breed detected from description (e.g. "YORKIE", "SHIH TZU"). Null if not found. */
|
|
79
|
+
inferredPetBreed: string | null;
|
|
80
|
+
/** Tutor (client) full name as detected from description. */
|
|
81
|
+
inferredOwnerName: string | null;
|
|
74
82
|
/** col_code of the attending vet, when detectable. */
|
|
75
83
|
inferredVetColCode: string | null;
|
|
76
84
|
/** col_code of the person who scheduled, when detectable in description. */
|
|
77
85
|
inferredSchedulerColCode: string | null;
|
|
86
|
+
/** Date string near the scheduler annotation (e.g. "16 MAYO", "02/12/25"). Null if not found. */
|
|
87
|
+
inferredSchedulingDate: string | null;
|
|
78
88
|
/** Q{id} or null. */
|
|
79
89
|
inferredQvetClientId: string | null;
|
|
80
90
|
/** Last 10 digits of phone, normalized. Null if not found. */
|