Defer weekend document reminders to Monday
Reminders that fall due on Saturday or Sunday no longer send over the weekend or spend the escalation ladder unused. SQL keeps calendar cadence and rolls weekend due times to Monday at the same clock hour. The first debounced notice is unchanged. Co-authored-by: Sacha Al Himdani <sacha@probo.com> Signed-off-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
@@ -420,6 +420,7 @@ func (d *DocumentVersionApprovalDecisions) LoadNextDueGroupForNotification(
|
||||
debounceBefore time.Time,
|
||||
reminderInterval time.Duration,
|
||||
) error {
|
||||
// Reminders skip Sat/Sun; a weekend due time waits until Monday same UTC hour.
|
||||
q := `
|
||||
WITH next_group AS (
|
||||
SELECT
|
||||
@@ -432,7 +433,25 @@ WITH next_group AS (
|
||||
AND notification_count < @max_notifications
|
||||
AND (
|
||||
(notification_count = 0 AND created_at < @debounce_before)
|
||||
OR (notification_count > 0 AND last_notified_at < @now::timestamptz - make_interval(secs => @reminder_interval_seconds * notification_count))
|
||||
OR (
|
||||
notification_count > 0
|
||||
AND EXTRACT(ISODOW FROM (@now::timestamptz AT TIME ZONE 'UTC')) BETWEEN 1 AND 5
|
||||
AND @now::timestamptz > (
|
||||
SELECT
|
||||
(due_at_utc + CASE EXTRACT(ISODOW FROM due_at_utc)
|
||||
WHEN 6 THEN interval '2 days'
|
||||
WHEN 7 THEN interval '1 day'
|
||||
ELSE interval '0'
|
||||
END) AT TIME ZONE 'UTC'
|
||||
FROM (
|
||||
SELECT
|
||||
(
|
||||
last_notified_at
|
||||
+ make_interval(secs => @reminder_interval_seconds * notification_count)
|
||||
) AT TIME ZONE 'UTC' AS due_at_utc
|
||||
) AS reminder
|
||||
)
|
||||
)
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
@@ -531,6 +550,7 @@ func (d DocumentVersionApprovalDecisions) ClaimForNotification(
|
||||
ids[i] = decision.ID
|
||||
}
|
||||
|
||||
// Reminders skip Sat/Sun; a weekend due time waits until Monday same UTC hour.
|
||||
q := `
|
||||
UPDATE document_version_approval_decisions
|
||||
SET
|
||||
@@ -542,7 +562,25 @@ WHERE
|
||||
AND notification_count < @max_notifications
|
||||
AND (
|
||||
(notification_count = 0 AND created_at < @debounce_before)
|
||||
OR (notification_count > 0 AND last_notified_at < @now::timestamptz - make_interval(secs => @reminder_interval_seconds * notification_count))
|
||||
OR (
|
||||
notification_count > 0
|
||||
AND EXTRACT(ISODOW FROM (@now::timestamptz AT TIME ZONE 'UTC')) BETWEEN 1 AND 5
|
||||
AND @now::timestamptz > (
|
||||
SELECT
|
||||
(due_at_utc + CASE EXTRACT(ISODOW FROM due_at_utc)
|
||||
WHEN 6 THEN interval '2 days'
|
||||
WHEN 7 THEN interval '1 day'
|
||||
ELSE interval '0'
|
||||
END) AT TIME ZONE 'UTC'
|
||||
FROM (
|
||||
SELECT
|
||||
(
|
||||
last_notified_at
|
||||
+ make_interval(secs => @reminder_interval_seconds * notification_count)
|
||||
) AT TIME ZONE 'UTC' AS due_at_utc
|
||||
) AS reminder
|
||||
)
|
||||
)
|
||||
)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
@@ -412,7 +412,9 @@ WHERE
|
||||
// request gets: the first notice plus three reminders. A request is due for its
|
||||
// next email once it is past its scheduled offset — the first email after the
|
||||
// debounce delay, then reminders at 1x, 2x and 3x the reminder interval after
|
||||
// the previous email — and stops once it reaches this cap.
|
||||
// the previous email — and stops once it reaches this cap. Reminder sends that
|
||||
// fall on Saturday or Sunday are deferred to Monday at the same clock time;
|
||||
// the first debounced notice is unchanged.
|
||||
const documentNotificationMaxCount = 4
|
||||
|
||||
func remainingNotificationIDs(all []gid.GID, claimed []gid.GID) []gid.GID {
|
||||
@@ -445,6 +447,7 @@ func (pvss *DocumentVersionSignatures) LoadNextDueGroupForNotification(
|
||||
debounceBefore time.Time,
|
||||
reminderInterval time.Duration,
|
||||
) error {
|
||||
// Reminders skip Sat/Sun; a weekend due time waits until Monday same UTC hour.
|
||||
q := `
|
||||
WITH next_group AS (
|
||||
SELECT
|
||||
@@ -457,7 +460,25 @@ WITH next_group AS (
|
||||
AND notification_count < @max_notifications
|
||||
AND (
|
||||
(notification_count = 0 AND requested_at < @debounce_before)
|
||||
OR (notification_count > 0 AND last_notified_at < @now::timestamptz - make_interval(secs => @reminder_interval_seconds * notification_count))
|
||||
OR (
|
||||
notification_count > 0
|
||||
AND EXTRACT(ISODOW FROM (@now::timestamptz AT TIME ZONE 'UTC')) BETWEEN 1 AND 5
|
||||
AND @now::timestamptz > (
|
||||
SELECT
|
||||
(due_at_utc + CASE EXTRACT(ISODOW FROM due_at_utc)
|
||||
WHEN 6 THEN interval '2 days'
|
||||
WHEN 7 THEN interval '1 day'
|
||||
ELSE interval '0'
|
||||
END) AT TIME ZONE 'UTC'
|
||||
FROM (
|
||||
SELECT
|
||||
(
|
||||
last_notified_at
|
||||
+ make_interval(secs => @reminder_interval_seconds * notification_count)
|
||||
) AT TIME ZONE 'UTC' AS due_at_utc
|
||||
) AS reminder
|
||||
)
|
||||
)
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
@@ -554,6 +575,7 @@ func (pvss DocumentVersionSignatures) ClaimForNotification(
|
||||
ids[i] = signature.ID
|
||||
}
|
||||
|
||||
// Reminders skip Sat/Sun; a weekend due time waits until Monday same UTC hour.
|
||||
q := `
|
||||
UPDATE document_version_signatures
|
||||
SET
|
||||
@@ -565,7 +587,25 @@ WHERE
|
||||
AND notification_count < @max_notifications
|
||||
AND (
|
||||
(notification_count = 0 AND requested_at < @debounce_before)
|
||||
OR (notification_count > 0 AND last_notified_at < @now::timestamptz - make_interval(secs => @reminder_interval_seconds * notification_count))
|
||||
OR (
|
||||
notification_count > 0
|
||||
AND EXTRACT(ISODOW FROM (@now::timestamptz AT TIME ZONE 'UTC')) BETWEEN 1 AND 5
|
||||
AND @now::timestamptz > (
|
||||
SELECT
|
||||
(due_at_utc + CASE EXTRACT(ISODOW FROM due_at_utc)
|
||||
WHEN 6 THEN interval '2 days'
|
||||
WHEN 7 THEN interval '1 day'
|
||||
ELSE interval '0'
|
||||
END) AT TIME ZONE 'UTC'
|
||||
FROM (
|
||||
SELECT
|
||||
(
|
||||
last_notified_at
|
||||
+ make_interval(secs => @reminder_interval_seconds * notification_count)
|
||||
) AT TIME ZONE 'UTC' AS due_at_utc
|
||||
) AS reminder
|
||||
)
|
||||
)
|
||||
)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
@@ -179,7 +179,7 @@ func New() *Implm {
|
||||
Document: DocumentNotificationConfig{
|
||||
Interval: 300, // 5 minutes
|
||||
DebounceDelay: 900, // 15 minutes
|
||||
ReminderInterval: 86400, // 1 day base cadence (1x, 2x, 3x)
|
||||
ReminderInterval: 86400, // 1 day base cadence (1x, 2x, 3x; weekend reminders → Monday)
|
||||
},
|
||||
},
|
||||
CustomDomains: CustomDomainsConfig{
|
||||
|
||||
@@ -40,7 +40,9 @@ type DocumentNotificationConfig struct {
|
||||
// DebounceDelay is how long a request must have been pending before its
|
||||
// first notification is sent.
|
||||
DebounceDelay int `json:"debounce-delay"`
|
||||
// ReminderInterval is the base reminder cadence. Reminders are sent at
|
||||
// 1x, 2x and 3x this interval after the previous email, then stop.
|
||||
// ReminderInterval is the base reminder cadence in seconds. Reminders are
|
||||
// sent at 1x, 2x and 3x this interval after the previous email, then stop.
|
||||
// Reminder sends that fall on Saturday or Sunday are deferred to Monday at
|
||||
// the same clock time; the first (debounced) notice is unchanged.
|
||||
ReminderInterval int `json:"reminder-interval"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user