Fix Monday.com date parsing and normalize dropdown labels
- Rewrite parseToISO to handle many OCR date formats: "july 20, 02", "20/May/1999", "08/JUN/26", "Oct / 02 / 2004", "Feb 19, 2016", etc. - Return null instead of raw string when date parsing fails, preventing Monday.com ColumnValueException for unparseable dates - Normalize long nextStep text from pre-update OCR runs (e.g. "Expressing my faith in Jesus..." -> "Baptism") before sending to Monday.com dropdowns Made-with: Cursor
This commit is contained in:
parent
daec81884c
commit
ec51c049c3
1 changed files with 91 additions and 9 deletions
|
|
@ -155,6 +155,20 @@ function toCleanString(val: unknown): string | null {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DROPDOWN_NORMALIZATIONS: [RegExp, string][] = [
|
||||||
|
[/expressing my faith in jesus/i, "Baptism"],
|
||||||
|
[/i want to be baptized/i, "Baptism"],
|
||||||
|
[/learning more about becoming a partner/i, "Next Steps"],
|
||||||
|
[/attend next steps/i, "Next Steps"],
|
||||||
|
];
|
||||||
|
|
||||||
|
function normalizeDropdownLabel(label: string): string {
|
||||||
|
for (const [pattern, replacement] of DROPDOWN_NORMALIZATIONS) {
|
||||||
|
if (pattern.test(label)) return replacement;
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
function formatForColumnType(colType: string, val: unknown): unknown | null {
|
function formatForColumnType(colType: string, val: unknown): unknown | null {
|
||||||
switch (colType) {
|
switch (colType) {
|
||||||
case "boolean":
|
case "boolean":
|
||||||
|
|
@ -172,15 +186,20 @@ function formatForColumnType(colType: string, val: unknown): unknown | null {
|
||||||
case "dropdown": {
|
case "dropdown": {
|
||||||
const str = toCleanString(val);
|
const str = toCleanString(val);
|
||||||
if (!str) return null;
|
if (!str) return null;
|
||||||
const labels = str.split(",").map((s) => s.trim()).filter(Boolean);
|
const labels = str.split(",").map((s) => normalizeDropdownLabel(s.trim())).filter(Boolean);
|
||||||
return labels.length > 0 ? { labels } : null;
|
return labels.length > 0 ? { labels } : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "date": {
|
case "date": {
|
||||||
const str = toCleanString(val);
|
const str = toCleanString(val);
|
||||||
if (!str) return null;
|
if (!str) return null;
|
||||||
|
if (typeof val === "object" && !Array.isArray(val) && (val as Record<string, unknown>).date) {
|
||||||
|
const inner = String((val as Record<string, unknown>).date);
|
||||||
|
const iso = parseToISO(inner);
|
||||||
|
return iso ? { date: iso } : null;
|
||||||
|
}
|
||||||
const iso = parseToISO(str);
|
const iso = parseToISO(str);
|
||||||
return iso ? { date: iso } : str;
|
return iso ? { date: iso } : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "numeric":
|
case "numeric":
|
||||||
|
|
@ -212,14 +231,77 @@ function formatForColumnType(colType: string, val: unknown): unknown | null {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MONTH_MAP: Record<string, string> = {
|
||||||
|
jan: "01", january: "01", feb: "02", february: "02",
|
||||||
|
mar: "03", march: "03", apr: "04", april: "04",
|
||||||
|
may: "05", jun: "06", june: "06", jul: "07", july: "07",
|
||||||
|
aug: "08", august: "08", sep: "09", september: "09",
|
||||||
|
oct: "10", october: "10", nov: "11", november: "11",
|
||||||
|
dec: "12", december: "12",
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizeYear(y: string): string {
|
||||||
|
if (y.length === 4) return y;
|
||||||
|
const n = parseInt(y);
|
||||||
|
return n > 50 ? `19${y.padStart(2, "0")}` : `20${y.padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
function parseToISO(dateStr: string): string | null {
|
function parseToISO(dateStr: string): string | null {
|
||||||
const parts = dateStr.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{2,4})$/);
|
const s = dateStr.replace(/\s+/g, " ").trim();
|
||||||
if (!parts) return null;
|
|
||||||
let [, m, d, y] = parts;
|
// MM/DD/YYYY or DD/MM/YYYY (all-numeric)
|
||||||
if (y.length === 2) y = parseInt(y) > 50 ? `19${y}` : `20${y}`;
|
const numParts = s.match(/^(\d{1,2})\s*[/\-.]\s*(\d{1,2})\s*[/\-.]\s*(\d{2,4})$/);
|
||||||
const month = m.padStart(2, "0");
|
if (numParts) {
|
||||||
const day = d.padStart(2, "0");
|
let [, a, b, y] = numParts;
|
||||||
return `${y}-${month}-${day}`;
|
y = normalizeYear(y);
|
||||||
|
const aNum = parseInt(a), bNum = parseInt(b);
|
||||||
|
let month: string, day: string;
|
||||||
|
if (aNum > 12 && bNum <= 12) {
|
||||||
|
day = a; month = b;
|
||||||
|
} else {
|
||||||
|
month = a; day = b;
|
||||||
|
}
|
||||||
|
return `${y}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DD/Mon/YYYY e.g. "20/May/1999", "08/JUN/26"
|
||||||
|
const dmyNamed = s.match(/^(\d{1,2})\s*[/\-.]\s*([A-Za-z]+)\s*[/\-.]\s*(\d{2,4})$/);
|
||||||
|
if (dmyNamed) {
|
||||||
|
const [, d, mName, y] = dmyNamed;
|
||||||
|
const month = MONTH_MAP[mName.toLowerCase()];
|
||||||
|
if (month) return `${normalizeYear(y)}-${month}-${d.padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Month DD, YY(YY)" e.g. "july 20, 02", "Feb 19, 2016", "October 5, 1990"
|
||||||
|
const mdyNamed = s.match(/^([A-Za-z]+)\s+(\d{1,2}),?\s*(\d{2,4})$/);
|
||||||
|
if (mdyNamed) {
|
||||||
|
const [, mName, d, y] = mdyNamed;
|
||||||
|
const month = MONTH_MAP[mName.toLowerCase()];
|
||||||
|
if (month) return `${normalizeYear(y)}-${month}-${d.padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Mon / DD / YYYY" with spaces e.g. "Oct / 02 / 2004"
|
||||||
|
const spacedNamed = s.match(/^([A-Za-z]+)\s*[/\-.]\s*(\d{1,2})\s*[/\-.]\s*(\d{2,4})$/);
|
||||||
|
if (spacedNamed) {
|
||||||
|
const [, mName, d, y] = spacedNamed;
|
||||||
|
const month = MONTH_MAP[mName.toLowerCase()];
|
||||||
|
if (month) return `${normalizeYear(y)}-${month}-${d.padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// YYYY-MM-DD already ISO
|
||||||
|
const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||||
|
if (iso) return s;
|
||||||
|
|
||||||
|
// Last resort: try native Date parsing
|
||||||
|
const d = new Date(s);
|
||||||
|
if (!isNaN(d.getTime()) && d.getFullYear() > 1900) {
|
||||||
|
const yyyy = String(d.getFullYear());
|
||||||
|
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
||||||
|
const dd = String(d.getDate()).padStart(2, "0");
|
||||||
|
return `${yyyy}-${mm}-${dd}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapCardToColumnValues(
|
export function mapCardToColumnValues(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue