scheduler.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { dateTime } from '@grafana/data';
  2. import { formatUtcOffset } from '@grafana/ui/src/components/DateTimePickers/TimeZonePicker/TimeZoneOffset';
  3. import { IntervalFrequency, ReportTime, SchedulingData, SchedulingFrequency, SchedulingOptions } from '../../types';
  4. import { initialState } from '../state/reducers';
  5. import { getTime, createDate, getDate, padTime } from './dateTime';
  6. /**
  7. * Process schedule data
  8. * @param scheduleData
  9. */
  10. export const getSchedule = (scheduleData = {} as SchedulingData) => {
  11. const {
  12. time,
  13. startDate,
  14. endDate,
  15. endTime,
  16. timeZone,
  17. frequency,
  18. dayOfMonth,
  19. workdaysOnly,
  20. intervalFrequency,
  21. intervalAmount,
  22. sendTime,
  23. } = scheduleData;
  24. const parsedTime = !time && startDate ? getTime(String(startDate)) : time;
  25. const combinedStartDate =
  26. startDate && sendTime !== 'now' ? createDate(startDate, parsedTime as ReportTime, timeZone) : '';
  27. const combinedEndDate =
  28. endDate && ![SchedulingFrequency.Once, SchedulingFrequency.Never].includes(frequency)
  29. ? createDate(endDate, endTime as unknown as ReportTime, timeZone)
  30. : '';
  31. const options = {
  32. frequency,
  33. timeZone,
  34. workdaysOnly,
  35. intervalFrequency,
  36. intervalAmount: intervalAmount ? parseInt(intervalAmount, 10) : 0,
  37. startDate: combinedStartDate,
  38. endDate: combinedEndDate,
  39. dayOfMonth: dayOfMonth ? 'last' : '',
  40. };
  41. // Remove empty/falsy fields from the schedule object
  42. return Object.fromEntries(Object.entries(options).filter(([_, val]) => val)) as unknown as SchedulingOptions;
  43. };
  44. export function getOrdinal(n: number) {
  45. const suffixes = ['th', 'st', 'nd', 'rd'];
  46. const value = n % 100;
  47. return n + (suffixes[(value - 20) % 10] || suffixes[value] || suffixes[0]);
  48. }
  49. export const isHourFrequency = (frequency: SchedulingFrequency, interval = IntervalFrequency.Hours) => {
  50. return frequency === SchedulingFrequency.Custom && interval === IntervalFrequency.Hours;
  51. };
  52. export const showWorkdaysOnly = (frequency: SchedulingFrequency, interval = IntervalFrequency.Hours) => {
  53. return (
  54. [SchedulingFrequency.Hourly, SchedulingFrequency.Daily].includes(frequency) || isHourFrequency(frequency, interval)
  55. );
  56. };
  57. export function parseScheduleTime({
  58. startDate,
  59. endDate,
  60. intervalFrequency = IntervalFrequency.Hours,
  61. intervalAmount = 2,
  62. frequency,
  63. dayOfMonth,
  64. timeZone,
  65. workdaysOnly,
  66. }: SchedulingOptions) {
  67. if (!startDate) {
  68. return '';
  69. }
  70. const locale = 'en-US';
  71. const { hour: h, minute: m } = getTime(startDate);
  72. const minute = padTime(m);
  73. const hour = padTime(h);
  74. const day = dateTime(getDate(startDate)).locale(locale).format('dddd');
  75. const date = dayOfMonth === 'last' ? `the last` : getOrdinal((getDate(startDate) as Date).getDate());
  76. let duration, time;
  77. const offset = formatUtcOffset(Date.now(), timeZone);
  78. const timeString = `at ${hour}:${minute}${offset ? ` ${offset}` : ''}`;
  79. const workdaysOnlyStr = workdaysOnly && showWorkdaysOnly(frequency, intervalFrequency) ? ', Monday to Friday' : '';
  80. if (endDate) {
  81. duration = `${dateTime(getDate(startDate)).locale(locale).format('LL')} - ${dateTime(getDate(endDate))
  82. .locale(locale)
  83. .format('LL')}`;
  84. }
  85. switch (frequency) {
  86. case SchedulingFrequency.Monthly:
  87. time = `Monthly on ${date} day ${timeString}`;
  88. break;
  89. case SchedulingFrequency.Weekly:
  90. time = `Every ${day} ${timeString}`;
  91. break;
  92. case SchedulingFrequency.Daily:
  93. time = `Daily ${timeString}`;
  94. break;
  95. case SchedulingFrequency.Hourly:
  96. time = `Hourly at minute ${minute}`;
  97. break;
  98. case SchedulingFrequency.Custom:
  99. time = `Every ${intervalAmount} ${intervalFrequency}`;
  100. break;
  101. case SchedulingFrequency.Once:
  102. time = `Once on ${dateTime(getDate(startDate)).locale(locale).format('LL')}`;
  103. break;
  104. case SchedulingFrequency.Never:
  105. time = `Never`;
  106. break;
  107. }
  108. if (duration && time) {
  109. return `${time}, ${duration}${workdaysOnlyStr}`;
  110. } else if (frequency === SchedulingFrequency.Custom) {
  111. time += `, ${
  112. intervalFrequency === IntervalFrequency.Hours
  113. ? `from ${hour}:${minute}${offset ? ` ${offset}` : ''}`
  114. : `from ${dateTime(getDate(startDate)).locale(locale).format('LL')}`
  115. }`;
  116. }
  117. return time + workdaysOnlyStr;
  118. }
  119. const recurrenceMap = new Map([
  120. [SchedulingFrequency.Monthly, 'month'],
  121. [SchedulingFrequency.Daily, 'day'],
  122. [SchedulingFrequency.Weekly, 'week'],
  123. [SchedulingFrequency.Hourly, 'hour'],
  124. ]);
  125. export const schedulePreview = (schedule: SchedulingData) => {
  126. const { frequency, intervalFrequency = IntervalFrequency.Hours, intervalAmount = '2' } = schedule;
  127. if (frequency === SchedulingFrequency.Never) {
  128. return 'The report will not be sent.';
  129. }
  130. const workdaysOnly = schedule.workdaysOnly ? ', Monday to Friday.' : '.';
  131. if (!schedule.sendTime || schedule.sendTime === 'now') {
  132. let preview = `The report will be sent immediately after it is saved`;
  133. let recurrence;
  134. if (recurrenceMap.has(frequency)) {
  135. recurrence = recurrenceMap.get(frequency);
  136. } else if (frequency === SchedulingFrequency.Custom) {
  137. recurrence = `${intervalAmount} ${intervalFrequency}`;
  138. }
  139. if (recurrence) {
  140. preview += ` and will be sent every ${recurrence}`;
  141. }
  142. return `${preview}${workdaysOnly}`;
  143. }
  144. const preview = parseScheduleTime(getSchedule(schedule));
  145. if (preview) {
  146. return `The report will be sent: ${preview}.`;
  147. }
  148. return '';
  149. };
  150. export const scheduleUpdated = (newSchedule: Partial<SchedulingOptions>) => {
  151. const originalSchedule = initialState.report.schedule;
  152. if (Object.keys(originalSchedule).length !== Object.keys(newSchedule).length) {
  153. return true;
  154. }
  155. return Object.entries(originalSchedule).some(([key, value]) => value !== newSchedule[key as keyof SchedulingOptions]);
  156. };