questionnaire-cron.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import axiosRetry from 'axios-retry';
  2. import { configManager } from '~/server/service/config-manager';
  3. import CronService from '~/server/service/cron';
  4. import { getRandomIntInRange } from '~/utils/rand';
  5. import { StatusType } from '../../interfaces/questionnaire-answer-status';
  6. import type { IQuestionnaireOrder } from '../../interfaces/questionnaire-order';
  7. import ProactiveQuestionnaireAnswer from '../models/proactive-questionnaire-answer';
  8. import QuestionnaireAnswer from '../models/questionnaire-answer';
  9. import QuestionnaireAnswerStatus from '../models/questionnaire-answer-status';
  10. import QuestionnaireOrder from '../models/questionnaire-order';
  11. const axios = require('axios').default;
  12. axiosRetry(axios, { retries: 3 });
  13. /**
  14. * Manages cronjob which
  15. * 1. fetches QuestionnaireOrders from questionnaire server
  16. * 2. updates QuestionnaireOrder collection to contain only the ones that exist in the fetched list and is not finished (doesn't have to be started)
  17. * 3. changes QuestionnaireAnswerStatuses which are 'skipped' to 'not_answered'
  18. * 4. resend QuestionnaireAnswers & ProactiveQuestionnaireAnswers which failed to reach questionnaire server
  19. */
  20. class QuestionnaireCronService extends CronService {
  21. sleep = (msec: number): Promise<void> => new Promise(resolve => setTimeout(resolve, msec));
  22. override getCronSchedule(): string {
  23. return configManager.getConfig('crowi', 'app:questionnaireCronSchedule');
  24. }
  25. override async executeJob(): Promise<void> {
  26. // sleep for a random amount to scatter request time from GROWI apps to questionnaire server
  27. await this.sleepBeforeJob();
  28. const questionnaireServerOrigin = configManager.getConfig('crowi', 'app:questionnaireServerOrigin');
  29. const fetchQuestionnaireOrders = async(): Promise<IQuestionnaireOrder[]> => {
  30. const response = await axios.get(`${questionnaireServerOrigin}/questionnaire-order/index`);
  31. return response.data.questionnaireOrders;
  32. };
  33. const saveUnfinishedOrders = async(questionnaireOrders: IQuestionnaireOrder[]) => {
  34. const currentDate = new Date(Date.now());
  35. const unfinishedOrders = questionnaireOrders.filter(order => new Date(order.showUntil) > currentDate);
  36. await QuestionnaireOrder.insertMany(unfinishedOrders);
  37. };
  38. const changeSkippedAnswerStatusToNotAnswered = async() => {
  39. await QuestionnaireAnswerStatus.updateMany(
  40. { status: StatusType.skipped },
  41. { status: StatusType.not_answered },
  42. );
  43. };
  44. const resendQuestionnaireAnswers = async() => {
  45. const questionnaireAnswers = await QuestionnaireAnswer.find()
  46. .select('-_id -answers._id -growiInfo._id -userInfo._id');
  47. const proactiveQuestionnaireAnswers = await ProactiveQuestionnaireAnswer.find()
  48. .select('-_id -growiInfo._id -userInfo._id');
  49. axios.post(`${questionnaireServerOrigin}/questionnaire-answer/batch`, { questionnaireAnswers })
  50. .then(async() => {
  51. await QuestionnaireAnswer.deleteMany();
  52. });
  53. axios.post(`${questionnaireServerOrigin}/questionnaire-answer/proactive/batch`, { proactiveQuestionnaireAnswers })
  54. .then(async() => {
  55. await ProactiveQuestionnaireAnswer.deleteMany();
  56. });
  57. };
  58. const questionnaireOrders: IQuestionnaireOrder[] = await fetchQuestionnaireOrders();
  59. resendQuestionnaireAnswers();
  60. // reset QuestionnaireOrder collection and save unfinished ones that exist on questionnaire server
  61. await QuestionnaireOrder.deleteMany();
  62. await saveUnfinishedOrders(questionnaireOrders);
  63. await changeSkippedAnswerStatusToNotAnswered();
  64. }
  65. private async sleepBeforeJob() {
  66. const maxHoursUntilRequest = configManager.getConfig('crowi', 'app:questionnaireCronMaxHoursUntilRequest');
  67. const maxSecondsUntilRequest = maxHoursUntilRequest * 60 * 60;
  68. const secToSleep = getRandomIntInRange(0, maxSecondsUntilRequest);
  69. await this.sleep(secToSleep * 1000);
  70. }
  71. }
  72. const questionnaireCronService = new QuestionnaireCronService();
  73. export default questionnaireCronService;