From c928ccb20167c0846b66fe27ccc5292fae81ccc9 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 1 Sep 2013 19:55:45 -0700 Subject: [PATCH] Added progress messaging, using info logging Also extension methods for complete and failed (for coloured UI messaging) --- NzbDrone.Api/Commands/CommandConnection.cs | 4 +- NzbDrone.Api/NzbDrone.Api.csproj | 3 ++ .../ProgressMessageConnection.cs | 24 +++++++++++ .../ProgressMessageModule.cs | 21 ++++++++++ .../ProgressMessageResource.cs | 12 ++++++ .../MessageAggregatorCommandTests.cs | 8 ++-- .../Instrumentation/LogEventExtensions.cs | 1 - .../Instrumentation/LoggerExtensions.cs | 40 +++++++++++++++++++ NzbDrone.Common/Messaging/IProcessMessage.cs | 1 - .../Tracking/CommandTrackingService.cs | 12 +++--- .../Messaging/Tracking/ProcessState.cs | 14 +++++++ .../Messaging/Tracking/TrackedCommand.cs | 11 +---- NzbDrone.Common/NzbDrone.Common.csproj | 2 + NzbDrone.Core/Indexers/RssSyncService.cs | 3 +- NzbDrone.Core/NzbDrone.Core.csproj | 2 +- .../ProgressMessaging/ProgressMessage.cs | 5 +-- ...gingTarget.cs => ProgressMessageTarget.cs} | 8 +++- .../ProgressMessageCollection.js | 40 +++++++++++++++++++ UI/ProgressMessaging/ProgressMessageModel.js | 8 ++++ UI/Router.js | 3 +- UI/Series/Index/SeriesIndexLayout.js | 1 - 21 files changed, 191 insertions(+), 32 deletions(-) create mode 100644 NzbDrone.Api/ProgressMessaging/ProgressMessageConnection.cs create mode 100644 NzbDrone.Api/ProgressMessaging/ProgressMessageModule.cs create mode 100644 NzbDrone.Api/ProgressMessaging/ProgressMessageResource.cs create mode 100644 NzbDrone.Common/Instrumentation/LoggerExtensions.cs create mode 100644 NzbDrone.Common/Messaging/Tracking/ProcessState.cs rename NzbDrone.Core/ProgressMessaging/{ProgressMessagingTarget.cs => ProgressMessageTarget.cs} (85%) create mode 100644 UI/ProgressMessaging/ProgressMessageCollection.js create mode 100644 UI/ProgressMessaging/ProgressMessageModel.js diff --git a/NzbDrone.Api/Commands/CommandConnection.cs b/NzbDrone.Api/Commands/CommandConnection.cs index 1d9285d0b..227c69bec 100644 --- a/NzbDrone.Api/Commands/CommandConnection.cs +++ b/NzbDrone.Api/Commands/CommandConnection.cs @@ -11,7 +11,7 @@ namespace NzbDrone.Api.Commands public class CommandConnection : NzbDronePersistentConnection, IHandleAsync, IHandleAsync, - IHandle + IHandleAsync { public override string Resource { @@ -28,7 +28,7 @@ namespace NzbDrone.Api.Commands BroadcastMessage(message.TrackedCommand); } - public void Handle(CommandFailedEvent message) + public void HandleAsync(CommandFailedEvent message) { BroadcastMessage(message.TrackedCommand); } diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index fe4ffcc5a..0be2d8ce9 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -86,6 +86,9 @@ + + + diff --git a/NzbDrone.Api/ProgressMessaging/ProgressMessageConnection.cs b/NzbDrone.Api/ProgressMessaging/ProgressMessageConnection.cs new file mode 100644 index 000000000..a5b4afdb0 --- /dev/null +++ b/NzbDrone.Api/ProgressMessaging/ProgressMessageConnection.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.AspNet.SignalR; +using Microsoft.AspNet.SignalR.Infrastructure; +using NzbDrone.Api.SignalR; +using NzbDrone.Common.Messaging; +using NzbDrone.Core.ProgressMessaging; + +namespace NzbDrone.Api.ProgressMessaging +{ + public class ProgressMessageConnection : NzbDronePersistentConnection, + IHandleAsync + { + public override string Resource + { + get { return "/ProgressMessage"; } + } + + public void HandleAsync(NewProgressMessageEvent message) + { + var context = ((ConnectionManager)GlobalHost.ConnectionManager).GetConnection(GetType()); + context.Connection.Broadcast(message.ProgressMessage); + } + } +} diff --git a/NzbDrone.Api/ProgressMessaging/ProgressMessageModule.cs b/NzbDrone.Api/ProgressMessaging/ProgressMessageModule.cs new file mode 100644 index 000000000..a06db9c24 --- /dev/null +++ b/NzbDrone.Api/ProgressMessaging/ProgressMessageModule.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Nancy; +using NzbDrone.Api.Extensions; + +namespace NzbDrone.Api.ProgressMessaging +{ + public class ProgressMessageModule : NzbDroneRestModule + { + public ProgressMessageModule() + { + Get["/"] = x => GetAllMessages(); + } + + private Response GetAllMessages() + { + return new List().AsResponse(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/ProgressMessaging/ProgressMessageResource.cs b/NzbDrone.Api/ProgressMessaging/ProgressMessageResource.cs new file mode 100644 index 000000000..3117eb142 --- /dev/null +++ b/NzbDrone.Api/ProgressMessaging/ProgressMessageResource.cs @@ -0,0 +1,12 @@ +using System; +using NzbDrone.Api.REST; + +namespace NzbDrone.Api.ProgressMessaging +{ + public class ProgressMessageResource : RestResource + { + public DateTime Time { get; set; } + public String CommandId { get; set; } + public String Message { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Common.Test/EventingTests/MessageAggregatorCommandTests.cs b/NzbDrone.Common.Test/EventingTests/MessageAggregatorCommandTests.cs index 35d7d03d1..b5c45bef0 100644 --- a/NzbDrone.Common.Test/EventingTests/MessageAggregatorCommandTests.cs +++ b/NzbDrone.Common.Test/EventingTests/MessageAggregatorCommandTests.cs @@ -30,11 +30,11 @@ namespace NzbDrone.Common.Test.EventingTests Mocker.GetMock() .Setup(c => c.TrackIfNew(It.IsAny())) - .Returns(new TrackedCommand(new CommandA(), CommandState.Running)); + .Returns(new TrackedCommand(new CommandA(), ProcessState.Running)); Mocker.GetMock() .Setup(c => c.TrackIfNew(It.IsAny())) - .Returns(new TrackedCommand(new CommandB(), CommandState.Running)); + .Returns(new TrackedCommand(new CommandB(), ProcessState.Running)); } [Test] @@ -44,7 +44,7 @@ namespace NzbDrone.Common.Test.EventingTests Mocker.GetMock() .Setup(c => c.TrackIfNew(commandA)) - .Returns(new TrackedCommand(commandA, CommandState.Running)); + .Returns(new TrackedCommand(commandA, ProcessState.Running)); Subject.PublishCommand(commandA); @@ -69,7 +69,7 @@ namespace NzbDrone.Common.Test.EventingTests Mocker.GetMock() .Setup(c => c.TrackIfNew(commandA)) - .Returns(new TrackedCommand(commandA, CommandState.Running)); + .Returns(new TrackedCommand(commandA, ProcessState.Running)); Subject.PublishCommand(commandA); diff --git a/NzbDrone.Common/Instrumentation/LogEventExtensions.cs b/NzbDrone.Common/Instrumentation/LogEventExtensions.cs index cb2fe44f2..373aa9201 100644 --- a/NzbDrone.Common/Instrumentation/LogEventExtensions.cs +++ b/NzbDrone.Common/Instrumentation/LogEventExtensions.cs @@ -13,7 +13,6 @@ namespace NzbDrone.Common.Instrumentation return HashUtil.CalculateCrc(hashSeed); } - public static string GetFormattedMessage(this LogEventInfo logEvent) { var message = logEvent.FormattedMessage; diff --git a/NzbDrone.Common/Instrumentation/LoggerExtensions.cs b/NzbDrone.Common/Instrumentation/LoggerExtensions.cs new file mode 100644 index 000000000..9816965c7 --- /dev/null +++ b/NzbDrone.Common/Instrumentation/LoggerExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog; +using NzbDrone.Common.Messaging.Tracking; + +namespace NzbDrone.Common.Instrumentation +{ + public static class LoggerExtensions + { + public static void Complete(this Logger logger, string message) + { + var logEvent = new LogEventInfo(LogLevel.Info, logger.Name, message); + logEvent.Properties.Add("Status", ProcessState.Completed); + + logger.Log(logEvent); + } + + public static void Complete(this Logger logger, string message, params object[] args) + { + var formattedMessage = String.Format(message, args); + Complete(logger, formattedMessage); + } + + public static void Failed(this Logger logger, string message) + { + var logEvent = new LogEventInfo(LogLevel.Info, logger.Name, message); + logEvent.Properties.Add("Status", ProcessState.Failed); + + logger.Log(logEvent); + } + + public static void Failed(this Logger logger, string message, params object[] args) + { + var formattedMessage = String.Format(message, args); + Failed(logger, formattedMessage); + } + } +} diff --git a/NzbDrone.Common/Messaging/IProcessMessage.cs b/NzbDrone.Common/Messaging/IProcessMessage.cs index b008d9e9b..d22eeea64 100644 --- a/NzbDrone.Common/Messaging/IProcessMessage.cs +++ b/NzbDrone.Common/Messaging/IProcessMessage.cs @@ -4,7 +4,6 @@ public interface IProcessMessageAsync : IProcessMessage { } - public interface IProcessMessage : IProcessMessage { } public interface IProcessMessageAsync : IProcessMessageAsync { } diff --git a/NzbDrone.Common/Messaging/Tracking/CommandTrackingService.cs b/NzbDrone.Common/Messaging/Tracking/CommandTrackingService.cs index 2cb3b65ab..27c0c8bf1 100644 --- a/NzbDrone.Common/Messaging/Tracking/CommandTrackingService.cs +++ b/NzbDrone.Common/Messaging/Tracking/CommandTrackingService.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Common.Messaging.Tracking return null; } - var trackedCommand = new TrackedCommand(command, CommandState.Running); + var trackedCommand = new TrackedCommand(command, ProcessState.Running); Store(trackedCommand); return trackedCommand; @@ -45,7 +45,7 @@ namespace NzbDrone.Common.Messaging.Tracking if (trackedCommand == null) { - trackedCommand = new TrackedCommand(command, CommandState.Running); + trackedCommand = new TrackedCommand(command, ProcessState.Running); Store(trackedCommand); return new ExistingCommand(false, trackedCommand); @@ -57,7 +57,7 @@ namespace NzbDrone.Common.Messaging.Tracking public TrackedCommand Completed(TrackedCommand trackedCommand, TimeSpan runtime) { trackedCommand.StateChangeTime = DateTime.UtcNow; - trackedCommand.State = CommandState.Completed; + trackedCommand.State = ProcessState.Completed; trackedCommand.Runtime = runtime; Store(trackedCommand); @@ -68,7 +68,7 @@ namespace NzbDrone.Common.Messaging.Tracking public TrackedCommand Failed(TrackedCommand trackedCommand, Exception e) { trackedCommand.StateChangeTime = DateTime.UtcNow; - trackedCommand.State = CommandState.Failed; + trackedCommand.State = ProcessState.Failed; trackedCommand.Exception = e; Store(trackedCommand); @@ -94,7 +94,7 @@ namespace NzbDrone.Common.Messaging.Tracking private List Running(Type type = null) { - var running = AllTracked().Where(i => i.State == CommandState.Running); + var running = AllTracked().Where(i => i.State == ProcessState.Running); if (type != null) { @@ -116,7 +116,7 @@ namespace NzbDrone.Common.Messaging.Tracking public void Execute(TrackedCommandCleanupCommand message) { - var old = AllTracked().Where(c => c.State != CommandState.Running && c.StateChangeTime < DateTime.UtcNow.AddMinutes(-5)); + var old = AllTracked().Where(c => c.State != ProcessState.Running && c.StateChangeTime < DateTime.UtcNow.AddMinutes(-5)); foreach (var trackedCommand in old) { diff --git a/NzbDrone.Common/Messaging/Tracking/ProcessState.cs b/NzbDrone.Common/Messaging/Tracking/ProcessState.cs new file mode 100644 index 000000000..dc79c37f4 --- /dev/null +++ b/NzbDrone.Common/Messaging/Tracking/ProcessState.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Common.Messaging.Tracking +{ + public enum ProcessState + { + Running, + Completed, + Failed + } +} diff --git a/NzbDrone.Common/Messaging/Tracking/TrackedCommand.cs b/NzbDrone.Common/Messaging/Tracking/TrackedCommand.cs index 37f69de88..35b0e05ac 100644 --- a/NzbDrone.Common/Messaging/Tracking/TrackedCommand.cs +++ b/NzbDrone.Common/Messaging/Tracking/TrackedCommand.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Common.Messaging.Tracking public String Name { get; private set; } public String Type { get; private set; } public ICommand Command { get; private set; } - public CommandState State { get; set; } + public ProcessState State { get; set; } public DateTime StateChangeTime { get; set; } public TimeSpan Runtime { get; set; } public Exception Exception { get; set; } @@ -17,7 +17,7 @@ namespace NzbDrone.Common.Messaging.Tracking { } - public TrackedCommand(ICommand command, CommandState state) + public TrackedCommand(ICommand command, ProcessState state) { Id = command.CommandId; Name = command.GetType().Name; @@ -27,11 +27,4 @@ namespace NzbDrone.Common.Messaging.Tracking StateChangeTime = DateTime.UtcNow; } } - - public enum CommandState - { - Running = 0, - Completed = 1, - Failed = 2 - } } diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index 75b255d08..d25e5eb61 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -92,6 +92,8 @@ + + diff --git a/NzbDrone.Core/Indexers/RssSyncService.cs b/NzbDrone.Core/Indexers/RssSyncService.cs index baf414fac..dcbf340b1 100644 --- a/NzbDrone.Core/Indexers/RssSyncService.cs +++ b/NzbDrone.Core/Indexers/RssSyncService.cs @@ -1,5 +1,6 @@ using System.Linq; using NLog; +using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Messaging; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; @@ -38,7 +39,7 @@ namespace NzbDrone.Core.Indexers var decisions = _downloadDecisionMaker.GetRssDecision(reports); var qualifiedReports = _downloadApprovedReports.DownloadApproved(decisions); - _logger.Info("RSS Sync Completed. Reports found: {0}, Reports downloaded: {1}", reports.Count, qualifiedReports.Count()); + _logger.Complete("RSS Sync Completed. Reports found: {0}, Reports downloaded: {1}", reports.Count, qualifiedReports.Count()); } public void Execute(RssSyncCommand message) diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 6a13f8b82..6cb78cca8 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -221,7 +221,7 @@ - + diff --git a/NzbDrone.Core/ProgressMessaging/ProgressMessage.cs b/NzbDrone.Core/ProgressMessaging/ProgressMessage.cs index 876087efb..b9bc8fc6d 100644 --- a/NzbDrone.Core/ProgressMessaging/ProgressMessage.cs +++ b/NzbDrone.Core/ProgressMessaging/ProgressMessage.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using NzbDrone.Common.Messaging.Tracking; namespace NzbDrone.Core.ProgressMessaging { @@ -10,5 +8,6 @@ namespace NzbDrone.Core.ProgressMessaging public DateTime Time { get; set; } public String CommandId { get; set; } public String Message { get; set; } + public ProcessState Status { get; set; } } } diff --git a/NzbDrone.Core/ProgressMessaging/ProgressMessagingTarget.cs b/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs similarity index 85% rename from NzbDrone.Core/ProgressMessaging/ProgressMessagingTarget.cs rename to NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs index 247fa7686..ee99f7599 100644 --- a/NzbDrone.Core/ProgressMessaging/ProgressMessagingTarget.cs +++ b/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs @@ -4,17 +4,18 @@ using NLog; using NLog.Layouts; using NLog.Targets; using NzbDrone.Common.Messaging; +using NzbDrone.Common.Messaging.Tracking; using NzbDrone.Core.Lifecycle; namespace NzbDrone.Core.ProgressMessaging { - public class ProgressMessagingTarget : TargetWithLayout, IHandle, IHandle + public class ProgressMessageTarget : TargetWithLayout, IHandle, IHandle { private readonly IMessageAggregator _messageAggregator; public LoggingRule Rule { get; set; } - public ProgressMessagingTarget(IMessageAggregator messageAggregator) + public ProgressMessageTarget(IMessageAggregator messageAggregator) { _messageAggregator = messageAggregator; } @@ -55,10 +56,13 @@ namespace NzbDrone.Core.ProgressMessaging return; } + var status = logEvent.Properties.ContainsKey("Status") ? (ProcessState)logEvent.Properties["Status"] : ProcessState.Running; + var message = new ProgressMessage(); message.Time = logEvent.TimeStamp; message.CommandId = commandId; message.Message = logEvent.FormattedMessage; + message.Status = status; _messageAggregator.PublishEvent(new NewProgressMessageEvent(message)); } diff --git a/UI/ProgressMessaging/ProgressMessageCollection.js b/UI/ProgressMessaging/ProgressMessageCollection.js new file mode 100644 index 000000000..063307f10 --- /dev/null +++ b/UI/ProgressMessaging/ProgressMessageCollection.js @@ -0,0 +1,40 @@ +'use strict'; +define( + [ + 'backbone', + 'ProgressMessaging/ProgressMessageModel', + 'Shared/Messenger', + 'Mixins/backbone.signalr.mixin' + ], function (Backbone, ProgressMessageModel, Messenger) { + + var ProgressMessageCollection = Backbone.Collection.extend({ + url : window.ApiRoot + '/progressmessage', + model: ProgressMessageModel + }); + + var collection = new ProgressMessageCollection().bindSignalR(); + + collection.signalRconnection.received(function (message) { + + var type = getMessengerType(message.status); + + Messenger.show({ + id : message.commandId, + message: message.message, + type : type + }); + }); + + var getMessengerType = function (status) { + switch (status) { + case 'completed': + return 'success'; + case 'failed': + return 'error'; + default: + return 'info'; + } + } + + return collection; + }); diff --git a/UI/ProgressMessaging/ProgressMessageModel.js b/UI/ProgressMessaging/ProgressMessageModel.js new file mode 100644 index 000000000..33a6217c8 --- /dev/null +++ b/UI/ProgressMessaging/ProgressMessageModel.js @@ -0,0 +1,8 @@ +'use strict'; +define( + [ + 'backbone' + ], function (Backbone) { + return Backbone.Model.extend({ + }); + }); diff --git a/UI/Router.js b/UI/Router.js index 86b42c1ee..4388c4f69 100644 --- a/UI/Router.js +++ b/UI/Router.js @@ -5,11 +5,12 @@ require( 'marionette', 'Controller', 'Series/SeriesCollection', + 'ProgressMessaging/ProgressMessageCollection', 'Shared/Actioneer', 'Navbar/NavbarView', 'jQuery/RouteBinder', 'jquery' - ], function (App, Marionette, Controller, SeriesCollection, Actioneer, NavbarView, RouterBinder, $) { + ], function (App, Marionette, Controller, SeriesCollection, ProgressMessageCollection, Actioneer, NavbarView, RouterBinder, $) { var Router = Marionette.AppRouter.extend({ diff --git a/UI/Series/Index/SeriesIndexLayout.js b/UI/Series/Index/SeriesIndexLayout.js index 59ae9163b..dd44e3cb6 100644 --- a/UI/Series/Index/SeriesIndexLayout.js +++ b/UI/Series/Index/SeriesIndexLayout.js @@ -105,7 +105,6 @@ define( title : 'RSS Sync', icon : 'icon-rss', command : 'rsssync', - successMessage: 'RSS Sync Completed', errorMessage : 'RSS Sync Failed!' }, {