Browse Source

UDC: include date and environment block on first upload

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5620 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Daniel Grunwald 16 years ago
parent
commit
e3f935c15e
  1. 52
      src/AddIns/Misc/UsageDataCollector/UsageDataCollector.AddIn/AnalyticsMonitor.cs
  2. 53
      src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataSessionWriter.cs
  3. 43
      src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataUploader.cs

52
src/AddIns/Misc/UsageDataCollector/UsageDataCollector.AddIn/AnalyticsMonitor.cs

@ -6,14 +6,16 @@ @@ -6,14 +6,16 @@
// </file>
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using ICSharpCode.Core;
using ICSharpCode.Core.Services;
using ICSharpCode.SharpDevelop;
using ICSharpCode.UsageDataCollector.Contracts;
namespace ICSharpCode.UsageDataCollector
{
@ -78,29 +80,12 @@ namespace ICSharpCode.UsageDataCollector @@ -78,29 +80,12 @@ namespace ICSharpCode.UsageDataCollector
/// </summary>
public void OpenSession()
{
IEnumerable<UsageDataEnvironmentProperty> appEnvironmentProperties = GetAppProperties();
bool sessionOpened = false;
lock (lockObj) {
if (session == null) {
try {
session = new UsageDataSessionWriter(dbFileName);
session.OnException = MessageService.ShowException;
session.AddEnvironmentData("appVersion", RevisionClass.FullVersion);
session.AddEnvironmentData("language", ResourceService.Language);
session.AddEnvironmentData("culture", CultureInfo.CurrentCulture.Name);
string PROCESSOR_ARCHITECTURE = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432");
if (string.IsNullOrEmpty(PROCESSOR_ARCHITECTURE)) {
PROCESSOR_ARCHITECTURE = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
}
if (!string.IsNullOrEmpty(PROCESSOR_ARCHITECTURE)) {
session.AddEnvironmentData("architecture", PROCESSOR_ARCHITECTURE);
}
session.AddEnvironmentData("userAddInCount", AddInTree.AddIns.Where(a => !a.IsPreinstalled).Count().ToString());
#if DEBUG
session.AddEnvironmentData("debug", "true");
#endif
sessionOpened = true;
} catch (IncompatibleDatabaseException ex) {
if (ex.ActualVersion < ex.ExpectedVersion) {
LoggingService.Info("AnalyticsMonitor: " + ex.Message + ", removing old database");
@ -115,14 +100,43 @@ namespace ICSharpCode.UsageDataCollector @@ -115,14 +100,43 @@ namespace ICSharpCode.UsageDataCollector
LoggingService.Warn("AnalyticsMonitor: " + ex.Message);
}
}
if (session != null) {
session.OnException = MessageService.ShowException;
session.AddEnvironmentData(appEnvironmentProperties);
sessionOpened = true;
}
}
}
if (sessionOpened) {
UsageDataUploader uploader = new UsageDataUploader(dbFileName);
uploader.EnvironmentDataForDummySession = appEnvironmentProperties;
ThreadPool.QueueUserWorkItem(delegate { uploader.StartUpload(UploadUrl); });
}
}
static IEnumerable<UsageDataEnvironmentProperty> GetAppProperties()
{
List<UsageDataEnvironmentProperty> properties = new List<UsageDataEnvironmentProperty> {
new UsageDataEnvironmentProperty { Name = "appVersion", Value = RevisionClass.FullVersion },
new UsageDataEnvironmentProperty { Name = "language", Value = ResourceService.Language },
new UsageDataEnvironmentProperty { Name = "culture", Value = CultureInfo.CurrentCulture.Name },
new UsageDataEnvironmentProperty { Name = "userAddInCount", Value = AddInTree.AddIns.Where(a => !a.IsPreinstalled).Count().ToString() }
};
string PROCESSOR_ARCHITECTURE = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432");
if (string.IsNullOrEmpty(PROCESSOR_ARCHITECTURE)) {
PROCESSOR_ARCHITECTURE = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
}
if (!string.IsNullOrEmpty(PROCESSOR_ARCHITECTURE)) {
properties.Add(new UsageDataEnvironmentProperty { Name = "architecture", Value = PROCESSOR_ARCHITECTURE });
}
#if DEBUG
properties.Add(new UsageDataEnvironmentProperty { Name = "debug", Value = "true" });
#endif
return properties;
}
/// <summary>
/// Retrieves all stored data as XML text.
/// </summary>

53
src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataSessionWriter.cs

@ -11,13 +11,17 @@ using System.Data.SQLite; @@ -11,13 +11,17 @@ using System.Data.SQLite;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using ICSharpCode.UsageDataCollector.Contracts;
namespace ICSharpCode.UsageDataCollector
{
/// <summary>
@ -25,7 +29,7 @@ namespace ICSharpCode.UsageDataCollector @@ -25,7 +29,7 @@ namespace ICSharpCode.UsageDataCollector
///
/// This class is thread-safe.
/// </summary>
public class UsageDataSessionWriter : IDisposable
public sealed class UsageDataSessionWriter : IDisposable
{
readonly object lockObj = new object();
SQLiteConnection connection;
@ -153,6 +157,19 @@ namespace ICSharpCode.UsageDataCollector @@ -153,6 +157,19 @@ namespace ICSharpCode.UsageDataCollector
}
}
/// <summary>
/// Gets the default environment data.
/// </summary>
public static UsageDataEnvironmentProperty[] GetDefaultEnvironmentData()
{
return new [] {
new UsageDataEnvironmentProperty { Name = "platform", Value = Environment.OSVersion.Platform.ToString() },
new UsageDataEnvironmentProperty { Name = "osVersion", Value = Environment.OSVersion.Version.ToString() },
new UsageDataEnvironmentProperty { Name = "processorCount", Value = Environment.ProcessorCount.ToString() },
new UsageDataEnvironmentProperty { Name = "dotnetRuntime", Value = Environment.Version.ToString() }
};
}
void StartSession()
{
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) {
@ -161,10 +178,7 @@ namespace ICSharpCode.UsageDataCollector @@ -161,10 +178,7 @@ namespace ICSharpCode.UsageDataCollector
"SELECT last_insert_rowid();";
sessionID = (long)cmd.ExecuteScalar();
}
AddEnvironmentData("platform", Environment.OSVersion.Platform.ToString());
AddEnvironmentData("osVersion", Environment.OSVersion.Version.ToString());
AddEnvironmentData("processorCount", Environment.ProcessorCount.ToString());
AddEnvironmentData("dotnetRuntime", Environment.Version.ToString());
AddEnvironmentData(GetDefaultEnvironmentData());
transaction.Commit();
}
}
@ -185,24 +199,28 @@ namespace ICSharpCode.UsageDataCollector @@ -185,24 +199,28 @@ namespace ICSharpCode.UsageDataCollector
/// <summary>
/// Adds environment data to the current session.
/// </summary>
/// <param name="name">Name of the data entry.</param>
/// <param name="value">Value of the data entry.</param>
public void AddEnvironmentData(string name, string value)
public void AddEnvironmentData(IEnumerable<UsageDataEnvironmentProperty> properties)
{
if (name == null)
throw new ArgumentNullException("name");
if (properties == null)
throw new ArgumentNullException("properties");
UsageDataEnvironmentProperty[] pArray = properties.ToArray();
lock (lockObj) {
if (isDisposed)
throw new ObjectDisposedException(GetType().Name);
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) {
foreach (UsageDataEnvironmentProperty p in pArray) {
using (SQLiteCommand cmd = this.connection.CreateCommand()) {
cmd.CommandText = "INSERT INTO Environment (session, name, value)" +
" VALUES (?, ?, ?);";
cmd.Parameters.Add(new SQLiteParameter { Value = sessionID });
cmd.Parameters.Add(new SQLiteParameter { Value = name });
cmd.Parameters.Add(new SQLiteParameter { Value = value });
cmd.Parameters.Add(new SQLiteParameter { Value = p.Name });
cmd.Parameters.Add(new SQLiteParameter { Value = p.Value });
cmd.ExecuteNonQuery();
}
}
transaction.Commit();
}
}
}
Queue<FeatureUse> delayedStart = new Queue<FeatureUse>();
@ -482,6 +500,16 @@ namespace ICSharpCode.UsageDataCollector @@ -482,6 +500,16 @@ namespace ICSharpCode.UsageDataCollector
/// </summary>
public IncompatibleDatabaseException() {}
/// <summary>
/// Creates a new IncompatibleDatabaseException instance.
/// </summary>
public IncompatibleDatabaseException(string message) : base(message) {}
/// <summary>
/// Creates a new IncompatibleDatabaseException instance.
/// </summary>
public IncompatibleDatabaseException(string message, Exception innerException) : base(message, innerException) {}
/// <summary>
/// Creates a new IncompatibleDatabaseException instance.
/// </summary>
@ -504,6 +532,7 @@ namespace ICSharpCode.UsageDataCollector @@ -504,6 +532,7 @@ namespace ICSharpCode.UsageDataCollector
}
/// <inheritdoc/>
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);

43
src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataUploader.cs

@ -37,6 +37,11 @@ namespace ICSharpCode.UsageDataCollector @@ -37,6 +37,11 @@ namespace ICSharpCode.UsageDataCollector
this.databaseFileName = databaseFileName;
}
/// <summary>
/// Gets/Sets environment data that is sent with the dummy session on the first upload.
/// </summary>
public IEnumerable<UsageDataEnvironmentProperty> EnvironmentDataForDummySession { get; set; }
SQLiteConnection OpenConnection()
{
SQLiteConnectionStringBuilder conn = new SQLiteConnectionStringBuilder();
@ -60,7 +65,7 @@ namespace ICSharpCode.UsageDataCollector @@ -60,7 +65,7 @@ namespace ICSharpCode.UsageDataCollector
message = FetchDataForUpload(connection, true);
}
}
using (StringWriter w = new StringWriter()) {
using (StringWriter w = new StringWriter(CultureInfo.InvariantCulture)) {
using (XmlTextWriter xmlWriter = new XmlTextWriter(w)) {
xmlWriter.Formatting = Formatting.Indented;
DataContractSerializer serializer = new DataContractSerializer(typeof(UsageDataMessage));
@ -93,13 +98,17 @@ namespace ICSharpCode.UsageDataCollector @@ -93,13 +98,17 @@ namespace ICSharpCode.UsageDataCollector
public void StartUpload(Binding binding, EndpointAddress endpoint)
{
UsageDataMessage message;
bool addDummySession = false;
using (SQLiteConnection connection = OpenConnection()) {
using (SQLiteTransaction transaction = connection.BeginTransaction()) {
CheckDatabaseVersion(connection);
if (HasAlreadyUploadedToday(connection)) {
HasUploadedTodayStatus status = HasAlreadyUploadedToday(connection);
if (status == HasUploadedTodayStatus.Yes) {
message = null;
} else {
message = FetchDataForUpload(connection, false);
if (status == HasUploadedTodayStatus.NeverUploaded)
addDummySession = true;
}
transaction.Commit();
}
@ -107,6 +116,19 @@ namespace ICSharpCode.UsageDataCollector @@ -107,6 +116,19 @@ namespace ICSharpCode.UsageDataCollector
if (message != null) {
string commaSeparatedSessionIDList = GetCommaSeparatedIDList(message.Sessions);
if (addDummySession) {
// A dummy session is used for the first upload to notify the server of the user's environment.
// Without this, we couldn't tell the version of a user who tries SharpDevelop once but doesn't use it long
// enough for an actual session to be uploaded.
UsageDataSession dummySession = new UsageDataSession();
dummySession.SessionID = 0;
dummySession.StartTime = DateTime.UtcNow;
dummySession.EnvironmentProperties.AddRange(UsageDataSessionWriter.GetDefaultEnvironmentData());
if (this.EnvironmentDataForDummySession != null)
dummySession.EnvironmentProperties.AddRange(this.EnvironmentDataForDummySession);
message.Sessions.Add(dummySession);
}
DataContractSerializer serializer = new DataContractSerializer(typeof(UsageDataMessage));
byte[] data;
using (MemoryStream ms = new MemoryStream()) {
@ -158,19 +180,26 @@ namespace ICSharpCode.UsageDataCollector @@ -158,19 +180,26 @@ namespace ICSharpCode.UsageDataCollector
}
}
bool HasAlreadyUploadedToday(SQLiteConnection connection)
enum HasUploadedTodayStatus
{
No,
Yes,
NeverUploaded
}
static HasUploadedTodayStatus HasAlreadyUploadedToday(SQLiteConnection connection)
{
using (SQLiteCommand cmd = connection.CreateCommand()) {
cmd.CommandText = "SELECT value > datetime('now','-1 day') FROM Properties WHERE name='lastUpload';";
object result = cmd.ExecuteScalar();
if (result == null)
return false; // no lastUpload entry -> DB was never uploaded
return (long)result > 0;
return HasUploadedTodayStatus.NeverUploaded; // no lastUpload entry -> DB was never uploaded
return (long)result > 0 ? HasUploadedTodayStatus.Yes : HasUploadedTodayStatus.No;
}
}
#region FetchDataForUpload
UsageDataMessage FetchDataForUpload(SQLiteConnection connection, bool fetchIncompleteSessions)
static UsageDataMessage FetchDataForUpload(SQLiteConnection connection, bool fetchIncompleteSessions)
{
UsageDataMessage message = new UsageDataMessage();
// Retrieve the User ID
@ -261,7 +290,7 @@ namespace ICSharpCode.UsageDataCollector @@ -261,7 +290,7 @@ namespace ICSharpCode.UsageDataCollector
}
#endregion
string GetCommaSeparatedIDList(IEnumerable<UsageDataSession> sessions)
static string GetCommaSeparatedIDList(IEnumerable<UsageDataSession> sessions)
{
return string.Join(
",",

Loading…
Cancel
Save