idioma
Llámenos: 1-800-497-0151

Blog

Cómo hacer que el cuadro de diálogo de progreso de la gestión de datos sea útil para los usuarios

  • , Consultor

No me gustan los diálogos de progreso. ¿Por qué? Creo que la mayoría mienten. 3 segundos para el 98% y luego 10 minutos para el último 2%. ¡¿QUÉ?! Hagámoslo mejor. Usemos el Diálogo de Progreso de Gestión de Datos de OneStream para informar al usuario, para que sepa la verdad sobre la tarea en ejecución.

Ya que estamos, permitamos al usuario cancelar una tarea en ejecución y detenerla.

Si has creado algún proceso de Gestión de Datos de larga duración dentro de OneStream, ya sabes, de esos que se repiten durante meses o llaman a sistemas externos… Entonces probablemente hayas pensado que sería útil proporcionar retroalimentación útil mientras se ejecuta.

Aquí es donde entra en juego BRApi.TaskActivity.UpdateRunningTaskActivityAndCheckIfCanceled. Permite enviar mensajes de estado al cuadro de diálogo de Gestión de Datos y, lo que es igual de importante, comprobar si el usuario ha decidido cancelar el proceso.

El patrón básico es simple: llama a este método regularmente a lo largo del código, pasa un mensaje que describa lo que estás haciendo y usa el valor de retorno verdadero/falso para decidir si salir. No me gusta escribir nombres de funciones de más de 60 caracteres que están enterrados en objetos profundos de una biblioteca. Y BRApi.TaskActivity.UpdateRunningTaskActivityAndCheckIfCanceled es así de feo. Solo necesita una API. Así que escribamos una. En este caso, se trata de un método contenedor simple de una línea llamado UpdateDMDialog(message As String). La ​​función devuelve el mismo booleano que nuestra función de biblioteca de más de 60 caracteres, indicando si el usuario ha hecho clic en cancelar. Por lo tanto, si devuelve verdadero, sé que el usuario canceló el proceso y también puedo salir de mi código. Esto ofrece a los usuarios una forma de detener el proceso sin matar el servidor ni esperar a que se agote el tiempo de espera.

Consulta el código a continuación para ver un ejemplo práctico de cómo usar BRApi.TaskActivity.UpdateRunningTaskActivityAndCheckIfCanceled

El verdadero valor aquí reside en los mensajes que envías. Piensa menos en "30 % completado" y más en "Obteniendo registros de mayo..." o "Esperando el trabajo de NetSuite 12734...". Cualquier cosa que muestre lo que está sucediendo y en qué punto del proceso te encuentras da a los usuarios confianza o, al menos, una idea de si deben ir a tomar un café o cancelar. Incluso puedes incluir el recuento de registros, los ID de las tareas o la latencia de la API si quieres algo más sofisticado.

Utilizo este patrón en integraciones de datos, importaciones de archivos y prácticamente en cualquier lugar donde las cosas puedan tardar o fallar a mitad del proceso. Es ligero, da control al usuario y es mucho mejor que dejarlos sentados mirando un cuadro de diálogo que gira y miente sobre el progreso real. Añade unas cuantas llamadas a UpdateDMDialog en tu bucle, ¡y listo! Usuario feliz e informado.

C#

public class DataImporter
{
    private readonly object si;
    private readonly object _args;
    private readonly string _dataType;
    private decimal _progress;

    public DataImporter(object sessionInfo, object args, string dataType)
    {
        si = sessionInfo;
        _args = args;
        _dataType = dataType;
        _progress = 0m;
    }

    private bool UpdateDMDialog(string message)
    {
        return BRApi.TaskActivity.UpdateRunningTaskActivityAndCheckIfCanceled(si, _args, message, _progress);
    }

    public async Task<DataTable> GetData(string yearInput)
    {
        var response = new List<string>();
        long totalRecords = 0;

        UpdateDMDialog($"Starting {_dataType} import for year {yearInput}");

        for (int month = 1; month <= 12; month++)
        {
            string monthStr = $"{yearInput}-{month:D2}";
            UpdateDMDialog($"Starting {monthStr}...");

            string taskId = await Mock_DataRequestStart(monthStr);
            await Mock_DataRequestStatus(taskId);

            var result = await Mock_DataRequestComplete(monthStr, taskId);

            _progress = month * 8.33m;
            UpdateDMDialog($"Finished {monthStr} — {result.RecordCount} records");

            if (result.Data != null)
            {
                if (response.Count == 0)
                    response.AddRange(result.Data);
                else
                    response.AddRange(result.Data.Skip(1));

                totalRecords += result.RecordCount;
            }

            if (UpdateDMDialog($"Imported {monthStr}")) return new DataTable();
        }

        var table = Mock_ToDataTable(response);
        string preview = JsonConvert.SerializeObject(
            table.AsEnumerable().Take(5).Select(r => r.ItemArray), Formatting.Indented);

        UpdateDMDialog($"All months complete. Total records: {totalRecords}");
        BRApi.ErrorLog.LogMessage(si, $"Preview:\n{preview}");

        return table;
    }

    // Mock helpers
    private Task<string> Mock_DataRequestStart(string month)
        => Task.FromResult(Guid.NewGuid().ToString());

    private Task Mock_DataRequestStatus(string taskId)
        => Task.Delay(200);

    private Task<DataResult> Mock_DataRequestComplete(string month, string taskId)
        => Task.FromResult(new DataResult
        {
            Data = new List<string> { "header", $"data for {month}" },
            RecordCount = 1
        });

    private DataTable Mock_ToDataTable(List<string> rows)
    {
        var table = new DataTable();
        table.Columns.Add("Content", typeof(string));
        foreach (var row in rows)
            table.Rows.Add(row);
        return table;
    }

    private class DataResult
    {
        public List<string> Data { get; set; }
        public long RecordCount { get; set; }
    }
}

VB.NET

Public Class DataImporter
    Private ReadOnly si As Object
    Private ReadOnly _args As Object
    Private ReadOnly _dataType As String
    Private _progress As Decimal

    Public Sub New(sessionInfo As Object, args As Object, dataType As String)
        Me.si = sessionInfo
        Me._args = args
        Me._dataType = dataType
        Me._progress = 0D
    End Sub

    Private Function UpdateDMDialog(message As String) As Boolean
        Return BRApi.TaskActivity.UpdateRunningTaskActivityAndCheckIfCanceled(si, _args, message, _progress)
    End Function

    Public Async Function GetData(yearInput As String) As Task(Of DataTable)
        Dim response As New List(Of String)()
        Dim totalRecords As Long = 0

        UpdateDMDialog($"Starting {_dataType} import for year {yearInput}")

        For month As Integer = 1 To 12
            Dim monthStr As String = $"{yearInput}-{month:D2}"
            UpdateDMDialog($"Starting {monthStr}...")

            Dim taskId As String = Await Mock_DataRequestStart(monthStr)
            Await Mock_DataRequestStatus(taskId)

            Dim result As DataResult = Await Mock_DataRequestComplete(monthStr, taskId)

            _progress = month * 8.33D
            UpdateDMDialog($"Finished {monthStr} — {result.RecordCount} records")

            If result.Data IsNot Nothing Then
                If response.Count = 0 Then
                    response.AddRange(result.Data)
                Else
                    response.AddRange(result.Data.Skip(1))
                End If

                totalRecords += result.RecordCount
            End If

            If UpdateDMDialog($"Imported {monthStr}") Then
                Return New DataTable()
            End If
        Next

        Dim table As DataTable = Mock_ToDataTable(response)
        Dim preview As String = JsonConvert.SerializeObject(
            table.AsEnumerable().Take(5).Select(Function(r) r.ItemArray),
            Formatting.Indented
        )

        UpdateDMDialog($"All months complete. Total records: {totalRecords}")
        BRApi.ErrorLog.LogMessage(si, $"Preview:{vbLf}{preview}")

        Return table
    End Function

    ' Mock helpers
    Private Function Mock_DataRequestStart(month As String) As Task(Of String)
        Return Task.FromResult(Guid.NewGuid().ToString())
    End Function

    Private Function Mock_DataRequestStatus(taskId As String) As Task
        Return Task.Delay(200)
    End Function

    Private Function Mock_DataRequestComplete(month As String, taskId As String) As Task(Of DataResult)
        Dim result As New DataResult With {
            .Data = New List(Of String) From {"header", $"data for {month}"},
            .RecordCount = 1
        }
        Return Task.FromResult(result)
    End Function

    Private Function Mock_ToDataTable(rows As List(Of String)) As DataTable
        Dim table As New DataTable()
        table.Columns.Add("Content", GetType(String))
        For Each row As String In rows
            table.Rows.Add(row)
        Next
        Return table
    End Function

    Private Class DataResult
        Public Property Data As List(Of String)
        Public Property RecordCount As Long
    End Class
End Class

Póngase en contacto con MindStream Analytics

¿Desea obtener más información sobre el software OneStream? Complete el formulario a continuación y nos pondremos en contacto con usted en breve.


Seminario web destacado

OneStream Quick Views

Descubra el poder de OneStream Quickviews y mejore su proceso de planificación y análisis financiero (FP&A) con este interesante seminario web presentado por Erick Lewis de MindStream Analytics.

Cómo OneStream Quickviews le da la A a FP&A

Partner SpotLight

OneStream Diamond Partner

OneStream CPM

OneStream aligns to your business needs and changes more quickly and easily than any other product by offering one platform and one model for all financial CPM solutions. OneStream employs Guided Workflows, validations and flexible mapping to deliver data quality confidence for all collections and analysis while reducing risk throughout the entire auditable financial process.

OneStream Profile