diff --git a/src/DynamoCLI/CommandLineRunner.cs b/src/DynamoCLI/CommandLineRunner.cs
index 8b9a26547c0..b10f511bed6 100644
--- a/src/DynamoCLI/CommandLineRunner.cs
+++ b/src/DynamoCLI/CommandLineRunner.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
@@ -46,7 +46,7 @@ private static XmlDocument RunCommandLineArgs(DynamoModel model, StartupUtils.Co
Console.WriteLine("geometryFilePath option is only available when running DynamoWPFCLI, not DynamoCLI");
}
- model.HostAnalyticsInfo = cmdLineArgs.AnalyticsInfo;
+ DynamoModel.HostAnalyticsInfo = cmdLineArgs.AnalyticsInfo;
cmdLineArgs.ImportedPaths.ToList().ForEach(path =>
{
diff --git a/src/DynamoCore/Extensions/ReadyParams.cs b/src/DynamoCore/Extensions/ReadyParams.cs
index bf0a25c58ab..8de32c8fe06 100644
--- a/src/DynamoCore/Extensions/ReadyParams.cs
+++ b/src/DynamoCore/Extensions/ReadyParams.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using Dynamo.Graph.Workspaces;
@@ -78,7 +78,7 @@ public virtual ICommandExecutive CommandExecutive
///
/// HostInfo object, Useful to determine what host context Dynamo is running in.
///
- internal HostAnalyticsInfo HostInfo => dynamoModel.HostAnalyticsInfo;
+ internal HostAnalyticsInfo HostInfo => DynamoModel.HostAnalyticsInfo;
///
diff --git a/src/DynamoCore/Extensions/StartupParams.cs b/src/DynamoCore/Extensions/StartupParams.cs
index 3820c74ca7d..033a800e4f5 100644
--- a/src/DynamoCore/Extensions/StartupParams.cs
+++ b/src/DynamoCore/Extensions/StartupParams.cs
@@ -120,7 +120,7 @@ internal StartupParams(DynamoModel dynamoModel)
pathManager = dynamoModel.PathManager;
libraryLoader = new ExtensionLibraryLoader(dynamoModel);
customNodeManager = dynamoModel.CustomNodeManager;
- dynamoVersion = new Version(dynamoModel.Version);
+ dynamoVersion = new Version(DynamoModel.Version);
preferences = dynamoModel.PreferenceSettings;
linterManager = dynamoModel.LinterManager;
IsGeometryLibraryLoaded = dynamoModel.IsASMLoaded;
diff --git a/src/DynamoCore/Logging/AnalyticsService.cs b/src/DynamoCore/Logging/AnalyticsService.cs
index 9af4b8333c7..500796725dc 100644
--- a/src/DynamoCore/Logging/AnalyticsService.cs
+++ b/src/DynamoCore/Logging/AnalyticsService.cs
@@ -1,4 +1,3 @@
-using Dynamo.Graph.Workspaces;
using Dynamo.Models;
using Autodesk.Analytics.ADP;
using Autodesk.Analytics.Core;
@@ -9,49 +8,29 @@ namespace Dynamo.Logging
///
/// Utility class to support analytics tracking.
///
- class AnalyticsService
+ internal class AnalyticsService
{
// Use the Analytics.Core interface so that we do not have to load the ADP assembly at this time.
private static IAnalyticsUI adpAnalyticsUI;
///
- /// Starts the client when DynamoModel is created. This method initializes
+ /// Indicates that we don't want to shut down analytics when DynamoModel shuts down.
+ /// Sometimes we want to keep Analytics service running even when we don't have a DynamoModel started.
+ ///
+ internal static bool KeepAlive { get; set; }
+
+ ///
+ /// Starts the Analytics client. This method initializes
/// the Analytics service and application life cycle start is tracked.
///
- /// DynamoModel
- /// Analytics won't be started if IsHeadless, but ADP may be loaded to be disabled.
- /// Analytics won't be started if isTestMode, ADP will not be loaded.
- internal static void Start(DynamoModel model, bool isHeadless, bool isTestMode)
+ internal static void Start()
{
- if (isTestMode)
- {
- if (Analytics.DisableAnalytics)
- {
- model.Logger.Log("Incompatible configuration: [IsTestMode] and [Analytics disabled] ");
- }
- return;
- }
-
- if (isHeadless)
- {
- return;
- }
-
// Initialize the concrete class only when we initialize the Service.
// This will also load the Analytics.Net.ADP assembly
// We must initialize the ADPAnalyticsUI instance before the Analytics.Start call.
adpAnalyticsUI = new ADPAnalyticsUI();
- Analytics.Start(new DynamoAnalyticsClient(model));
- model.WorkspaceAdded += OnWorkspaceAdded;
- }
-
- static void OnWorkspaceAdded(WorkspaceModel obj)
- {
- if (obj is CustomNodeWorkspaceModel)
- Analytics.TrackScreenView("CustomWorkspace");
- else
- Analytics.TrackScreenView("Workspace");
+ Analytics.Start(new DynamoAnalyticsClient(DynamoModel.HostAnalyticsInfo));
}
///
@@ -99,7 +78,10 @@ internal static bool IsADPAvailable()
///
internal static void ShutDown()
{
- Analytics.ShutDown();
+ if (!KeepAlive)
+ {
+ Analytics.ShutDown();
+ }
}
///
@@ -113,6 +95,7 @@ internal static void ShowADPConsentDialog(IntPtr? host)
adpAnalyticsUI.ShowOptInDialog(System.Threading.Thread.CurrentThread.CurrentUICulture.Name, false, host);
}
}
+
internal static string GetUserIDForSession()
{
if (Analytics.client is DynamoAnalyticsClient dac)
diff --git a/src/DynamoCore/Logging/DynamoAnalyticsClient.cs b/src/DynamoCore/Logging/DynamoAnalyticsClient.cs
index 7c72eff1b74..0e8471136ce 100644
--- a/src/DynamoCore/Logging/DynamoAnalyticsClient.cs
+++ b/src/DynamoCore/Logging/DynamoAnalyticsClient.cs
@@ -1,9 +1,10 @@
using System;
using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
using Autodesk.Analytics.ADP;
using Autodesk.Analytics.Core;
using Autodesk.Analytics.Events;
-using Dynamo.Interfaces;
using Dynamo.Models;
using Microsoft.Win32;
@@ -17,7 +18,7 @@ public DynamoAnalyticsSession()
SessionId = Guid.NewGuid().ToString();
}
- public void Start(DynamoModel model)
+ public void Start()
{
StabilityCookie.Startup();
}
@@ -68,8 +69,11 @@ public static String GetUserID()
///
/// Dynamo specific implementation of IAnalyticsClient
///
- class DynamoAnalyticsClient : IAnalyticsClient, IDisposable
+ internal class DynamoAnalyticsClient : IAnalyticsClient, IDisposable
{
+ private readonly ManualResetEventSlim serviceInitialized = new ManualResetEventSlim(false);
+ private readonly object trackEventLockObj = new object();
+
///
/// A dummy IDisposable class
///
@@ -84,8 +88,6 @@ public void Dispose() { }
private const string ANALYTICS_PROPERTY = "UA-52186525-1";
#endif
- private readonly IPreferences preferences = null;
-
public static IDisposable Disposable { get { return new Dummy(); } }
private readonly ProductInfo product;
@@ -107,29 +109,24 @@ public bool ReportingAnalytics
}
///
- /// Constructs DynamoAnalyticsClient with given DynamoModel
+ /// Constructs DynamoAnalyticsClient from existing HostAnalyticsInfo
///
- /// DynamoModel
- public DynamoAnalyticsClient(DynamoModel dynamoModel)
+ public DynamoAnalyticsClient(HostAnalyticsInfo hostAnalyticsInfo)
{
- //Set the preferences, so that we can get live value of analytics
- //reporting approved status.
- preferences = dynamoModel.PreferenceSettings;
-
if (Session == null) Session = new DynamoAnalyticsSession();
//Setup Analytics service, and StabilityCookie.
- Session.Start(dynamoModel);
+ Session.Start();
//Dynamo app version.
- var appversion = dynamoModel.AppVersion;
+ var appversion = DynamoModel.AppVersion;
- var hostName = string.IsNullOrEmpty(dynamoModel.HostName) ? "Dynamo" : dynamoModel.HostName;
+ var hostName = string.IsNullOrEmpty(hostAnalyticsInfo.HostName) ? "Dynamo" : hostAnalyticsInfo.HostName;
- hostInfo = new HostContextInfo() { ParentId = dynamoModel.HostAnalyticsInfo.ParentId, SessionId = dynamoModel.HostAnalyticsInfo.SessionId };
+ hostInfo = new HostContextInfo() { ParentId = hostAnalyticsInfo.ParentId, SessionId = hostAnalyticsInfo.SessionId };
string buildId = String.Empty, releaseId = String.Empty;
- if (Version.TryParse(dynamoModel.Version, out Version version))
+ if (Version.TryParse(DynamoModel.Version, out Version version))
{
buildId = $"{version.Major}.{version.Minor}.{version.Build}"; // BuildId has the following format major.minor.build, ex: 2.5.1
releaseId = $"{version.Major}.{version.Minor}.0"; // ReleaseId has the following format: major.minor.0; ex: 2.5.0
@@ -148,16 +145,9 @@ private void RegisterADPTracker(Service service)
}
}
- ///
- /// Starts the client when DynamoModel is created. This method initializes
- /// the Analytics service and application life cycle start is tracked.
- ///
- public void Start()
+ private void StartInternal()
{
- // Start Analytics service regardless of optin status.
- // Each track event will be enabled/disabled based on the corresponding optin status.
- // Ex. ADP will manage optin status internally
- if (preferences != null && !Analytics.DisableAnalytics)
+ if (!Analytics.DisableAnalytics)
{
//Register trackers
var service = Service.Instance;
@@ -167,102 +157,214 @@ public void Start()
RegisterADPTracker(service);
//If not ReportingAnalytics, then set the idle time as infinite so idle state is not recorded.
- Service.StartUp(product, new UserInfo(Session.UserId), hostInfo, TimeSpan.FromMinutes(30));
- TrackPreferenceInternal("ReportingAnalytics", "", ReportingAnalytics ? 1 : 0);
+ Service.StartUp(product, new UserInfo(Session.UserId), hostInfo, TimeSpan.FromMinutes(30));
}
+
+ serviceInitialized.Set();
+ }
+ ///
+ /// Starts the client when DynamoModel is created. This method initializes
+ /// the Analytics service and application life cycle start is tracked.
+ ///
+ public void Start()
+ {
+ // Start Analytics service regardless of optin status.
+ // Each track event will be enabled/disabled based on the corresponding optin status.
+ // Ex. ADP will manage optin status internally
+ Task.Run(() => StartInternal());
+
+ TrackPreference("ReportingAnalytics", "", ReportingAnalytics ? 1 : 0);
}
public void ShutDown()
{
+ if (!Analytics.DisableAnalytics) serviceInitialized.Wait();
Dispose();
}
public void TrackEvent(Actions action, Categories category, string description, int? value)
{
- if (!ReportingAnalytics) return;
+ if (Analytics.DisableAnalytics) return;
- var e = AnalyticsEvent.Create(category.ToString(), action.ToString(), description, value);
- e.Track();
+ Task.Run(() =>
+ {
+ serviceInitialized.Wait();
+
+ lock(trackEventLockObj)
+ {
+ if (!ReportingAnalytics) return;
+
+ var e = AnalyticsEvent.Create(category.ToString(), action.ToString(), description, value);
+ e.Track();
+ }
+ });
}
public void TrackPreference(string name, string stringValue, int? metricValue)
{
- if (!ReportingAnalytics) return;
+ if (Analytics.DisableAnalytics) return;
- TrackPreferenceInternal(name, stringValue, metricValue);
- }
+ Task.Run(() =>
+ {
+ serviceInitialized.Wait();
- private void TrackPreferenceInternal(string name, string stringValue, int? metricValue)
- {
- var e = AnalyticsEvent.Create(Categories.Preferences.ToString(), name, stringValue, metricValue);
- e.Track();
+ lock (trackEventLockObj)
+ {
+ if (!ReportingAnalytics) return;
+
+ var e = AnalyticsEvent.Create(Categories.Preferences.ToString(), name, stringValue, metricValue);
+ e.Track();
+ }
+ });
}
public void TrackTimedEvent(Categories category, string variable, TimeSpan time, string description = "")
{
- if (!ReportingAnalytics) return;
+ if (Analytics.DisableAnalytics) return;
- var e = new TimedEvent(time)
+ Task.Run(() =>
{
- Category = category.ToString(),
- VariableName = variable,
- Description = description
- };
- e.Track();
+ serviceInitialized.Wait();
+
+ lock (trackEventLockObj)
+ {
+ if (!ReportingAnalytics) return;
+
+ var e = new TimedEvent(time)
+ {
+ Category = category.ToString(),
+ VariableName = variable,
+ Description = description
+ };
+ e.Track();
+ }
+ });
}
public void TrackScreenView(string viewName)
{
- if (!ReportingAnalytics) return;
+ if (Analytics.DisableAnalytics) return;
- var e = new ScreenViewEvent(viewName);
- e.Track();
+ Task.Run(() =>
+ {
+ serviceInitialized.Wait();
+
+ lock (trackEventLockObj)
+ {
+ if (!ReportingAnalytics) return;
+
+ var e = new ScreenViewEvent(viewName);
+ e.Track();
+ }
+ });
}
public void TrackException(Exception ex, bool isFatal)
{
- if (!ReportingAnalytics) return;
+ if (Analytics.DisableAnalytics) return;
+
+ Task.Run(() =>
+ {
+ serviceInitialized.Wait();
+
+ lock (trackEventLockObj)
+ {
+ if (!ReportingAnalytics) return;
- Service.TrackException(ex, isFatal);
+ Service.TrackException(ex, isFatal);
+ }
+ });
}
+ [Obsolete("Method will become private in Dynamo 4.0, please use CreateTaskTimedEvent")]
public IDisposable CreateTimedEvent(Categories category, string variable, string description, int? value)
- {
- if (!ReportingAnalytics) return Disposable;
+ {
+ serviceInitialized.Wait();
- var e = new TimedEvent()
+ lock (trackEventLockObj)
{
- Category = category.ToString(),
- VariableName = variable,
- Description = description,
- Value = value
- };
- //Timed event does not need startup tracking.
- return e;
+ if (!ReportingAnalytics) return Disposable;
+
+ var e = new TimedEvent()
+ {
+ Category = category.ToString(),
+ VariableName = variable,
+ Description = description,
+ Value = value
+ };
+ //Timed event does not need startup tracking.
+ return e;
+ }
+ }
+
+ public Task CreateTaskTimedEvent(Categories category, string variable, string description, int? value)
+ {
+ if (Analytics.DisableAnalytics) return Task.FromResult(Disposable);
+
+ return Task.Run(() => CreateTimedEvent(category, variable, description, value));
}
+ [Obsolete("Property will become private in Dynamo 4.0, please use CreateTaskCommandEvent")]
public IDisposable CreateCommandEvent(string name, string description, int? value)
{
- if (!ReportingAnalytics) return Disposable;
+ serviceInitialized.Wait();
+
+ lock (trackEventLockObj)
+ {
+ if (!ReportingAnalytics) return Disposable;
- var e = new CommandEvent(name) { Description = description, Value = value };
- e.Track();
- return e;
+ var e = new CommandEvent(name) { Description = description, Value = value };
+ e.Track();
+ return e;
+ }
+ }
+
+ public Task CreateTaskCommandEvent(string name, string description, int? value)
+ {
+ if (Analytics.DisableAnalytics) return Task.FromResult(Disposable);
+
+ return Task.Run(() => CreateCommandEvent(name, description, value));
}
+ public void EndEventTask(Task taskToEnd)
+ {
+ if (Analytics.DisableAnalytics) return;
+
+ Task.Run(() =>
+ {
+ lock(trackEventLockObj)
+ {
+ taskToEnd.Wait();
+ taskToEnd.Result.Dispose();
+ }
+ });
+ }
+
+ [Obsolete("Property will become private in Dynamo 4.0, please use TrackTaskFileOperationEvent")]
public IDisposable TrackFileOperationEvent(string filepath, Actions operation, int size, string description)
{
+ serviceInitialized.Wait();
if (!ReportingAnalytics) return Disposable;
- var e = new FileOperationEvent()
+ lock(trackEventLockObj)
{
- FilePath = filepath,
- FileSize = size,
- FileAction = FileAction(operation),
- Description = description
- };
- e.Track();
- return e;
+ var e = new FileOperationEvent()
+ {
+ FilePath = filepath,
+ FileSize = size,
+ FileAction = FileAction(operation),
+ Description = description
+ };
+ e.Track();
+ return e;
+ }
+ }
+
+ public Task TrackTaskFileOperationEvent(string filepath, Actions operation, int size, string description)
+ {
+ if (Analytics.DisableAnalytics) return Task.FromResult(Disposable);
+
+ return Task.Run(() => TrackFileOperationEvent(filepath, operation, size, description));
}
private FileOperationEvent.Actions FileAction(Actions operation)
diff --git a/src/DynamoCore/Logging/IAnalyticsSession.cs b/src/DynamoCore/Logging/IAnalyticsSession.cs
index 6d0fe530914..da823da1ffb 100644
--- a/src/DynamoCore/Logging/IAnalyticsSession.cs
+++ b/src/DynamoCore/Logging/IAnalyticsSession.cs
@@ -1,5 +1,4 @@
-using System;
-using Dynamo.Models;
+using System;
namespace Dynamo.Logging
{
@@ -21,11 +20,10 @@ public interface IAnalyticsSession : IDisposable
string SessionId { get; }
///
- /// Starts the session for the given DynamoModel.
+ /// Starts the session.
/// The Session is closed when Dispose() is called.
///
- /// DynamoModel
- void Start(DynamoModel model);
+ void Start();
///
/// Returns a logger to record usage.
///
diff --git a/src/DynamoCore/Models/DynamoModel.cs b/src/DynamoCore/Models/DynamoModel.cs
index 31bc7fd8ff2..0d3368d1f66 100644
--- a/src/DynamoCore/Models/DynamoModel.cs
+++ b/src/DynamoCore/Models/DynamoModel.cs
@@ -199,7 +199,7 @@ internal LuceneSearchUtility LuceneUtility
///
/// This version of Dynamo.
///
- public string Version
+ public static string Version
{
get { return DefaultUpdateManager.GetProductVersion().ToString(); }
}
@@ -218,7 +218,7 @@ public string Version
///
/// Host analytics info
///
- public HostAnalyticsInfo HostAnalyticsInfo { get; set; }
+ public static HostAnalyticsInfo HostAnalyticsInfo { get; set; }
///
/// Boolean indication of launching Dynamo in service mode, this mode is optimized for minimal launch time, mostly leveraged by CLI or WPF CLI.
@@ -288,7 +288,7 @@ public string Version
///
/// The application version string for analytics reporting APIs
///
- internal virtual string AppVersion
+ internal static string AppVersion
{
get
{
@@ -740,20 +740,7 @@ protected DynamoModel(IStartConfiguration config)
// or the feature flags client for web traffic reason.
if (!IsServiceMode && !areAnalyticsDisabledFromConfig && !Analytics.DisableAnalytics)
{
- // Start the Analytics service only when a session is not present.
- // In an integrator host, as splash screen can be closed without shutting down the ViewModel, the analytics service is not stopped.
- // So we don't want to start it when splash screen or dynamo window is launched again.
- if (Analytics.client == null)
- {
- AnalyticsService.Start(this, IsHeadless, IsTestMode);
- }
- else if (Analytics.client is DynamoAnalyticsClient dac)
- {
- if (dac.Session == null)
- {
- AnalyticsService.Start(this, IsHeadless, IsTestMode);
- }
- }
+ HandleAnalytics();
//run process startup/reading on another thread so we don't block dynamo startup.
//if we end up needing to control aspects of dynamo model or view startup that we can't make
@@ -1031,6 +1018,38 @@ protected DynamoModel(IStartConfiguration config)
DynamoReady(new ReadyParams(this));
}
+ private void HandleAnalytics()
+ {
+ if (IsTestMode)
+ {
+ if (Analytics.DisableAnalytics)
+ {
+ Logger.Log("Incompatible configuration: [IsTestMode] and [Analytics disabled] ");
+ }
+ return;
+ }
+
+ if (IsHeadless)
+ {
+ return;
+ }
+
+ // Start the Analytics service only when a session is not present.
+ // In an integrator host, as splash screen can be closed without shutting down the ViewModel, the analytics service is not stopped.
+ // So we don't want to start it when splash screen or dynamo window is launched again.
+ if (Analytics.client == null)
+ {
+ AnalyticsService.Start();
+ }
+ else if (Analytics.client is DynamoAnalyticsClient dac)
+ {
+ if (dac.Session == null)
+ {
+ AnalyticsService.Start();
+ }
+ }
+ }
+
private void SearchModel_ItemProduced(NodeModel node)
{
ExecuteCommand(new CreateNodeCommand(node, 0, 0, true, true));
@@ -1615,6 +1634,7 @@ private void InitializeNodeLibrary()
DumpLibrarySnapshot(functionGroups);
#endif
+
// Load local custom nodes and locally imported libraries
foreach (var path in pathManager.DefinitionDirectories)
{
diff --git a/src/DynamoCore/Models/DynamoModelEvents.cs b/src/DynamoCore/Models/DynamoModelEvents.cs
index 7a76d0379c7..2860fd24ad4 100644
--- a/src/DynamoCore/Models/DynamoModelEvents.cs
+++ b/src/DynamoCore/Models/DynamoModelEvents.cs
@@ -8,6 +8,7 @@
using System.Collections.Generic;
using Dynamo.Graph;
using Dynamo.Extensions;
+using Dynamo.Logging;
namespace Dynamo.Models
{
@@ -158,6 +159,11 @@ protected virtual void OnWorkspaceAdded(WorkspaceModel obj)
var handler = WorkspaceAdded;
if (handler != null) handler(obj);
+ if (obj is CustomNodeWorkspaceModel)
+ Analytics.TrackScreenView("CustomWorkspace");
+ else
+ Analytics.TrackScreenView("Workspace");
+
WorkspaceEvents.OnWorkspaceAdded(obj.Guid, obj.Name, obj.GetType());
}
diff --git a/src/DynamoCore/Models/RecordableCommands.cs b/src/DynamoCore/Models/RecordableCommands.cs
index 557827372e8..44355e6cf15 100644
--- a/src/DynamoCore/Models/RecordableCommands.cs
+++ b/src/DynamoCore/Models/RecordableCommands.cs
@@ -528,7 +528,7 @@ protected override void SerializeCore(XmlElement element)
internal override void TrackAnalytics()
{
// Log file open action and the number of nodes in the opened workspace
- Dynamo.Logging.Analytics.TrackFileOperationEvent(
+ Dynamo.Logging.Analytics.TrackTaskFileOperationEvent(
FilePath,
Logging.Actions.Open,
dynamoModel.CurrentWorkspace.Nodes.Count());
@@ -628,7 +628,7 @@ protected override void SerializeCore(XmlElement element)
internal override void TrackAnalytics()
{
// Log file open action and the number of nodes in the opened workspace
- Dynamo.Logging.Analytics.TrackFileOperationEvent(
+ Dynamo.Logging.Analytics.TrackTaskFileOperationEvent(
FilePath,
Logging.Actions.Open,
dynamoModel.CurrentWorkspace.Nodes.Count());
@@ -705,7 +705,7 @@ protected override void SerializeCore(XmlElement element)
internal override void TrackAnalytics()
{
// Log file open action and the number of nodes in the opened workspace
- Dynamo.Logging.Analytics.TrackFileOperationEvent(
+ Dynamo.Logging.Analytics.TrackTaskFileOperationEvent(
"In memory json file",
Logging.Actions.Open,
dynamoModel.CurrentWorkspace.Nodes.Count());
@@ -1795,7 +1795,7 @@ protected override void SerializeCore(XmlElement element)
internal override void TrackAnalytics()
{
- Dynamo.Logging.Analytics.TrackCommandEvent(
+ Dynamo.Logging.Analytics.TrackTaskCommandEvent(
CmdOperation.ToString()); // "Undo" or "Redo"
}
diff --git a/src/DynamoCoreWpf/Utilities/CrashReportTool.cs b/src/DynamoCoreWpf/Utilities/CrashReportTool.cs
index 67249bb0f70..40f742d1a25 100644
--- a/src/DynamoCoreWpf/Utilities/CrashReportTool.cs
+++ b/src/DynamoCoreWpf/Utilities/CrashReportTool.cs
@@ -228,9 +228,9 @@ internal static bool ShowCrashErrorReportWindow(DynamoViewModel viewModel, Crash
string appConfig = "";
if (model != null)
{
- var appName = GetHostAppName(model);
- appConfig = $"";
}
@@ -271,18 +271,14 @@ internal static bool ShowCrashErrorReportWindow(DynamoViewModel viewModel, Crash
return false;
}
- internal static string GetHostAppName(DynamoModel model)
+ internal static string GetHostAppName()
{
//default to app name being process name, but prefer HostAnalyticsInfo.HostName
//then legacy Model.HostName
var appName = Process.GetCurrentProcess().ProcessName;
- if (!string.IsNullOrEmpty(model.HostAnalyticsInfo.HostName))
+ if (!string.IsNullOrEmpty(DynamoModel.HostAnalyticsInfo.HostName))
{
- appName = model.HostAnalyticsInfo.HostName;
- }
- else if (!string.IsNullOrEmpty(model.HostName))
- {
- appName = model.HostName;
+ appName = DynamoModel.HostAnalyticsInfo.HostName;
}
return appName;
diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
index 5985e801a57..d1f5ff0b286 100644
--- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
+++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
@@ -479,7 +479,7 @@ public ObservableCollection RecentFiles
public string Version
{
- get { return model.Version; }
+ get { return DynamoModel.Version; }
}
public string HostVersion
@@ -2817,7 +2817,7 @@ public void SaveImage(object parameters)
{
OnRequestSaveImage(this, new ImageSaveEventArgs(parameters.ToString()));
- Dynamo.Logging.Analytics.TrackCommandEvent("ImageCapture",
+ Dynamo.Logging.Analytics.TrackTaskCommandEvent("ImageCapture",
"NodeCount", CurrentSpace.Nodes.Count());
}
@@ -3292,7 +3292,7 @@ private void ExportToSTL(object parameter)
{
BackgroundPreviewViewModel.ExportToSTL(_fileDialog.FileName, HomeSpace.Name);
- Dynamo.Logging.Analytics.TrackCommandEvent("ExportToSTL");
+ Dynamo.Logging.Analytics.TrackTaskCommandEvent("ExportToSTL");
}
}
diff --git a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs
index aa64e8920fe..38e8fb18017 100644
--- a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs
+++ b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs
@@ -1681,7 +1681,7 @@ private void DoGraphAutoLayout(object o)
Model.DoGraphAutoLayout();
DynamoViewModel.RaiseCanExecuteUndoRedo();
- Dynamo.Logging.Analytics.TrackCommandEvent("GraphLayout");
+ Dynamo.Logging.Analytics.TrackTaskCommandEvent("GraphLayout");
}
private static bool CanDoGraphAutoLayout(object o)
@@ -1753,7 +1753,7 @@ internal void CollapseSelectedNodes()
DynamoViewModel.Model.CustomNodeManager.Collapse(selectedNodes,
selectedNotes, Model, DynamoModel.IsTestMode, args));
- Dynamo.Logging.Analytics.TrackCommandEvent("NewCustomNode",
+ Dynamo.Logging.Analytics.TrackTaskCommandEvent("NewCustomNode",
"NodeCount", selectedNodes.Count());
}
diff --git a/src/DynamoCoreWpf/ViewModels/Menu/PreferencesViewModel.cs b/src/DynamoCoreWpf/ViewModels/Menu/PreferencesViewModel.cs
index 4c3b2a1fa65..e7bbbf1395d 100644
--- a/src/DynamoCoreWpf/ViewModels/Menu/PreferencesViewModel.cs
+++ b/src/DynamoCoreWpf/ViewModels/Menu/PreferencesViewModel.cs
@@ -1050,7 +1050,7 @@ public bool IsDynamoRevit
{
// HostAnalyticsInfo is not set when this is invoked??
//return this.dynamoViewModel.Model.HostAnalyticsInfo.HostName.Equals("Dynamo Revit");
- var host = this.dynamoViewModel.Model.HostAnalyticsInfo.HostName;
+ var host = DynamoModel.HostAnalyticsInfo.HostName;
if (host != null)
{
diff --git a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs
index dea21f98741..7ac62388b67 100644
--- a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs
+++ b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs
@@ -894,7 +894,7 @@ internal async void ExecutePackageDownload(string name, PackageVersion package,
// Determine if there are any dependencies that are made with a newer version
// of Dynamo (this includes the root package)
- var dynamoVersion = VersionUtilities.PartialParse(DynamoViewModel.Model.Version);
+ var dynamoVersion = VersionUtilities.PartialParse(DynamoModel.Version);
var futureDeps = newPackageHeaders.Where(dep => VersionUtilities.PartialParse(dep.engine_version) > dynamoVersion);
// If any of the required packages use a newer version of Dynamo, show a dialog to the user
diff --git a/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs b/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs
index 512cd8693c9..1e00d55d0f7 100644
--- a/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs
+++ b/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs
@@ -213,7 +213,7 @@ internal MLNodeAutoCompletionRequest GenerateRequestForMLAutocomplete()
request.Port.ListAtLevel = portInfo.Level;
// Set host info
- var hostName = string.IsNullOrEmpty(dynamoViewModel.Model.HostAnalyticsInfo.HostName) ? dynamoViewModel.Model.HostName : dynamoViewModel.Model.HostAnalyticsInfo.HostName;
+ var hostName = string.IsNullOrEmpty(DynamoModel.HostAnalyticsInfo.HostName) ? dynamoViewModel.Model.HostName : DynamoModel.HostAnalyticsInfo.HostName;
var hostNameEnum = GetHostNameEnum(hostName);
if (hostNameEnum != HostNames.None)
diff --git a/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs b/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs
index 318cca4b224..153b1af1e9d 100644
--- a/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs
+++ b/src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs
@@ -1506,7 +1506,7 @@ private void DynamoViewModelRequestShowPackageManagerSearch(object s, EventArgs
if (!DisplayTermsOfUseForAcceptance())
return; // Terms of use not accepted.
- var cmd = Analytics.TrackCommandEvent("SearchPackage");
+ var cmd = Analytics.TrackTaskCommandEvent("SearchPackage");
// The package search view model is shared and can be shared by resources at the moment
// If it hasn't been initialized yet, we do that here
@@ -1527,7 +1527,7 @@ private void DynamoViewModelRequestShowPackageManagerSearch(object s, EventArgs
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
- _searchPkgsView.Closed += (sender, args) => { _searchPkgsView = null; cmd.Dispose(); };
+ _searchPkgsView.Closed += (sender, args) => { _searchPkgsView = null; Analytics.EndTaskCommandEvent(cmd); };
_searchPkgsView.Show();
if (_searchPkgsView.IsLoaded && IsLoaded) _searchPkgsView.Owner = this;
diff --git a/src/DynamoCoreWpf/Views/Menu/PreferencesView.xaml.cs b/src/DynamoCoreWpf/Views/Menu/PreferencesView.xaml.cs
index 9b3e0f85fbf..90e976e3d10 100644
--- a/src/DynamoCoreWpf/Views/Menu/PreferencesView.xaml.cs
+++ b/src/DynamoCoreWpf/Views/Menu/PreferencesView.xaml.cs
@@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
@@ -36,7 +37,7 @@ public partial class PreferencesView : Window
// Used for tracking the manage package command event
// This is not a command any more but we keep it
// around in a compatible way for now
- private IDisposable managePackageCommandEvent;
+ private Task managePackageCommandEvent;
//This list will be passed everytime that we create a new GroupStyle so the custom colors can remain
internal ObservableCollection stylesCustomColors;
@@ -367,7 +368,7 @@ private void InstalledPackagesExpander_OnExpanded(object sender, RoutedEventArgs
{
if (e.OriginalSource == e.Source)
{
- managePackageCommandEvent = Analytics.TrackCommandEvent("ManagePackage");
+ managePackageCommandEvent = Analytics.TrackTaskCommandEvent("ManagePackage");
}
}
@@ -375,7 +376,7 @@ private void InstalledPackagesExpander_OnCollapsed(object sender, RoutedEventArg
{
if (e.OriginalSource == e.Source)
{
- managePackageCommandEvent?.Dispose();
+ Analytics.EndTaskCommandEvent(managePackageCommandEvent);
}
}
diff --git a/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs b/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs
index 2a56843cc30..db0c28c3298 100644
--- a/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs
+++ b/src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs
@@ -83,7 +83,7 @@ public DynamoView DynamoView
// When view model is closed, we need to close the splash screen if it is displayed.
viewModel.RequestClose += SplashScreenRequestClose;
authManager = viewModel.Model.AuthenticationManager;
- hostAnalyticsInfo = viewModel.Model.HostAnalyticsInfo;
+ hostAnalyticsInfo = DynamoModel.HostAnalyticsInfo;
}
}
diff --git a/src/Libraries/PythonNodeModelsWpf/PythonNode.cs b/src/Libraries/PythonNodeModelsWpf/PythonNode.cs
index 1130a7f6974..f2e60676e61 100644
--- a/src/Libraries/PythonNodeModelsWpf/PythonNode.cs
+++ b/src/Libraries/PythonNodeModelsWpf/PythonNode.cs
@@ -183,7 +183,7 @@ private void EditScriptContent(object sender, EventArgs e)
{
try
{
- using (var cmd = Dynamo.Logging.Analytics.TrackCommandEvent("PythonEdit"))
+ using (var cmd = Dynamo.Logging.Analytics.TrackTaskCommandEvent("PythonEdit"))
{
if (editWindow != null)
{
diff --git a/src/NodeServices/Analytics.cs b/src/NodeServices/Analytics.cs
index 2031ec5c7cc..052b1bd21b7 100644
--- a/src/NodeServices/Analytics.cs
+++ b/src/NodeServices/Analytics.cs
@@ -1,4 +1,6 @@
using System;
+using System.Threading;
+using System.Threading.Tasks;
namespace Dynamo.Logging
{
@@ -38,9 +40,6 @@ internal static void Start(IAnalyticsClient client)
internal static void ShutDown()
{
if (client != null) client.ShutDown();
-
- IDisposable disposable = client as IDisposable;
- if (disposable != null) disposable.Dispose();
client = null;
}
@@ -153,6 +152,25 @@ public static IDisposable CreateTimedEvent(Categories category, string variable,
return client.CreateTimedEvent(category, variable, description, value);
}
+ ///
+ /// Creates a new task timed event with start state and tracks its start.
+ /// After the task is completed, disposing the returned event will record the event completion.
+ ///
+ /// Event category
+ /// Timed varaible name
+ /// Event description
+ /// A metric value associated with the event
+ /// Task defined by an IDisposable event
+ public static Task CreateTaskTimedEvent(Categories category, string variable, string description = "", int? value = null)
+ {
+ if (client == null)
+ {
+ return Task.FromResult(new Dummy() as IDisposable);
+ }
+
+ return client.CreateTaskTimedEvent(category, variable, description, value);
+ }
+
///
/// Creates a new command event of the given name. Start of the
/// command is tracked. When the event is disposed, it's completion is tracked.
@@ -160,7 +178,7 @@ public static IDisposable CreateTimedEvent(Categories category, string variable,
/// Command name
/// Event description
/// A metric value associated with the event
- /// Event as IDisposable
+ /// Task defined by an IDisposable event
public static IDisposable TrackCommandEvent(string name, string description = "", int? value = null)
{
if (client == null) return new Dummy();
@@ -168,6 +186,29 @@ public static IDisposable TrackCommandEvent(string name, string description = ""
return client.CreateCommandEvent(name, description, value);
}
+ ///
+ /// Creates a new command event task of the given name. Start of the
+ /// command is tracked. When the task is completed and the event is disposed, it's completion is tracked.
+ ///
+ /// Command name
+ /// Event description
+ /// A metric value associated with the event
+ /// Task defined by an IDisposable event
+ public static Task TrackTaskCommandEvent(string name, string description = "", int? value = null)
+ {
+ if (client == null)
+ {
+ return Task.FromResult(new Dummy() as IDisposable);
+ }
+
+ return client.CreateTaskCommandEvent(name, description, value);
+ }
+
+ public static void EndTaskCommandEvent(Task taskEvent)
+ {
+ client?.EndEventTask(taskEvent);
+ }
+
///
/// Creates a new file operation event and tracks the start of the event.
/// Disposing the returned event will record its completion.
@@ -184,6 +225,25 @@ public static IDisposable TrackFileOperationEvent(string filepath, Actions opera
return client.TrackFileOperationEvent(filepath, operation, size, description);
}
+ ///
+ /// Creates a new task file operation event and tracks the start of the event.
+ /// After the task is completed, disposing the returned event will record its completion.
+ ///
+ /// File path
+ /// File operation
+ /// Size parameter
+ /// Event description
+ /// Task defined by an IDisposable event
+ public static Task TrackTaskFileOperationEvent(string filepath, Actions operation, int size, string description = "")
+ {
+ if (client == null)
+ {
+ return Task.FromResult(new Dummy() as IDisposable);
+ }
+
+ return client.TrackTaskFileOperationEvent(filepath, operation, size, description);
+ }
+
///
/// Logs usage data
///
diff --git a/src/NodeServices/IAnalyticsClient.cs b/src/NodeServices/IAnalyticsClient.cs
index ff9591f40df..b0fbeb0d529 100644
--- a/src/NodeServices/IAnalyticsClient.cs
+++ b/src/NodeServices/IAnalyticsClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
namespace Dynamo.Logging
{
@@ -516,6 +517,17 @@ public interface IAnalyticsClient
/// Event as IDisposable
IDisposable CreateTimedEvent(Categories category, string variable, string description, int? value);
+ ///
+ /// Creates a new task timed event with start state and tracks its start.
+ /// After task is compoleted, disposing the returnd event will record the event completion.
+ ///
+ /// Event category
+ /// Timed varaible name
+ /// Event description
+ /// A metric value associated with the event
+ /// Event as IDisposable
+ Task CreateTaskTimedEvent(Categories category, string variable, string description, int? value);
+
///
/// Creates a new command event of the given name. Start of the
/// command is tracked. When the event is disposed, it's completion is tracked.
@@ -526,6 +538,22 @@ public interface IAnalyticsClient
/// Event as IDisposable
IDisposable CreateCommandEvent(string name, string description, int? value);
+ ///
+ /// Creates a new task command event of the given name. Start of the
+ /// command is tracked. When the task is completed and the event is disposed, it's completion is tracked.
+ ///
+ /// Command name
+ /// Event description
+ /// A metric value associated with the event
+ /// Event as IDisposable
+ Task CreateTaskCommandEvent(string name, string description, int? value);
+
+ ///
+ /// Waits for the given task to end so that it can dispose the event and
+ /// complete the tracking.
+ ///
+ void EndEventTask(Task taskToEnd);
+
///
/// Creates a new file operation event and tracks the start of the event.
/// Disposing the returned event will record its completion.
@@ -537,6 +565,17 @@ public interface IAnalyticsClient
/// Event as IDisposable
IDisposable TrackFileOperationEvent(string filepath, Actions operation, int size, string description);
+ ///
+ /// Creates a new file operation task event and tracks the start of the event.
+ /// After the task is completed, disposing the returned event will record its completion.
+ ///
+ /// File path
+ /// File operation
+ /// Size parameter
+ /// Event description
+ /// Event as IDisposable
+ Task TrackTaskFileOperationEvent(string filepath, Actions operation, int size, string description);
+
///
/// Logs usage data
///
diff --git a/test/DynamoCoreTests/AnalyticsTests.cs b/test/DynamoCoreTests/AnalyticsTests.cs
index c733b200ad7..4c19c6282e6 100644
--- a/test/DynamoCoreTests/AnalyticsTests.cs
+++ b/test/DynamoCoreTests/AnalyticsTests.cs
@@ -133,7 +133,7 @@ public DynamoAnalyticsTests()
protected override Mock MockClient()
{
- var client = new Mock(CurrentDynamoModel) { CallBase = true };
+ var client = new Mock(DynamoModel.HostAnalyticsInfo) { CallBase = true };
var session = MockAnalyticsSession();
client.Setup(c => c.Session).Returns(session);
return client.As();
@@ -144,7 +144,7 @@ private IAnalyticsSession MockAnalyticsSession()
var session = new Mock();
session.Setup(s => s.UserId).Returns("DynamoTestUser");
session.Setup(s => s.SessionId).Returns("UniqueSession");
- session.Setup(s => s.Start(It.IsAny())).Callback(SetupServices);
+ session.Setup(s => s.Start()).Callback(SetupServices);
return session.Object;
}
diff --git a/test/DynamoCoreWpfTests/CrashReportingTests.cs b/test/DynamoCoreWpfTests/CrashReportingTests.cs
index ec061cd4db9..44578c0296e 100644
--- a/test/DynamoCoreWpfTests/CrashReportingTests.cs
+++ b/test/DynamoCoreWpfTests/CrashReportingTests.cs
@@ -196,17 +196,13 @@ public void TestCERTool()
public void TestAppNameSentToCER()
{
CurrentDynamoModel.HostName = null;
- var name = CrashReportTool.GetHostAppName(CurrentDynamoModel);
+ var name = CrashReportTool.GetHostAppName();
//if both hostname and hostinfo.hostname are null, then use proc name.
Assert.True(name.Contains("testhost") || name.Contains("nunit-agent"));
- CurrentDynamoModel.HostName = "dynamotestmock";
- name = CrashReportTool.GetHostAppName(CurrentDynamoModel);
- //use hostname over proc name
- Assert.AreEqual(CurrentDynamoModel.HostName, name);
- CurrentDynamoModel.HostAnalyticsInfo = new HostAnalyticsInfo(){HostName = "123"};
- name = CrashReportTool.GetHostAppName(CurrentDynamoModel);
+ DynamoModel.HostAnalyticsInfo = new HostAnalyticsInfo(){HostName = "123"};
+ name = CrashReportTool.GetHostAppName();
//prefer hostinfo.hostname over others.
- Assert.AreEqual(CurrentDynamoModel.HostAnalyticsInfo.HostName, name);
+ Assert.AreEqual(DynamoModel.HostAnalyticsInfo.HostName, name);
}
}
}
diff --git a/test/Libraries/CommandLineTests/CommandLineTests.cs b/test/Libraries/CommandLineTests/CommandLineTests.cs
index 7c3a153659c..e2e16ae5bc3 100644
--- a/test/Libraries/CommandLineTests/CommandLineTests.cs
+++ b/test/Libraries/CommandLineTests/CommandLineTests.cs
@@ -95,7 +95,7 @@ public void CanSetHostNameWithCommandLineRunner()
string commandstring = $"-o {openpath} --HostName {hostName}";
runner.Run(CommandstringToArgs(commandstring));
- Assert.AreEqual(this.CurrentDynamoModel.HostAnalyticsInfo.HostName, "DynamoFormIt");
+ Assert.AreEqual(Dynamo.Models.DynamoModel.HostAnalyticsInfo.HostName, "DynamoFormIt");
}
[Test]
@@ -107,7 +107,7 @@ public void CanSetParentIdWithCommandLineRunner()
string commandstring = $"-o {openpath} -p {parentId}";
runner.Run(CommandstringToArgs(commandstring));
- Assert.AreEqual(this.CurrentDynamoModel.HostAnalyticsInfo.ParentId, "RVT&2022&MUI64&22.0.2.392");
+ Assert.AreEqual(Dynamo.Models.DynamoModel.HostAnalyticsInfo.ParentId, "RVT&2022&MUI64&22.0.2.392");
}
[Test]
@@ -119,7 +119,7 @@ public void CanSetSessionIdWithCommandLineRunner()
string commandstring = $"-o {openpath} -s {sessionId}";
runner.Run(CommandstringToArgs(commandstring));
- Assert.AreEqual(this.CurrentDynamoModel.HostAnalyticsInfo.SessionId, "ABCDEFG");
+ Assert.AreEqual(Dynamo.Models.DynamoModel.HostAnalyticsInfo.SessionId, "ABCDEFG");
}
[Test]