diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs index eb2d0ddda7..41bab7b772 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs @@ -111,6 +111,9 @@ string id var conversionProgressDict = new ConcurrentDictionary(); conversionProgressDict["Conversion"] = 0; + // track object types for mixpanel logging + Dictionary typeCountDict = new(); + // create a commit prefix: used for layers and block definition names var commitPrefix = Formatting.CommitInfo(stream.name, state.BranchName, id); @@ -217,18 +220,22 @@ string id return; } + if (StoredObjects.TryGetValue(commitObj.OriginalId, out Base commitBaseObj)) + { + // log received object type + typeCountDict.TryGetValue(commitBaseObj.speckle_type, out var currentCount); + typeCountDict[commitBaseObj.speckle_type] = ++currentCount; + } + else + { + commitObj.Update(status: ApplicationObject.State.Failed, logItem: "Object not found in StoredObjects"); + } + // convert base (or base fallback values) and store in appobj converted prop if (commitObj.Convertible) { - if (StoredObjects.TryGetValue(commitObj.OriginalId, out Base obj)) - { - converter.Report.Log(commitObj); // Log object so converter can access - commitObj.Converted = ConvertObject(obj, converter); - } - else - { - commitObj.Update(status: ApplicationObject.State.Failed, logItem: "Object not found in StoredObjects"); - } + converter.Report.Log(commitObj); // Log object so converter can access + commitObj.Converted = ConvertObject(commitBaseObj, converter); } else { @@ -271,6 +278,20 @@ string id } progress.Report.Merge(converter.Report); + // track the object type counts as an event before we try to receive + // this will tell us the composition of a commit the user is trying to receive, even if it's not successfully received + // we are capped at 255 properties for mixpanel events, so we need to check dict entries + var typeCountList = typeCountDict + .Select(o => new { TypeName = o.Key, Count = o.Value }) + .OrderBy(pair => pair.Count) + .Reverse() + .Take(200); + + Analytics.TrackEvent( + Analytics.Events.ConvertToNative, + new Dictionary() { { "typeCount", typeCountList } } + ); + // add applicationID xdata before bake if (!ApplicationIdManager.AddApplicationIdXDataToDoc(Doc, tr)) { diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs index 231f8542bf..2c65993377 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs @@ -179,6 +179,9 @@ ref int convertedCount var commitLayerObjects = new Dictionary>(); var commitCollections = new Dictionary(); + // track object types for mixpanel logging + Dictionary typeCountDict = new(); + foreach (var autocadObjectHandle in state.SelectedObjectIds) { // handle user cancellation @@ -201,6 +204,11 @@ ref int convertedCount continue; } + // log selection object type + var objectType = obj.GetType().ToString(); + typeCountDict.TryGetValue(objectType, out var currentCount); + typeCountDict[objectType] = ++currentCount; + // create applicationobject for reporting Base converted = null; var descriptor = ObjectDescriptor(obj); @@ -342,6 +350,20 @@ bool isOldApplicationId(string appId) #endregion + // track the object type counts as an event before we try to send + // this will tell us the composition of a commit the user is trying to convert and send, even if it's not successfully converted or sent + // we are capped at 255 properties for mixpanel events, so we need to check dict entries + var typeCountList = typeCountDict + .Select(o => new { TypeName = o.Key, Count = o.Value }) + .OrderBy(pair => pair.Count) + .Reverse() + .Take(200); + + Analytics.TrackEvent( + Analytics.Events.ConvertToSpeckle, + new Dictionary() { { "typeCount", typeCountList } } + ); + tr.Commit(); } } diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs index 5d7cf33b84..3d2d4a7bf0 100644 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Receive.cs @@ -53,6 +53,9 @@ public override async Task ReceiveStream(StreamState state, Progres converter.SetConverterSettings(settings); + // track object types for mixpanel logging + Dictionary typeCountDict = new(); + Commit myCommit = await ConnectorHelpers.GetCommitFromState(state, progress.CancellationToken); state.LastCommit = myCommit; Base commitObject = await ConnectorHelpers.ReceiveCommit(myCommit, state, progress); @@ -70,8 +73,27 @@ await ConnectorHelpers.TryCommitReceived( foreach (var previewObj in Preview) { progress.Report.Log(previewObj); + if (StoredObjects.TryGetValue(previewObj.OriginalId, out Base previewBaseObj)) + { + typeCountDict.TryGetValue(previewBaseObj.speckle_type, out var currentCount); + typeCountDict[previewBaseObj.speckle_type] = ++currentCount; + } } + // track the object type counts as an event before we try to receive + // this will tell us the composition of a commit the user is trying to convert and receive, even if it's not successfully converted or received + // we are capped at 255 properties for mixpanel events, so we need to check dict entries + var typeCountList = typeCountDict + .Select(o => new { TypeName = o.Key, Count = o.Value }) + .OrderBy(pair => pair.Count) + .Reverse() + .Take(200); + + Analytics.TrackEvent( + Analytics.Events.ConvertToNative, + new Dictionary() { { "typeCount", typeCountList } } + ); + converter.ReceiveMode = state.ReceiveMode; // needs to be set for editing to work var previousObjects = new StreamStateCache(state); diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs index 5611ff5697..75f83e9cc1 100644 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit.Send.cs @@ -99,6 +99,9 @@ public override async Task SendStream(StreamState state, ProgressViewMod var conversionProgressDict = new ConcurrentDictionary { ["Conversion"] = 0 }; var convertedCount = 0; + // track object types for mixpanel logging + Dictionary typeCountDict = new(); + await APIContext .Run(() => { @@ -114,6 +117,11 @@ await APIContext break; } + // log selection object type + var revitObjectType = revitElement.GetType().ToString(); + typeCountDict.TryGetValue(revitObjectType, out var currentCount); + typeCountDict[revitObjectType] = ++currentCount; + bool isAlreadyConverted = GetOrCreateApplicationObject( revitElement, converter.Report, @@ -136,10 +144,12 @@ out ApplicationObject reportObj Base result = ConvertToSpeckle(revitElement, converter); + // log converted object reportObj.Update( status: ApplicationObject.State.Created, logItem: $"Sent as {ConnectorRevitUtils.SimplifySpeckleType(result.speckle_type)}" ); + if (result.applicationId != reportObj.applicationId) { SpeckleLog.Logger.Information( @@ -180,6 +190,20 @@ out ApplicationObject reportObj throw new SpeckleException("Zero objects converted successfully. Send stopped."); } + // track the object type counts as an event before we try to send + // this will tell us the composition of a commit the user is trying to convert and send, even if it's not successfully converted or sent + // we are capped at 255 properties for mixpanel events, so we need to check dict entries + var typeCountList = typeCountDict + .Select(o => new { TypeName = o.Key, Count = o.Value }) + .OrderBy(pair => pair.Count) + .Reverse() + .Take(200); + + Analytics.TrackEvent( + Analytics.Events.ConvertToSpeckle, + new Dictionary() { { "typeCount", typeCountList } } + ); + commitObjectBuilder.BuildCommitObject(commitObject); var transports = new List() { new ServerTransport(client.Account, streamId) }; diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs index ee72860080..e85475969d 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs @@ -55,6 +55,9 @@ public override async Task ReceiveStream(StreamState state, Progres var conversionProgressDict = new ConcurrentDictionary(); conversionProgressDict["Conversion"] = 0; + // track object types for mixpanel logging + Dictionary typeCountDict = new(); + Base commitObject = null; if (Preview.Count == 0) { @@ -161,6 +164,7 @@ await ConnectorHelpers.TryCommitReceived( .Where(o => !string.IsNullOrEmpty(o)) .OrderBy(path => path.Count(c => c == ':')) .ToList(); + // if on create mode, make sure the parent commit layer is created first if (state.ReceiveMode == ReceiveMode.Create) { @@ -185,6 +189,7 @@ await ConnectorHelpers.TryCommitReceived( var collection = Preview .Where(o => o.Container == container && o.Descriptor.Contains("Collection")) .FirstOrDefault(); + if (collection != null) { var storedCollection = StoredObjects[collection.OriginalId]; @@ -217,6 +222,13 @@ await ConnectorHelpers.TryCommitReceived( foreach (var previewObj in Preview) { + // log received object type + if (StoredObjects.TryGetValue(previewObj.OriginalId, out Base previewBaseObj)) + { + typeCountDict.TryGetValue(previewBaseObj.speckle_type, out var currentCount); + typeCountDict[previewBaseObj.speckle_type] = ++currentCount; + } + if (previewObj.Status != ApplicationObject.State.Unknown) { continue; // this has already been converted and baked @@ -303,6 +315,20 @@ await ConnectorHelpers.TryCommitReceived( RhinoDoc.LayerTableEvent += RhinoDoc_LayerChange; // reactivate the layer handler + // track the object type counts as an event before we try to receive + // this will tell us the composition of a commit the user is trying to convert and receive, even if it's not successfully converted or received + // we are capped at 255 properties for mixpanel events, so we need to check dict entries + var typeCountList = typeCountDict + .Select(o => new { TypeName = o.Key, Count = o.Value }) + .OrderBy(pair => pair.Count) + .Reverse() + .Take(200); + + Speckle.Core.Logging.Analytics.TrackEvent( + Speckle.Core.Logging.Analytics.Events.ConvertToNative, + new Dictionary() { { "typeCount", typeCountList } } + ); + // undo notes edit var segments = Doc.Notes.Split(new[] { "%%%" }, StringSplitOptions.None).ToList(); Doc.Notes = segments[0]; diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs index 9513836067..991119c979 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Send.cs @@ -53,6 +53,9 @@ public override async Task SendStream(StreamState state, ProgressViewMod var commitLayers = new Dictionary(); var commitCollections = new Dictionary(); + // track object types for mixpanel logging + Dictionary typeCountDict = new(); + // convert all commit objs foreach (var selectedId in state.SelectedObjectIds) { @@ -63,6 +66,11 @@ public override async Task SendStream(StreamState state, ProgressViewMod var reportObj = new ApplicationObject(selectedId, "Unknown"); if (Utils.FindObjectBySelectedId(Doc, selectedId, out object obj, out string descriptor)) { + // log selection object type + var objectType = obj.GetType().ToString(); + typeCountDict.TryGetValue(objectType, out var currentCount); + typeCountDict[objectType] = ++currentCount; + // create applicationObject reportObj = new ApplicationObject(selectedId, descriptor); converter.Report.Log(reportObj); // Log object so converter can access @@ -228,10 +236,25 @@ void AddParent(Layer childLayer) throw new SpeckleException("Zero objects converted successfully. Send stopped."); } + // track the object type counts as an event before we try to send + // this will tell us the composition of a commit the user is trying to convert and send, even if it's not successfully converted or sent + // we are capped at 255 properties for mixpanel events, so we need to check dict entries + var typeCountList = typeCountDict + .Select(o => new { TypeName = o.Key, Count = o.Value }) + .OrderBy(pair => pair.Count) + .Reverse() + .Take(200); + + Speckle.Core.Logging.Analytics.TrackEvent( + Speckle.Core.Logging.Analytics.Events.ConvertToSpeckle, + new Dictionary() { { "typeCount", typeCountList } } + ); + progress.CancellationToken.ThrowIfCancellationRequested(); progress.Max = objCount; + // send the commit var transports = new List { new ServerTransport(client.Account, streamId) }; var objectId = await Operations.Send( @@ -260,6 +283,7 @@ void AddParent(Layer childLayer) } var commitId = await ConnectorHelpers.CreateCommit(client, actualCommit, progress.CancellationToken); + return commitId; } } diff --git a/Core/Core/Logging/Analytics.cs b/Core/Core/Logging/Analytics.cs index c96fa5b28b..66f77895cf 100644 --- a/Core/Core/Logging/Analytics.cs +++ b/Core/Core/Logging/Analytics.cs @@ -67,7 +67,17 @@ public enum Events /// /// Event triggered by the Mapping Tool /// - MappingsAction + MappingsAction, + + /// + /// Event triggered when user selects object to convert to Speckle on Send + /// + ConvertToSpeckle, + + /// + /// Event triggered when user selects object to convert to Native on Receive + /// + ConvertToNative } private const string MIXPANEL_TOKEN = "acd87c5a50b56df91a795e999812a3a4"; diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs index 1702695a7a..057144695d 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs @@ -143,7 +143,6 @@ public Base ConvertToSpeckle(object @object) break; case AcadDB.Polyline o: @base = o.IsOnlyLines ? PolylineToSpeckle(o) : (Base)PolycurveToSpeckle(o); - break; case AcadDB.Polyline3d o: @base = PolylineToSpeckle(o);