public void ProgressManager_MultipleOperations_Sequential_Test() { // Create manager var manager = new ProgressManager(); // Create operations var operations = manager.CreateOperations(3); // Assert initial state after creating operations Assert.That(manager.Progress, Is.Zero); Assert.That(manager.IsActive, Is.True); Assert.That(manager.Operations.Count, Is.EqualTo(operations.Count)); // Loop through each operation and report progress for (var i = 0; i < operations.Count; i++) { // Assert intermediate state Assert.That(manager.Progress, Is.EqualTo(1.0 * i / operations.Count)); Assert.That(manager.IsActive, Is.True); Assert.That(manager.Operations.Count, Is.EqualTo(operations.Count)); using (var operation = operations[i]) { operation.Report(0.2); operation.Report(0.5); operation.Report(0.9); } } // Assert final state Assert.That(manager.Progress, Is.Zero); Assert.That(manager.IsActive, Is.False); Assert.That(manager.Operations.Count, Is.Zero); }
public async void ExportChannels() { // Get last used token var token = _settingsService.LastToken !; // Create dialog var dialog = _viewModelFactory.CreateExportSetupViewModel(SelectedGuild !, SelectedChannels !); // Show dialog, if canceled - return if (await _dialogManager.ShowDialogAsync(dialog) != true) { return; } // Create a progress operation for each channel to export var operations = ProgressManager.CreateOperations(dialog.Channels.Count); // Export channels var successfulExportCount = 0; for (var i = 0; i < dialog.Channels.Count; i++) { var operation = operations[i]; var channel = dialog.Channels[i]; try { await _exportService.ExportChatLogAsync(token, dialog.Guild, channel, dialog.OutputPath !, dialog.SelectedFormat, dialog.PartitionLimit, dialog.After, dialog.Before, operation); successfulExportCount++; } catch (HttpErrorStatusCodeException ex) when(ex.StatusCode == HttpStatusCode.Forbidden) { Notifications.Enqueue($"You don't have access to channel [{channel.Model.Name}]"); } catch (HttpErrorStatusCodeException ex) when(ex.StatusCode == HttpStatusCode.NotFound) { Notifications.Enqueue($"Channel [{channel.Model.Name}] doesn't exist"); } catch (DomainException ex) { Notifications.Enqueue(ex.Message); } finally { operation.Dispose(); } } // Notify of overall completion if (successfulExportCount > 0) { Notifications.Enqueue($"Successfully exported {successfulExportCount} channel(s)"); } }
public void Progress_manager_can_create_multiple_operations_at_once() { // Arrange var manager = new ProgressManager(); // Act var operations = manager.CreateOperations(3); // Assert operations.Should().HaveCount(3); }
public void ProgressManager_MultipleOperations_DifferentWeight_Test() { // Create manager var manager = new ProgressManager(); // Create operations var operations = manager.CreateOperations(3, i => i + 1); // Assert initial state after creating operations Assert.That(manager.Progress, Is.Zero); Assert.That(manager.IsActive, Is.True); Assert.That(manager.Operations.Count, Is.EqualTo(operations.Count)); // Report new progress const double newProgress = 0.5; foreach (var operation in operations) { operation.Report(newProgress); } // Assert intermediate state Assert.That(manager.Progress, Is.EqualTo(newProgress)); Assert.That(manager.IsActive, Is.True); Assert.That(manager.Operations.Count, Is.EqualTo(operations.Count)); // Report completion foreach (var operation in operations) { operation.Dispose(); } // Assert final state Assert.That(manager.Progress, Is.Zero); Assert.That(manager.IsActive, Is.False); Assert.That(manager.Operations.Count, Is.Zero); }
public async void ExportChannels() { // Get last used token var token = _settingsService.LastToken; // Create dialog var dialog = _viewModelFactory.CreateExportSetupViewModel(SelectedGuild, SelectedChannels); // Show dialog, if canceled - return if (await _dialogManager.ShowDialogAsync(dialog) != true) { return; } // Create a progress operation for each channel to export var operations = ProgressManager.CreateOperations(dialog.Channels.Count); // Export channels for (var i = 0; i < dialog.Channels.Count; i++) { // Get operation and channel var operation = operations[i]; var channel = dialog.Channels[i]; try { // Generate file path if necessary var filePath = dialog.OutputPath; if (ExportHelper.IsDirectoryPath(filePath)) { // Generate default file name var fileName = ExportHelper.GetDefaultExportFileName(dialog.SelectedFormat, dialog.Guild, channel, dialog.From, dialog.To); // Combine paths filePath = Path.Combine(filePath, fileName); } // Get chat log var chatLog = await _dataService.GetChatLogAsync(token, dialog.Guild, channel, dialog.From, dialog.To, operation); // Export _exportService.ExportChatLog(chatLog, filePath, dialog.SelectedFormat, dialog.PartitionLimit); // Notify completion Notifications.Enqueue($"Channel [{channel.Model.Name}] successfully exported"); } catch (HttpErrorStatusCodeException ex) when(ex.StatusCode == HttpStatusCode.Forbidden) { Notifications.Enqueue($"You don't have access to channel [{channel.Model.Name}]"); } catch (HttpErrorStatusCodeException ex) when(ex.StatusCode == HttpStatusCode.NotFound) { Notifications.Enqueue($"Channel [{channel.Model.Name}] doesn't exist"); } finally { // Dispose progress operation operation.Dispose(); } } }
protected async ValueTask ExportMultipleAsync(IConsole console, IReadOnlyList <Channel> channels) { // This uses a separate route from ExportCommandBase because the progress ticker is not thread-safe // Ugly code ahead. Will need to refactor. if (!string.IsNullOrWhiteSpace(DateFormat)) { SettingsService.DateFormat = DateFormat; } // Progress console.Output.Write($"Exporting {channels.Count} channels... "); var ticker = console.CreateProgressTicker(); // TODO: refactor this after improving Gress var progressManager = new ProgressManager(); progressManager.PropertyChanged += (sender, args) => ticker.Report(progressManager.Progress); var operations = progressManager.CreateOperations(channels.Count); // Export channels using var semaphore = new SemaphoreSlim(ParallelLimit.ClampMin(1)); var errors = new List <string>(); await Task.WhenAll(channels.Select(async(channel, i) => { var operation = operations[i]; await semaphore.WaitAsync(); var guild = await DataService.GetGuildAsync(Token, channel.GuildId); try { await ExportService.ExportChatLogAsync(Token, guild, channel, OutputPath, ExportFormat, PartitionLimit, After, Before, operation); } catch (HttpErrorStatusCodeException ex) when(ex.StatusCode == HttpStatusCode.Forbidden) { errors.Add("You don't have access to this channel."); } catch (HttpErrorStatusCodeException ex) when(ex.StatusCode == HttpStatusCode.NotFound) { errors.Add("This channel doesn't exist."); } catch (DomainException ex) { errors.Add(ex.Message); } finally { semaphore.Release(); operation.Dispose(); } })); ticker.Report(1); console.Output.WriteLine(); foreach (var error in errors) { console.Error.WriteLine(error); } console.Output.WriteLine("Done."); }