Skip to content

Commit

Permalink
fix logic and add better test
Browse files Browse the repository at this point in the history
  • Loading branch information
bracesproul committed Jan 27, 2025
1 parent 8909786 commit 5cbd594
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 45 deletions.
92 changes: 56 additions & 36 deletions src/agents/generate-post/nodes/schedule-post/find-date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,7 @@ export const ALLOWED_P3_DAY_AND_TIMES_IN_UTC = [
{ day: 1, hour: 1 },
];

// const FIRST_ALLOWED_P3_HOUR_WEEKEND = 21;
// const LAST_ALLOWED_P3_HOUR_WEEKEND = 23;
// const FIRST_ALLOWED_P3_HOUR_MONDAY = 0;
// const LAST_ALLOWED_P3_HOUR_MONDAY = 1;

type TakenScheduleDates = {
export type TakenScheduleDates = {
p1: Date[];
p2: Date[];
p3: Date[];
Expand Down Expand Up @@ -459,36 +454,47 @@ function getNextAvailableDate({
),
);
for (let i = 0; i < 14; i += 1) {
const day = tmp.getUTCDay();
const currentHour = tmp.getUTCHours();
const allowedSlots =
priority === "p1"
? ALLOWED_P1_DAY_AND_TIMES_IN_UTC
: priority === "p2"
? ALLOWED_P2_DAY_AND_TIMES_IN_UTC
: ALLOWED_P3_DAY_AND_TIMES_IN_UTC;
const day = tmp.getUTCDay();
const hr = tmp.getUTCHours();
// Find any slot on this day >= current hour
const sameDaySlots = allowedSlots
.filter((s) => s.day === day && s.hour >= hr)

// Only allow hours >= currentHour, but if we're exactly on currentHour, minutes must be 0
const validSlots = allowedSlots
.filter((s) => s.day === day)
.filter((s) => {
// skip all slots strictly less than current hour, or equal.
if (s.hour <= currentHour) return false;
return true;
})
.sort((a, b) => a.hour - b.hour);
if (sameDaySlots.length) {

if (validSlots.length) {
candidate = new Date(
Date.UTC(
tmp.getUTCFullYear(),
tmp.getUTCMonth(),
tmp.getUTCDate(),
sameDaySlots[0].hour,
validSlots[0].hour,
),
);
// Now candidate is guaranteed >= dateToCheck
break;
}
// No valid slot today, move to next midnight

// move to next day at midnight
tmp = new Date(
Date.UTC(tmp.getUTCFullYear(), tmp.getUTCMonth(), tmp.getUTCDate() + 1),
);
}
if (candidate < dateToCheck) {
throw new Error(`No valid future slot found for ${priority}.`);
throw new Error(
`No valid future slot found for ${priority} within 2 weeks of ${dateToCheck}`,
);
}
}

Expand Down Expand Up @@ -633,35 +639,49 @@ export async function getScheduledDateSeconds(
}
}

const nextAvailDate = getNextAvailableDate({
dateToCheck: currentTime,
priority,
takenDates: takenScheduleDates,
});
if (!nextAvailDate) {
throw new Error("Received no available times");
}

const isValidDate = validateScheduleDate(nextAvailDate, baseDate);

if (!isValidDate) {
// Send a message to slack
if (process.env.SLACK_CHANNEL_ID && process.env.SLACK_CHANNEL_ID) {
const slackClient = new SlackClient({
channelId: process.env.SLACK_CHANNEL_ID,
});

await slackClient.sendMessage(`**FAILED TO SCHEDULE POST**
let nextAvailDate: Date | undefined;
try {
nextAvailDate = getNextAvailableDate({
dateToCheck: currentTime,
priority,
takenDates: takenScheduleDates,
});
if (!nextAvailDate) {
throw new Error("Received no available times");
}
} catch (e: any) {
if (
"message" in e &&
e.message.includes("No valid future slot found for")
) {
// Send a message to slack
if (process.env.SLACK_CHANNEL_ID && process.env.SLACK_CHANNEL_ID) {
const slackClient = new SlackClient({
channelId: process.env.SLACK_CHANNEL_ID,
});

await slackClient.sendMessage(`**FAILED TO FIND DATE TO SCHEDULE POST**
Error message:
\`\`\`
${e.message}
\`\`\`
Priority: ${priority}
Schedule date: ${format(nextAvailDate, "MM/dd/yyyy hh:mm a z")}
Base date: ${format(baseDate, "MM/dd/yyyy hh:mm a z")}
Thread ID: ${config.configurable?.thread_id || "No thread ID found"}
Run ID: ${config.configurable?.run_id || "No run ID found"}
`);
`);
}
}

throw e;
}

const isValidDate = validateScheduleDate(nextAvailDate, baseDate);

if (!isValidDate) {
throw new Error(`FAILED TO SCHEDULE POST
Priority: ${priority}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { jest, describe, it, expect } from "@jest/globals";
import { jest, describe, it, expect, afterAll, afterEach } from "@jest/globals";
import { InMemoryStore } from "@langchain/langgraph";

import {
getScheduledDateSeconds,
getTakenScheduleDates,
putTakenScheduleDates,
TakenScheduleDates,
} from "../find-date.js";

// Define MOCK_CURRENT_DATE in UTC or as per the mocked timezone
const MOCK_CURRENT_DATE = new Date("2025-01-03T12:00:00.000Z"); // This aligns with 'America/Los_Angeles'
// const MOCK_CURRENT_DATE = new Date()
describe("Priority P1 get scheduled date", () => {
// Define MOCK_CURRENT_DATE in UTC or as per the mocked timezone
const MOCK_CURRENT_DATE = new Date("2025-01-03T12:00:00.000Z"); // This aligns with 'America/Los_Angeles'

jest.useFakeTimers();
jest.setSystemTime(MOCK_CURRENT_DATE);
jest.useFakeTimers();
jest.setSystemTime(MOCK_CURRENT_DATE);

afterAll(() => {
jest.useRealTimers();
});

describe("Priority P1 get scheduled date", () => {
const EXPECTED_DATE_TIMES = [
"2025-01-04T16:00:00.000Z",
"2025-01-04T17:00:00.000Z",
Expand Down Expand Up @@ -101,6 +106,16 @@ describe("Priority P1 get scheduled date", () => {
});

describe("Priority P2 get scheduled date", () => {
// Define MOCK_CURRENT_DATE in UTC or as per the mocked timezone
const MOCK_CURRENT_DATE = new Date("2025-01-03T12:00:00.000Z"); // This aligns with 'America/Los_Angeles'

jest.useFakeTimers();
jest.setSystemTime(MOCK_CURRENT_DATE);

afterAll(() => {
jest.useRealTimers();
});

const EXPECTED_DATE_TIMES = [
// Monday/Friday
"2025-01-03T16:00:00.000Z",
Expand Down Expand Up @@ -200,6 +215,16 @@ describe("Priority P2 get scheduled date", () => {
});

describe("Priority P3 get scheduled date", () => {
// Define MOCK_CURRENT_DATE in UTC or as per the mocked timezone
const MOCK_CURRENT_DATE = new Date("2025-01-03T12:00:00.000Z"); // This aligns with 'America/Los_Angeles'

jest.useFakeTimers();
jest.setSystemTime(MOCK_CURRENT_DATE);

afterAll(() => {
jest.useRealTimers();
});

const EXPECTED_DATE_TIMES = [
// Weekend 1
"2025-01-04T21:00:00.000Z",
Expand Down Expand Up @@ -253,7 +278,6 @@ describe("Priority P3 get scheduled date", () => {
"2025-01-27T00:00:00.000Z",
"2025-01-27T01:00:00.000Z",
];
console.log("EXPECTED_DATE_TIMES", EXPECTED_DATE_TIMES.length);

it("can properly find and schedule dates", async () => {
const store = new InMemoryStore();
Expand All @@ -268,7 +292,6 @@ describe("Priority P3 get scheduled date", () => {
}

const scheduledDates = await getTakenScheduleDates(config);
console.log("scheduledDates", scheduledDates.p3);
expect(scheduledDates.p3.length).toBe(40);

// Convert both arrays to ISO strings and sort them for comparison
Expand All @@ -283,3 +306,59 @@ describe("Priority P3 get scheduled date", () => {
);
});
});

describe.skip("Get scheduled dates", () => {
// Reset the timer after each test, but individual tests may set their own timers
afterEach(() => {
jest.useRealTimers();
});

it("Can schedule for under an hour from the current time", async () => {
const defaultTakenDates: TakenScheduleDates = {
p1: [
new Date("2025-01-18T16:00:00.000Z"),
new Date("2025-01-18T17:00:00.000Z"),
new Date("2025-01-18T18:00:00.000Z"),
new Date("2025-01-19T16:00:00.000Z"),
new Date("2025-01-19T17:00:00.000Z"),
new Date("2025-01-19T18:00:00.000Z"),
],
p2: [
new Date("2025-01-17T17:00:00.000Z"),
new Date("2025-01-17T18:00:00.000Z"),
new Date("2025-01-18T19:00:00.000Z"),
new Date("2025-01-18T20:00:00.000Z"),
new Date("2025-01-18T21:00:00.000Z"),
new Date("2025-01-19T19:00:00.000Z"),
new Date("2025-01-19T20:00:00.000Z"),
new Date("2025-01-19T21:00:00.000Z"),
new Date("2025-01-20T16:00:00.000Z"),
],
p3: [
new Date("2025-01-18T21:00:00.000Z"),
new Date("2025-01-18T22:00:00.000Z"),
],
};
const store = new InMemoryStore();
const config = {
store,
};
await putTakenScheduleDates(defaultTakenDates, config);

// This is 8:04 AM PST (16:04 UTC)
const mockCurrentDate = new Date("2025-01-25T16:04:00.000Z");
jest.useFakeTimers();
jest.setSystemTime(mockCurrentDate);

const scheduledDate = await getScheduledDateSeconds(
"p1",
config,
mockCurrentDate,
);
expect(scheduledDate).toBeDefined();
// It should be 9AM, so check it's more than 3300 sec (55 min) and less than 3600 sec (1 hour)
// If this is true, then it means the post was likely scheduled for 9AM.
expect(scheduledDate).toBeGreaterThan(3300);
expect(scheduledDate).toBeLessThan(3600);
});
});

0 comments on commit 5cbd594

Please sign in to comment.