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 9f3686b83..acb8c8d36 100644 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.js @@ -7,6 +7,7 @@ import TableRow from 'Components/Table/TableRow'; import { icons } from 'Helpers/Props'; import formatDate from 'Utilities/Date/formatDate'; import formatDateTime from 'Utilities/Date/formatDateTime'; +import formatTimeSpan from 'Utilities/Date/formatTimeSpan'; import styles from './ScheduledTaskRow.css'; function getFormattedDates(props) { @@ -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/180_task_duration.cs b/src/NzbDrone.Core/Datastore/Migration/180_task_duration.cs new file mode 100644 index 000000000..b2519d7da --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/180_task_duration.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(180)] + 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 31b209410..dd2fe0ce0 100644 --- a/src/NzbDrone.Core/Jobs/ScheduledTask.cs +++ b/src/NzbDrone.Core/Jobs/ScheduledTask.cs @@ -10,6 +10,7 @@ namespace NzbDrone.Core.Jobs public int Interval { get; set; } public DateTime LastExecution { get; set; } public CommandPriority Priority { get; set; } + public DateTime LastStartTime { get; set; } public ScheduledTask() { diff --git a/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs b/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs index 400b48457..0167061e3 100644 --- a/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs +++ b/src/NzbDrone.Core/Jobs/ScheduledTaskRepository.cs @@ -8,7 +8,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 @@ -23,15 +23,16 @@ namespace NzbDrone.Core.Jobs return Query(c => c.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 ee0940dc9..dc5d44d60 100644 --- a/src/NzbDrone.Core/Jobs/Scheduler.cs +++ b/src/NzbDrone.Core/Jobs/Scheduler.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Jobs foreach (var task in tasks) { - _commandQueueManager.Push(task.TypeName, task.LastExecution, task.Priority, CommandTrigger.Scheduled); + _commandQueueManager.Push(task.TypeName, task.LastExecution, task.LastStartTime, task.Priority, CommandTrigger.Scheduled); } } finally diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index 2bcae4bac..7c7eda9b9 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -208,9 +208,14 @@ namespace NzbDrone.Core.Jobs _logger.Trace("Updating last run time for: {0}", scheduledTask.TypeName); var lastExecution = DateTime.UtcNow; + var startTime = message.Command.StartedAt.Value; - _scheduledTaskRepository.SetLastExecutionTime(scheduledTask.Id, lastExecution); - _cache.Find(scheduledTask.TypeName).LastExecution = lastExecution; + _scheduledTaskRepository.SetLastExecutionTime(scheduledTask.Id, lastExecution, startTime); + + var cached = _cache.Find(scheduledTask.TypeName); + + cached.LastExecution = lastExecution; + cached.LastStartTime = startTime; } } diff --git a/src/NzbDrone.Core/Messaging/Commands/Command.cs b/src/NzbDrone.Core/Messaging/Commands/Command.cs index 39b787f41..023b52d85 100644 --- a/src/NzbDrone.Core/Messaging/Commands/Command.cs +++ b/src/NzbDrone.Core/Messaging/Commands/Command.cs @@ -30,6 +30,7 @@ namespace NzbDrone.Core.Messaging.Commands 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 97c7b3ca5..44edc9009 100644 --- a/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs +++ b/src/NzbDrone.Core/Messaging/Commands/CommandQueueManager.cs @@ -20,7 +20,7 @@ namespace NzbDrone.Core.Messaging.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); @@ -136,10 +136,11 @@ namespace NzbDrone.Core.Messaging.Commands } } - 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/Sonarr.Api.V3/System/Tasks/TaskController.cs b/src/Sonarr.Api.V3/System/Tasks/TaskController.cs index 2ea015e9e..39fa5cc67 100644 --- a/src/Sonarr.Api.V3/System/Tasks/TaskController.cs +++ b/src/Sonarr.Api.V3/System/Tasks/TaskController.cs @@ -55,6 +55,7 @@ namespace Sonarr.Api.V3.System.Tasks TaskName = taskName, Interval = scheduledTask.Interval, LastExecution = scheduledTask.LastExecution, + LastStartTime = scheduledTask.LastStartTime, NextExecution = scheduledTask.LastExecution.AddMinutes(scheduledTask.Interval) }; } diff --git a/src/Sonarr.Api.V3/System/Tasks/TaskResource.cs b/src/Sonarr.Api.V3/System/Tasks/TaskResource.cs index b19f6dbc9..0e2d0a62b 100644 --- a/src/Sonarr.Api.V3/System/Tasks/TaskResource.cs +++ b/src/Sonarr.Api.V3/System/Tasks/TaskResource.cs @@ -9,6 +9,9 @@ namespace Sonarr.Api.V3.System.Tasks 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; } }