mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 14:14:17 +00:00
extract event match group logic and add unit test
This commit is contained in:
@@ -53,6 +53,7 @@ import {
|
|||||||
} from '../api/rundown';
|
} from '../api/rundown';
|
||||||
import { logAxiosError } from '../api/utils';
|
import { logAxiosError } from '../api/utils';
|
||||||
import { useEditorSettings } from '../stores/editorSettings';
|
import { useEditorSettings } from '../stores/editorSettings';
|
||||||
|
import { eventDurationMatchGroupTarget } from '../utils/time';
|
||||||
|
|
||||||
export type EventOptions = Partial<{
|
export type EventOptions = Partial<{
|
||||||
// options of any new entries (event / delay / group)
|
// options of any new entries (event / delay / group)
|
||||||
@@ -473,12 +474,16 @@ function useEntryActionsForRundown(scopedRundownId: string | undefined) {
|
|||||||
const rundown = queryClient.getQueryData<Rundown>(resolveCurrentRundownQueryKey());
|
const rundown = queryClient.getQueryData<Rundown>(resolveCurrentRundownQueryKey());
|
||||||
if (!rundown) return;
|
if (!rundown) return;
|
||||||
const group = rundown.entries[groupId];
|
const group = rundown.entries[groupId];
|
||||||
if (!group || !isOntimeGroup(group) || group.targetDuration === null) return;
|
if (!group || !isOntimeGroup(group)) return;
|
||||||
const event = rundown.entries[eventId];
|
const event = rundown.entries[eventId];
|
||||||
if (!event || !isOntimeEvent(event)) return;
|
if (!event || !isOntimeEvent(event)) return;
|
||||||
const durationDiff = group.targetDuration - group.duration;
|
|
||||||
const newDuration = event.duration + durationDiff;
|
const newDuration = eventDurationMatchGroupTarget({
|
||||||
if (newDuration < 0) return;
|
targetDuration: group.targetDuration,
|
||||||
|
groupDuration: group.duration,
|
||||||
|
eventDuration: event.duration,
|
||||||
|
});
|
||||||
|
if (!newDuration) return;
|
||||||
updateTimer(eventId, 'duration', String(newDuration / MILLIS_PER_SECOND) + 's', false);
|
updateTimer(eventId, 'duration', String(newDuration / MILLIS_PER_SECOND) + 's', false);
|
||||||
},
|
},
|
||||||
[queryClient, updateTimer, resolveCurrentRundownQueryKey],
|
[queryClient, updateTimer, resolveCurrentRundownQueryKey],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
|
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
|
||||||
|
|
||||||
import { formatDuration, formatTime, nowInMillis } from '../time';
|
import { formatDuration, formatTime, nowInMillis, eventDurationMatchGroupTarget } from '../time';
|
||||||
|
|
||||||
describe('nowInMillis()', () => {
|
describe('nowInMillis()', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -45,6 +45,101 @@ describe('formatTime()', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('eventDurationMatchGroupTarget()', () => {
|
||||||
|
it('returns unchanged duration when group already matches target', () => {
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: MILLIS_PER_HOUR,
|
||||||
|
groupDuration: MILLIS_PER_HOUR,
|
||||||
|
eventDuration: MILLIS_PER_MINUTE * 30,
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('increases event duration when group is shorter than target', () => {
|
||||||
|
// Group is 1h short of target, so event duration increases by 1h
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: MILLIS_PER_HOUR * 2, // 2h
|
||||||
|
groupDuration: MILLIS_PER_HOUR, // 1h
|
||||||
|
eventDuration: MILLIS_PER_MINUTE * 30, // 30m
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(MILLIS_PER_HOUR + MILLIS_PER_MINUTE * 30); // 1h30m
|
||||||
|
});
|
||||||
|
|
||||||
|
it('decreases event duration when group is longer than target', () => {
|
||||||
|
// Group is 30m over target, so event duration decreases by 30m
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: MILLIS_PER_HOUR, // 1h
|
||||||
|
groupDuration: MILLIS_PER_HOUR + MILLIS_PER_MINUTE * 30, // 1h30m
|
||||||
|
eventDuration: MILLIS_PER_MINUTE * 30, // 30m
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles zero target duration', () => {
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: 0,
|
||||||
|
groupDuration: MILLIS_PER_HOUR,
|
||||||
|
eventDuration: MILLIS_PER_HOUR,
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles zero group duration', () => {
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: MILLIS_PER_HOUR,
|
||||||
|
groupDuration: 0,
|
||||||
|
eventDuration: MILLIS_PER_MINUTE * 30,
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(MILLIS_PER_HOUR + MILLIS_PER_MINUTE * 30);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles zero event duration', () => {
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: MILLIS_PER_HOUR,
|
||||||
|
groupDuration: MILLIS_PER_MINUTE * 30,
|
||||||
|
eventDuration: 0,
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(MILLIS_PER_HOUR - MILLIS_PER_MINUTE * 30);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles all zero values', () => {
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: 0,
|
||||||
|
groupDuration: 0,
|
||||||
|
eventDuration: 0,
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null when result would be negative', () => {
|
||||||
|
// Group exceeds target by 1.5h, event shrinks by 1.5h (exceeds event duration)
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: MILLIS_PER_MINUTE * 30,
|
||||||
|
groupDuration: MILLIS_PER_HOUR * 2,
|
||||||
|
eventDuration: MILLIS_PER_HOUR,
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles large durations', () => {
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: MILLIS_PER_HOUR * 24, // 24h
|
||||||
|
groupDuration: MILLIS_PER_HOUR * 12, // 12h
|
||||||
|
eventDuration: MILLIS_PER_HOUR, // 1h
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(MILLIS_PER_HOUR * 13); // 13h
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null when targetDuration is null', () => {
|
||||||
|
const result = eventDurationMatchGroupTarget({
|
||||||
|
targetDuration: null,
|
||||||
|
groupDuration: MILLIS_PER_HOUR,
|
||||||
|
eventDuration: MILLIS_PER_MINUTE * 30,
|
||||||
|
});
|
||||||
|
expect(result).toStrictEqual(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('formatDuration()', () => {
|
describe('formatDuration()', () => {
|
||||||
it('formats durations correctly', () => {
|
it('formats durations correctly', () => {
|
||||||
expect(formatDuration(0)).toBe('0m');
|
expect(formatDuration(0)).toBe('0m');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MaybeNumber, MaybeString, OntimeEvent, Settings, TimeFormat } from 'ontime-types';
|
import { Maybe, MaybeNumber, MaybeString, OntimeEvent, Settings, TimeFormat } from 'ontime-types';
|
||||||
import {
|
import {
|
||||||
MILLIS_PER_HOUR,
|
MILLIS_PER_HOUR,
|
||||||
MILLIS_PER_MINUTE,
|
MILLIS_PER_MINUTE,
|
||||||
@@ -192,3 +192,29 @@ export function getExpectedTimesFromExtendedEvent(
|
|||||||
plannedEnd,
|
plannedEnd,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts an event's duration so the group matches a target duration.
|
||||||
|
* The difference between the target and the current group duration is
|
||||||
|
* added to (or subtracted from) the event's duration.
|
||||||
|
* @param targetDuration - The desired total duration for the group, or null
|
||||||
|
* @param groupDuration - The current total duration of all events in the group
|
||||||
|
* @param eventDuration - The current duration of the event being adjusted
|
||||||
|
* @returns The adjusted event duration, or null if targetDuration is null or
|
||||||
|
* the result would be negative
|
||||||
|
*/
|
||||||
|
export function eventDurationMatchGroupTarget({
|
||||||
|
targetDuration,
|
||||||
|
groupDuration,
|
||||||
|
eventDuration,
|
||||||
|
}: {
|
||||||
|
targetDuration: Maybe<number>;
|
||||||
|
groupDuration: number;
|
||||||
|
eventDuration: number;
|
||||||
|
}): Maybe<number> {
|
||||||
|
if (targetDuration === null) return null;
|
||||||
|
if (targetDuration === groupDuration) return null;
|
||||||
|
const durationDiff = targetDuration - groupDuration;
|
||||||
|
const newDuration = eventDuration + durationDiff;
|
||||||
|
return newDuration < 0 ? null : newDuration;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user