diff --git a/backend/src/assignment-scheduler.service.ts b/backend/src/assignment-scheduler.service.ts index 8821415..7d3e3b7 100644 --- a/backend/src/assignment-scheduler.service.ts +++ b/backend/src/assignment-scheduler.service.ts @@ -1,24 +1,50 @@ import { Injectable } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; +import { dbGetTaskGroupUsers } from './db/functions/task-group'; import { - dbGetTaskGroupUsers, - dbGetTaskGroupsToCreateForCurrentInterval, -} from './db/functions/task-group'; -import { dbGetAssignmentsForTaskGroup } from './db/functions/assignment-task-group'; + dbCreateTaskGroupAssignment, + dbGetAssignmentsForTaskGroup, + dbGetTaskGroupsToAssignForCurrentInterval, +} from './db/functions/assignment-task-group'; +import { randomFromArray } from './utils/array'; @Injectable() export class AssignmentSchedulerService { @Cron(CronExpression.EVERY_5_SECONDS) async handleCron() { + console.debug('CRON job running'); const taskGroupsToCreateAssignmentsFor = - await dbGetTaskGroupsToCreateForCurrentInterval(); + await dbGetTaskGroupsToAssignForCurrentInterval(); + console.debug({ taskGroupsToCreateAssignmentsFor }); for (const { taskGroupId } of taskGroupsToCreateAssignmentsFor) { - const users = await dbGetTaskGroupUsers(taskGroupId); - const assignments = await dbGetAssignmentsForTaskGroup( + const userIds = await dbGetTaskGroupUsers(taskGroupId); + if (userIds.length === 0) { + continue; + } + const lastAssignments = await dbGetAssignmentsForTaskGroup( taskGroupId, - users.length, + userIds.length, ); - console.log({ assignments }); + const firstTimeAssignmentUsers = userIds.filter( + ({ userId }) => + !lastAssignments.some((assignment) => assignment.userId === userId), + ); + + console.debug({ userIds }); + console.debug({ firstTimeAssignmentUsers }); + console.debug({ lastAssignments, taskGroupId }); + + let nextResponsibleUserId: number; + if (firstTimeAssignmentUsers.length === 0) { + nextResponsibleUserId = + lastAssignments[lastAssignments.length - 1].userId; + } else { + nextResponsibleUserId = randomFromArray( + firstTimeAssignmentUsers, + ).userId; + } + + await dbCreateTaskGroupAssignment(taskGroupId, nextResponsibleUserId); } } } diff --git a/backend/src/db/functions/assignment-task-group.ts b/backend/src/db/functions/assignment-task-group.ts index 94d18ff..df84356 100644 --- a/backend/src/db/functions/assignment-task-group.ts +++ b/backend/src/db/functions/assignment-task-group.ts @@ -1,6 +1,6 @@ -import { desc, eq } from 'drizzle-orm'; +import { count, desc, eq, or, sql } from 'drizzle-orm'; import { db } from '..'; -import { taskGroupAssignmentTable } from '../schema'; +import { taskGroupAssignmentTable, taskGroupTable } from '../schema'; export async function dbGetAssignmentsForTaskGroup( taskGroupId: number, @@ -16,3 +16,36 @@ export async function dbGetAssignmentsForTaskGroup( } return await result.limit(limit); } + +export async function dbCreateTaskGroupAssignment( + taskGroupId: number, + userId: number, +) { + await db.insert(taskGroupAssignmentTable).values({ taskGroupId, userId }); +} + +export async function dbGetTaskGroupsToAssignForCurrentInterval() { + try { + const taskGroupIdsToCreateAssignmentsFor = await db + .select({ + taskGroupId: taskGroupTable.id, + }) + .from(taskGroupTable) + .leftJoin( + taskGroupAssignmentTable, + eq(taskGroupTable.id, taskGroupAssignmentTable.taskGroupId), + ) + .groupBy(taskGroupTable.id) + .having( + or( + eq(count(taskGroupAssignmentTable.id), 0), + sql`MAX(${taskGroupAssignmentTable.createdAt}) < (NOW() - ${taskGroupTable.interval})`, + ), + ); + + return taskGroupIdsToCreateAssignmentsFor; + } catch (error) { + console.error({ error }); + throw error; + } +} diff --git a/backend/src/db/functions/task-group.ts b/backend/src/db/functions/task-group.ts index b2e04ed..46fef7e 100644 --- a/backend/src/db/functions/task-group.ts +++ b/backend/src/db/functions/task-group.ts @@ -1,12 +1,7 @@ -import { eq, sql } from 'drizzle-orm'; +import { eq } from 'drizzle-orm'; import { CreateTaskGroup } from 'src/task-group.controller'; import { db } from '..'; -import { - taskGroupAssignmentTable, - taskGroupTable, - taskGroupUserTable, - userTable, -} from '../schema'; +import { taskGroupTable, taskGroupUserTable, userTable } from '../schema'; export async function dbGetTaskGroups() { return await db.select().from(taskGroupTable); @@ -44,28 +39,6 @@ export async function dbCreateTaskGroup({ } } -export async function dbGetTaskGroupsToCreateForCurrentInterval() { - try { - const taskGroupIdsToCreateAssignmentsFor = await db - .select({ - taskGroupId: taskGroupTable.id, - }) - .from(taskGroupTable) - .innerJoin( - taskGroupAssignmentTable, - eq(taskGroupTable.id, taskGroupAssignmentTable.taskGroupId), - ) - .groupBy(taskGroupTable.id) - .having( - sql`MAX(${taskGroupAssignmentTable.createdAt}) < (NOW() - ${taskGroupTable.interval})`, - ); - return taskGroupIdsToCreateAssignmentsFor; - } catch (error) { - console.error({ error }); - throw error; - } -} - export async function dbGetTaskGroupUsers(taskGroupId: number) { try { const taskGroupUsers = await db diff --git a/backend/src/utils/array.ts b/backend/src/utils/array.ts new file mode 100644 index 0000000..0a82a0e --- /dev/null +++ b/backend/src/utils/array.ts @@ -0,0 +1,11 @@ +/** + * Returns a random value from an array of data. + * + * @param array - An array of data from which to select a random value. + * @returns A random value from the array. + */ +export function randomFromArray(array: readonly TItem[]) { + const randomIndex = Math.floor(Math.random() * array.length); + const randomValue = array[randomIndex]; + return randomValue; +}