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.
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; }
}
}
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
¿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.
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.
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.