diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.css b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.css index 924963258..a5c84ee3a 100644 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.css +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.css @@ -5,6 +5,7 @@ } .lastExecution, +.lastDuration, .nextExecution { composes: cell from '~Components/Table/Cells/TableRowCell.css'; diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js index 82cedc720..deeb161bc 100644 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import formatDate from 'Utilities/Date/formatDate'; import formatDateTime from 'Utilities/Date/formatDateTime'; +import formatTimeSpan from 'Utilities/Date/formatTimeSpan'; import { icons } from 'Helpers/Props'; import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; import TableRow from 'Components/Table/TableRow'; @@ -91,6 +92,8 @@ class ScheduledTaskRow extends Component { name, interval, lastExecution, + lastStartTime, + lastDuration, nextExecution, isQueued, isExecuting, @@ -108,6 +111,7 @@ class ScheduledTaskRow extends Component { const executeNow = !isDisabled && moment().isAfter(nextExecution); const hasNextExecutionTime = !isDisabled && !executeNow; const duration = moment.duration(interval, 'minutes').humanize().replace(/an?(?=\s)/, '1'); + const hasLastStartTime = moment(lastStartTime).isAfter('2010-01-01'); return ( @@ -125,6 +129,21 @@ class ScheduledTaskRow extends Component { {lastExecutionTime} + { + !hasLastStartTime && + - + } + + { + hasLastStartTime && + + {formatTimeSpan(lastDuration)} + + } + { isDisabled && - @@ -169,6 +188,8 @@ ScheduledTaskRow.propTypes = { name: PropTypes.string.isRequired, interval: PropTypes.number.isRequired, lastExecution: PropTypes.string.isRequired, + lastStartTime: PropTypes.string.isRequired, + lastDuration: PropTypes.string.isRequired, nextExecution: PropTypes.string.isRequired, isQueued: PropTypes.bool.isRequired, isExecuting: PropTypes.bool.isRequired, diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js index 9dbb72f4d..1176db157 100644 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.js @@ -22,6 +22,11 @@ const columns = [ label: 'Last Execution', isVisible: true }, + { + name: 'lastDuration', + label: 'Last Duration', + isVisible: true + }, { name: 'nextExecution', label: 'Next Execution', diff --git a/src/NzbDrone.Core/Datastore/Migration/163_task_duration.cs b/src/NzbDrone.Core/Datastore/Migration/163_task_duration.cs new file mode 100644 index 000000000..7a76e42b8 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/163_task_duration.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(163)] + public class task_duration : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("ScheduledTasks").AddColumn("LastStartTime").AsDateTime().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/Jobs/ScheduledTask.cs b/src/NzbDrone.Core/Jobs/ScheduledTask.cs index 5d842696d..8a048aeed 100644 --- a/src/NzbDrone.Core/Jobs/ScheduledTask.cs +++ b/src/NzbDrone.Core/Jobs/ScheduledTask.cs @@ -8,5 +8,6 @@ public class ScheduledTask : ModelBase public string TypeName { get; set; } public int Interval { get; set; } public DateTime LastExecution { get; set; } + public DateTime LastStartTime { get; set; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs b/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs index a7444beb2..7dbe32f80 100644 --- a/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs +++ b/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.Jobs public interface IScheduledTaskRepository : IBasicRepository { ScheduledTask GetDefinition(Type type); - void SetLastExecutionTime(int id, DateTime executionTime); + void SetLastExecutionTime(int id, DateTime executionTime, DateTime startTime); } public class ScheduledTaskRepository : BasicRepository, IScheduledTaskRepository @@ -25,15 +25,16 @@ public ScheduledTask GetDefinition(Type type) return Query(x => x.TypeName == type.FullName).Single(); } - public void SetLastExecutionTime(int id, DateTime executionTime) + public void SetLastExecutionTime(int id, DateTime executionTime, DateTime startTime) { var task = new ScheduledTask { Id = id, - LastExecution = executionTime + LastExecution = executionTime, + LastStartTime = startTime }; - SetFields(task, scheduledTask => scheduledTask.LastExecution); + SetFields(task, scheduledTask => scheduledTask.LastExecution, scheduledTask => scheduledTask.LastStartTime); } } } diff --git a/src/NzbDrone.Core/Jobs/Scheduler.cs b/src/NzbDrone.Core/Jobs/Scheduler.cs index a18aac556..b38b2c286 100644 --- a/src/NzbDrone.Core/Jobs/Scheduler.cs +++ b/src/NzbDrone.Core/Jobs/Scheduler.cs @@ -39,7 +39,7 @@ private void ExecuteCommands() foreach (var task in tasks) { - _commandQueueManager.Push(task.TypeName, task.LastExecution, CommandPriority.Low, CommandTrigger.Scheduled); + _commandQueueManager.Push(task.TypeName, task.LastExecution, task.LastStartTime, CommandPriority.Low, CommandTrigger.Scheduled); } } diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index be1467962..1d7495b39 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -181,7 +181,7 @@ public void Handle(CommandExecutedEvent message) if (scheduledTask != null && message.Command.Body.UpdateScheduledTask) { _logger.Trace("Updating last run time for: {0}", scheduledTask.TypeName); - _scheduledTaskRepository.SetLastExecutionTime(scheduledTask.Id, DateTime.UtcNow); + _scheduledTaskRepository.SetLastExecutionTime(scheduledTask.Id, DateTime.UtcNow, message.Command.StartedAt.Value); } } diff --git a/src/NzbDrone.Core/Messaging/Commands/Command.cs b/src/NzbDrone.Core/Messaging/Commands/Command.cs index db80322f5..2e11db195 100644 --- a/src/NzbDrone.Core/Messaging/Commands/Command.cs +++ b/src/NzbDrone.Core/Messaging/Commands/Command.cs @@ -26,6 +26,7 @@ public virtual bool SendUpdatesToClient public string Name { get; private set; } public DateTime? LastExecutionTime { get; set; } + public DateTime? LastStartTime { get; set; } public CommandTrigger Trigger { get; set; } public bool SuppressMessages { get; set; } diff --git a/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs b/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs index 1a199991b..c37e49335 100644 --- a/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs +++ b/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs @@ -17,7 +17,7 @@ public interface IManageCommandQueue { List PushMany(List commands) where TCommand : Command; CommandModel Push(TCommand command, CommandPriority priority = CommandPriority.Normal, CommandTrigger trigger = CommandTrigger.Unspecified) where TCommand : Command; - CommandModel Push(string commandName, DateTime? lastExecutionTime, CommandPriority priority = CommandPriority.Normal, CommandTrigger trigger = CommandTrigger.Unspecified); + CommandModel Push(string commandName, DateTime? lastExecutionTime, DateTime? lastStartTime, CommandPriority priority = CommandPriority.Normal, CommandTrigger trigger = CommandTrigger.Unspecified); IEnumerable Queue(CancellationToken cancellationToken); List All(); CommandModel Get(int id); @@ -124,10 +124,11 @@ public CommandModel Push(TCommand command, CommandPriority priority = return commandModel; } - public CommandModel Push(string commandName, DateTime? lastExecutionTime, CommandPriority priority = CommandPriority.Normal, CommandTrigger trigger = CommandTrigger.Unspecified) + public CommandModel Push(string commandName, DateTime? lastExecutionTime, DateTime? lastStartTime, CommandPriority priority = CommandPriority.Normal, CommandTrigger trigger = CommandTrigger.Unspecified) { dynamic command = GetCommand(commandName); command.LastExecutionTime = lastExecutionTime; + command.LastStartTime = lastStartTime; command.Trigger = trigger; return Push(command, priority, trigger); diff --git a/src/Radarr.Api.V3/System/Tasks/TaskModule.cs b/src/Radarr.Api.V3/System/Tasks/TaskModule.cs index 50d97d014..193e2936b 100644 --- a/src/Radarr.Api.V3/System/Tasks/TaskModule.cs +++ b/src/Radarr.Api.V3/System/Tasks/TaskModule.cs @@ -54,6 +54,7 @@ private static TaskResource ConvertToResource(ScheduledTask scheduledTask) TaskName = taskName, Interval = scheduledTask.Interval, LastExecution = scheduledTask.LastExecution, + LastStartTime = scheduledTask.LastStartTime, NextExecution = scheduledTask.LastExecution.AddMinutes(scheduledTask.Interval) }; } diff --git a/src/Radarr.Api.V3/System/Tasks/TaskResource.cs b/src/Radarr.Api.V3/System/Tasks/TaskResource.cs index 05c9acdef..7906ccd08 100644 --- a/src/Radarr.Api.V3/System/Tasks/TaskResource.cs +++ b/src/Radarr.Api.V3/System/Tasks/TaskResource.cs @@ -9,6 +9,9 @@ public class TaskResource : RestResource public string TaskName { get; set; } public int Interval { get; set; } public DateTime LastExecution { get; set; } + public DateTime LastStartTime { get; set; } public DateTime NextExecution { get; set; } + + public TimeSpan LastDuration => LastExecution - LastStartTime; } }