From 0acc123dd6627847a68ac84bf295bf827490da52 Mon Sep 17 00:00:00 2001 From: Aaron Putterman <60486980+abp6318@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:42:27 -0400 Subject: [PATCH 1/4] feat: scheduler dialog on dataset route --- packages/common/src/content/manageSchedule.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/common/src/content/manageSchedule.ts b/packages/common/src/content/manageSchedule.ts index 96596e77c3c..bc49abaf699 100644 --- a/packages/common/src/content/manageSchedule.ts +++ b/packages/common/src/content/manageSchedule.ts @@ -165,7 +165,8 @@ export const isDownloadSchedulingAvailable = ( ): boolean => { const token = requestOptions.authentication?.token; return ( - requestOptions.portal?.includes("arcgis.com") && + (requestOptions.portal?.includes("arcgis.com") || + requestOptions.authentication?.portal.includes("arcgis.com")) && access === "public" && !!token ); From d7b64c708e651c243ced959827fb4d87f01df471 Mon Sep 17 00:00:00 2001 From: Aaron Putterman <60486980+abp6318@users.noreply.github.com> Date: Wed, 25 Sep 2024 08:51:46 -0400 Subject: [PATCH 2/4] feat: next occurance utils --- packages/common/src/core/index.ts | 1 + .../common/src/core/nextOccuranceUtils.ts | 220 ++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 packages/common/src/core/nextOccuranceUtils.ts diff --git a/packages/common/src/core/index.ts b/packages/common/src/core/index.ts index fe1fb9d3409..159076673fc 100644 --- a/packages/common/src/core/index.ts +++ b/packages/common/src/core/index.ts @@ -14,6 +14,7 @@ export * from "./shareEntityWithGroups"; export * from "./unshareEntityWithGroups"; export * from "./getEntityGroups"; export * from "./getEntityThumbnailUrl"; +export * from "./nextOccuranceUtils"; // For sme reason, if these are exported here, // they are not actually exported in the final package. diff --git a/packages/common/src/core/nextOccuranceUtils.ts b/packages/common/src/core/nextOccuranceUtils.ts new file mode 100644 index 00000000000..cada6711af1 --- /dev/null +++ b/packages/common/src/core/nextOccuranceUtils.ts @@ -0,0 +1,220 @@ +import { IHubSchedule } from "./types"; + +// IHubSchedule utils + +/** + * Get the next daily occurance of the specified hour in the specified timezone + * @param hour takes an hour of the day between 0 and 23 + * @param timezone takes a timezone string + * @returns a Date object representing the next daily occurance of the specified hour + */ +const getNextDailyOccurance = (hour: number, timezone: string): Date => { + validateHour(hour); + + // Get the time in the specified timezone + const targetTime = getInitialTargetTime(timezone, "en-US"); + + if (targetTime.getHours() < hour) { + targetTime.setHours(hour, 0, 0, 0); + } else { + targetTime.setDate(targetTime.getDate() + 1); + targetTime.setHours(hour, 0, 0, 0); + } + + return targetTime; +}; + +/** + * Get the next weekly occurance of the specified hour in the specified timezone + * @param hour takes an hour of the day between 0 and 23 + * @param timezone takes a timezone string + * @param dayOfWeek takes a day of the week between 0 (Sunday) and 6 (Saturday) + * @returns a Date object representing the next weekly occurance of the specified hour + */ +const getNextWeeklyOccurance = ( + hour: number, + timezone: string, + dayOfWeek: number +): Date => { + validateHour(hour); + validateDayOfWeek(dayOfWeek); + + // Get the time in the specified timezone + const targetTime = getInitialTargetTime(timezone, "en-US"); + + // Calculate the difference in days between today and the next occurrence of the specified day of the week + const currentDayOfWeek = targetTime.getDay(); + let daysUntilNextEvent = (dayOfWeek - currentDayOfWeek + 7) % 7; + + // If the event is today but the hour has already passed, schedule it for next week + if (daysUntilNextEvent === 0 && targetTime.getHours() >= hour) { + daysUntilNextEvent = 7; + } + + // Set the event time to the next occurrence of the specified day at the given hour + targetTime.setDate(targetTime.getDate() + daysUntilNextEvent); + targetTime.setHours(hour, 0, 0, 0); + + // Convert the event time to an ISO string + return targetTime; +}; + +/** + * Get the next monthly occurance of the specified hour in the specified timezone + * @param hour takes an hour of the day between 0 and 23 + * @param timezone takes a timezone string + * @param dayOfMonth takes a day of the week between 1 and 28 + * @returns a Date object representing the next monthly occurance of the specified hour + */ +const getNextMonthlyOccurance = ( + hour: number, + timezone: string, + dayOfMonth: number +): Date => { + validateHour(hour); + validateDayOfMonth(dayOfMonth); + + // Get the time in the specified timezone + const targetTime = getInitialTargetTime(timezone, "en-US"); + + // Calculate the difference in days between today and the next occurrence of the specified day of the month + const currentDayOfMonth = targetTime.getDate(); + let daysUntilNextEvent = dayOfMonth - currentDayOfMonth; + + // If the event is today but the hour has already passed, schedule it for next month + if (daysUntilNextEvent === 0 && targetTime.getHours() >= hour) { + daysUntilNextEvent = 0; + targetTime.setMonth(targetTime.getMonth() + 1); + } else if (daysUntilNextEvent < 0) { + // If the day of the month has already passed, schedule it for next month + targetTime.setMonth(targetTime.getMonth() + 1); + targetTime.setDate(dayOfMonth); + } else { + // Set the event time to the next occurrence of the specified day at the given hour + targetTime.setDate(dayOfMonth); + } + + targetTime.setHours(hour, 0, 0, 0); + + // Convert the event time to an ISO string + return targetTime; +}; + +/** + * Get the next yearly occurance of the specified hour in the specified timezone + * @param hour takes an hour of the day between 0 and 23 + * @param timezone takes a timezone string + * @param dayOfMonth takes a day of the week between 1 and 28 + * @param month takes a month of the year between 0 and 11 + * @returns a Date object representing the next yearly occurance of the specified hour + */ +const getNextYearlyOccurance = ( + hour: number, + timezone: string, + dayOfMonth: number, + month: number +): Date => { + validateHour(hour); + validateDayOfMonth(dayOfMonth); + validateMonth(month); + + // Get the time in the specified timezone + const targetTime = getInitialTargetTime(timezone, "en-US"); + + // Calculate the next occurrence of the specified month and day + const currentMonth = targetTime.getMonth(); + const currentDayOfMonth = targetTime.getDate(); + const currentYear = targetTime.getFullYear(); + + if ( + currentMonth > month || + (currentMonth === month && + (currentDayOfMonth > dayOfMonth || + (currentDayOfMonth === dayOfMonth && targetTime.getHours() >= hour))) + ) { + // If the specified month and day have already passed this year, schedule it for next year + targetTime.setFullYear(currentYear + 1); + } + targetTime.setMonth(month); + targetTime.setDate(dayOfMonth); + targetTime.setHours(hour, 0, 0, 0); + + // Convert the event time to an ISO string + return targetTime; +}; + +/** + * Get the next occurance of the specified schedule + * @param schedule takes an IHubSchedule object + * @returns a Date object representing the next occurance of the specified schedule + */ +export const getNextOccurance = (schedule: IHubSchedule): Date => { + if (schedule.mode === "scheduled") { + switch (schedule.cadence) { + case "daily": + return getNextDailyOccurance(schedule.hour, schedule.timezone); + case "weekly": + return getNextWeeklyOccurance( + schedule.hour, + schedule.timezone, + schedule.day + ); + case "monthly": + return getNextMonthlyOccurance( + schedule.hour, + schedule.timezone, + schedule.date + ); + case "yearly": + return getNextYearlyOccurance( + schedule.hour, + schedule.timezone, + schedule.date, + schedule.month + ); + default: + throw new Error("Invalid cadence"); + } + } else { + // If the schedule mode is not "scheduled", return undefined because it is either "automatic" or "manual" + return undefined; + } +}; + +/****** REPEATED CODE ******/ +const getInitialTargetTime = (timezone: string, locale: string): Date => { + const now = new Date(); + const utcOffset = now.getTimezoneOffset() * 60000; + const localTime = new Date(now.getTime() + utcOffset); + const targetTime = new Date( + localTime.toLocaleString(locale, { timeZone: timezone }) + ); + return targetTime; +}; + +/****** VALIDATION ******/ +const validateHour = (hour: number): void => { + if (hour < 0 || hour > 23) { + throw new Error("Hour must be between 0 and 23"); + } +}; + +const validateDayOfWeek = (dayOfWeek: number): void => { + if (dayOfWeek < 0 || dayOfWeek > 6) { + throw new Error( + "Day of the week must be between 0 (Sunday) and 6 (Saturday)" + ); + } +}; + +const validateMonth = (month: number): void => { + if (month < 0 || month > 11) { + throw new Error("Month must be between 0 and 11"); + } +}; + +const validateDayOfMonth = (dayOfMonth: number): void => { + if (dayOfMonth < 1 || dayOfMonth > 28) { + throw new Error("Day of the month must be between 1 and 28"); + } +}; From 77419b9f9c58142a2eda0bf9cf0f422e68b90090 Mon Sep 17 00:00:00 2001 From: Aaron Putterman <60486980+abp6318@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:08:14 -0400 Subject: [PATCH 3/4] feat: added dynamic locale parameter --- packages/common/src/core/index.ts | 2 +- .../{nextOccuranceUtils.ts => schedule.ts} | 51 ++++++++++++++----- 2 files changed, 38 insertions(+), 15 deletions(-) rename packages/common/src/core/{nextOccuranceUtils.ts => schedule.ts} (85%) diff --git a/packages/common/src/core/index.ts b/packages/common/src/core/index.ts index 159076673fc..c454dbb2c89 100644 --- a/packages/common/src/core/index.ts +++ b/packages/common/src/core/index.ts @@ -14,7 +14,7 @@ export * from "./shareEntityWithGroups"; export * from "./unshareEntityWithGroups"; export * from "./getEntityGroups"; export * from "./getEntityThumbnailUrl"; -export * from "./nextOccuranceUtils"; +export * from "./schedule"; // For sme reason, if these are exported here, // they are not actually exported in the final package. diff --git a/packages/common/src/core/nextOccuranceUtils.ts b/packages/common/src/core/schedule.ts similarity index 85% rename from packages/common/src/core/nextOccuranceUtils.ts rename to packages/common/src/core/schedule.ts index cada6711af1..7f0b8deec35 100644 --- a/packages/common/src/core/nextOccuranceUtils.ts +++ b/packages/common/src/core/schedule.ts @@ -6,13 +6,18 @@ import { IHubSchedule } from "./types"; * Get the next daily occurance of the specified hour in the specified timezone * @param hour takes an hour of the day between 0 and 23 * @param timezone takes a timezone string + * @param locale takes a locale string * @returns a Date object representing the next daily occurance of the specified hour */ -const getNextDailyOccurance = (hour: number, timezone: string): Date => { +const getNextDailyOccurance = ( + hour: number, + timezone: string, + locale: string +): Date => { validateHour(hour); // Get the time in the specified timezone - const targetTime = getInitialTargetTime(timezone, "en-US"); + const targetTime = getInitialTargetTime(timezone, locale); if (targetTime.getHours() < hour) { targetTime.setHours(hour, 0, 0, 0); @@ -29,18 +34,20 @@ const getNextDailyOccurance = (hour: number, timezone: string): Date => { * @param hour takes an hour of the day between 0 and 23 * @param timezone takes a timezone string * @param dayOfWeek takes a day of the week between 0 (Sunday) and 6 (Saturday) + * @param locale takes a locale string * @returns a Date object representing the next weekly occurance of the specified hour */ const getNextWeeklyOccurance = ( hour: number, timezone: string, - dayOfWeek: number + dayOfWeek: number, + locale: string ): Date => { validateHour(hour); validateDayOfWeek(dayOfWeek); // Get the time in the specified timezone - const targetTime = getInitialTargetTime(timezone, "en-US"); + const targetTime = getInitialTargetTime(timezone, locale); // Calculate the difference in days between today and the next occurrence of the specified day of the week const currentDayOfWeek = targetTime.getDay(); @@ -64,18 +71,20 @@ const getNextWeeklyOccurance = ( * @param hour takes an hour of the day between 0 and 23 * @param timezone takes a timezone string * @param dayOfMonth takes a day of the week between 1 and 28 + * @param locale takes a locale string * @returns a Date object representing the next monthly occurance of the specified hour */ const getNextMonthlyOccurance = ( hour: number, timezone: string, - dayOfMonth: number + dayOfMonth: number, + locale: string ): Date => { validateHour(hour); validateDayOfMonth(dayOfMonth); // Get the time in the specified timezone - const targetTime = getInitialTargetTime(timezone, "en-US"); + const targetTime = getInitialTargetTime(timezone, locale); // Calculate the difference in days between today and the next occurrence of the specified day of the month const currentDayOfMonth = targetTime.getDate(); @@ -106,20 +115,22 @@ const getNextMonthlyOccurance = ( * @param timezone takes a timezone string * @param dayOfMonth takes a day of the week between 1 and 28 * @param month takes a month of the year between 0 and 11 + * @param locale takes a locale string * @returns a Date object representing the next yearly occurance of the specified hour */ const getNextYearlyOccurance = ( hour: number, timezone: string, dayOfMonth: number, - month: number + month: number, + locale: string ): Date => { validateHour(hour); validateDayOfMonth(dayOfMonth); validateMonth(month); // Get the time in the specified timezone - const targetTime = getInitialTargetTime(timezone, "en-US"); + const targetTime = getInitialTargetTime(timezone, locale); // Calculate the next occurrence of the specified month and day const currentMonth = targetTime.getMonth(); @@ -146,31 +157,38 @@ const getNextYearlyOccurance = ( /** * Get the next occurance of the specified schedule * @param schedule takes an IHubSchedule object + * @param locale takes a locale string * @returns a Date object representing the next occurance of the specified schedule */ -export const getNextOccurance = (schedule: IHubSchedule): Date => { +export const getNextOccurance = ( + schedule: IHubSchedule, + locale: string +): Date => { if (schedule.mode === "scheduled") { switch (schedule.cadence) { case "daily": - return getNextDailyOccurance(schedule.hour, schedule.timezone); + return getNextDailyOccurance(schedule.hour, schedule.timezone, locale); case "weekly": return getNextWeeklyOccurance( schedule.hour, schedule.timezone, - schedule.day + schedule.day, + locale ); case "monthly": return getNextMonthlyOccurance( schedule.hour, schedule.timezone, - schedule.date + schedule.date, + locale ); case "yearly": return getNextYearlyOccurance( schedule.hour, schedule.timezone, schedule.date, - schedule.month + schedule.month, + locale ); default: throw new Error("Invalid cadence"); @@ -181,7 +199,12 @@ export const getNextOccurance = (schedule: IHubSchedule): Date => { } }; -/****** REPEATED CODE ******/ +/** + * Get the initial target time in the specified timezone + * @param timezone the timezone string + * @param locale the locale string + * @returns a Date object representing the initial target time in the specified timezone + */ const getInitialTargetTime = (timezone: string, locale: string): Date => { const now = new Date(); const utcOffset = now.getTimezoneOffset() * 60000; From 3768bae07b4a264dac253cd335f2b59ad80b64d2 Mon Sep 17 00:00:00 2001 From: Aaron Putterman <60486980+abp6318@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:51:13 -0400 Subject: [PATCH 4/4] feat: tests stashing before dnm is applied --- packages/common/src/core/schedule.ts | 3 +- packages/common/test/core/schedule.test.ts | 35 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 packages/common/test/core/schedule.test.ts diff --git a/packages/common/src/core/schedule.ts b/packages/common/src/core/schedule.ts index 7f0b8deec35..034e9d7d9a8 100644 --- a/packages/common/src/core/schedule.ts +++ b/packages/common/src/core/schedule.ts @@ -1,6 +1,7 @@ import { IHubSchedule } from "./types"; -// IHubSchedule utils +// IHubSchedule util +// TODO: add comments for date math /** * Get the next daily occurance of the specified hour in the specified timezone diff --git a/packages/common/test/core/schedule.test.ts b/packages/common/test/core/schedule.test.ts new file mode 100644 index 00000000000..b4a2b12cb18 --- /dev/null +++ b/packages/common/test/core/schedule.test.ts @@ -0,0 +1,35 @@ + + +// TODO +describe("getNextOccurance", () => { + + it('no next occurrance, mode is automatic or manual', async () => { + const automatic = { mode: "automatic" }; + const manual = { mode: "manual" }; + // expect(getNextOccurance(automatic, "en-US").).toBe + }); + + it('is of mode scheduled, but invalid cadence', async () => { + + }); + + describe("gets daily occurrance", () => { + + }); + + describe("gets weekly occurrance", () => { + + }); + + describe("gets monthly occurrance", () => { + + }); + + describe("gets yearly occurrance", () => { + + }); + + describe("validation methods", () => { + + }); +});