<commit_before>﻿using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Rackspace.Threading;

namespace GitHub.Unity
{
    class UsageTracker : IUsageTracker
    {
        private readonly ILogging logger = Logging.GetLogger<UsageTracker>();
        private readonly NPath storePath;
        private readonly string userTrackingId;

        private IMetricsService client;
        private bool firstRun = true;
        private Timer timer;

        public UsageTracker(NPath storePath, string userTrackingId)
        {
            this.userTrackingId = userTrackingId;
            logger.Trace("Tracking Id:{0}", userTrackingId);

            this.storePath = storePath;

            RunTimer();
        }

        private async Task Initialize()
        {
            // The services needed by the usage tracker are loaded when they are first needed to
            // improve the startup time of the extension.
            await ThreadingHelper.SwitchToMainThreadAsync();

#if HAS_METRICS_SERVICE
            if (client == null)
            {
                client = new MetricsService($"GitHub4Unity{AssemblyVersionInformation.Version}");
            }
#endif
        }

        private async Task<UsageStore> LoadUsage()
        {
            await Initialize();

            string json = null;
            if (storePath.FileExists())
            {
                logger.Trace("LoadUsage: \"{0}\"", storePath);

                try
                {
                    json = storePath.ReadAllText(Encoding.UTF8);
                }
                catch (Exception ex)
                {
                    logger.Error(ex, "LoadUsage Error {0}", ex.Message);
                }
            }

            UsageStore result;
            try
            {
                if (json != null)
                {
                    result = SimpleJson.DeserializeObject<UsageStore>(json);
                    if (result.Model == null)
                    {
                        logger.Warning("Model is Null");
                    }

                    if (result.Model.Reports == null)
                    {
                        logger.Warning("Reports is Null");
                    }
                }
                else
                {
                    result = new UsageStore();
                }
            }
            catch (Exception ex)
            {
                result = new UsageStore();

                logger.Warning(ex, "Error Loading Usage: {0}; Deleting File", storePath);

                try
                {
                    //storePath.DeleteIfExists();
                }
                catch{}
            }

            //TODO: Figure out these values
            //result.Model.Lang = CultureInfo.InstalledUICulture.IetfLanguageTag;
            //result.Model.AppVersion = AssemblyVersionInformation.Version;

            //TODO: Get Unity Version
            //result.Model.UnityVersion
            //result.Model.VSVersion = vsservices.VSVersion;

            return result;
        }

        private void SaveUsage(UsageStore store)
        {
            if (!Enabled)
            {
                return;
            }

            var pathString = storePath.ToString();
            logger.Trace("SaveUsage: \"{0}\"", pathString);

            try
            {
                var json = SimpleJson.SerializeObject(store);
                storePath.WriteAllText(json, Encoding.UTF8);
            }
            catch (Exception ex)
            {
                logger.Error(ex, "SaveUsage Error: \"{0}\"", pathString);
            }
        }

        private void RunTimer()
        {
            // The timer first ticks after 3 minutes to allow things to settle down after startup.
            // This will be changed to 8 hours after the first tick by the TimerTick method.
            timer = new Timer(TimeSpan.FromSeconds(30).TotalMilliseconds);
            //timer = new Timer(TimeSpan.FromMinutes(3).TotalMilliseconds);
            timer.Elapsed += TimerTick;
            timer.Start();
        }
            
        private void TimerTick(object sender, ElapsedEventArgs e)
        {
            TimerTick().Catch((Action<Task, Exception>)((task, exception) => {
                logger.Error(exception, "TimerTicker Error: {0}", exception.Message);
            })).Forget();
        }

        private async Task TimerTick()
        {
            logger.Trace("TimerTick");

            await Initialize();
            var usageStore = await LoadUsage();

            if (firstRun)
            {
                timer.Interval = TimeSpan.FromHours(8).TotalMilliseconds;
                firstRun = false;

                if (!Enabled)
                {
                    logger.Warning("Tracking Disabled");
                    return;
                }
            }

            if (client == null)
            {
                logger.Warning("MetricsClient is null; stopping timer");
                if (timer != null)
                {
                    timer.Enabled = false;
                    timer = null;
                }
                return;
            }

            if (!Enabled)
            {
                logger.Warning("Tracking Disabled");
                return;
            }

            if (usageStore.LastUpdated.Date != DateTimeOffset.UtcNow.Date)
            {
                await SendUsage(usageStore);
            }
        }

        private async Task SendUsage(UsageStore usage)
        {
            logger.Trace("Sending Usage");

            var currentTimeOffset = DateTimeOffset.UtcNow;
            var currentDate = currentTimeOffset.Date;

            var extractReports = usage.Model.SelectReports(currentDate);
            if (!extractReports.Any())
            {
                logger.Trace("No items to send");
            }
            else
            {
                var success = false;
                try
                {
                    await client.PostUsage(userTrackingId, extractReports);
                    success = true;
                }
                catch (Exception ex)
                {
                    logger.Warning(ex, "Error Sending Usage");
                }

                if (success)
                {
                    usage.Model.RemoveReports(currentDate);
                }
            }

            usage.LastUpdated = currentTimeOffset;
            SaveUsage(usage);
        }

        public async Task IncrementLaunchCount()
        {
            var usageStore = await LoadUsage();

            var usage = usageStore.Model.GetCurrentUsage();
            usage.NumberOfStartups++;

            logger.Trace("IncrementLaunchCount Date:{0} NumberOfStartups:{1}", usage.Date, usage.NumberOfStartups);

            SaveUsage(usageStore);
        }

        public bool Enabled { get; set; }
    }
}
<commit_msg>Final tweaks<commit_after>﻿using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Rackspace.Threading;

namespace GitHub.Unity
{
    class UsageTracker : IUsageTracker
    {
        private readonly ILogging logger = Logging.GetLogger<UsageTracker>();
        private readonly NPath storePath;
        private readonly string userTrackingId;

        private IMetricsService client;
        private bool firstRun = true;
        private Timer timer;

        public UsageTracker(NPath storePath, string userTrackingId)
        {
            this.userTrackingId = userTrackingId;
            logger.Trace("Tracking Id:{0}", userTrackingId);

            this.storePath = storePath;

            RunTimer();
        }

        private async Task Initialize()
        {
            // The services needed by the usage tracker are loaded when they are first needed to
            // improve the startup time of the extension.
            await ThreadingHelper.SwitchToMainThreadAsync();

#if HAS_METRICS_SERVICE
            if (client == null)
            {
                client = new MetricsService($"GitHub4Unity{AssemblyVersionInformation.Version}");
            }
#endif
        }

        private async Task<UsageStore> LoadUsage()
        {
            await Initialize();

            string json = null;
            if (storePath.FileExists())
            {
                logger.Trace("LoadUsage: \"{0}\"", storePath);

                try
                {
                    json = storePath.ReadAllText(Encoding.UTF8);
                }
                catch (Exception ex)
                {
                    logger.Error(ex, "LoadUsage Error {0}", ex.Message);
                }
            }

            UsageStore result;
            try
            {
                if (json != null)
                {
                    result = SimpleJson.DeserializeObject<UsageStore>(json);
                    if (result.Model == null)
                    {
                        logger.Warning("Model is Null");
                    }

                    if (result.Model.Reports == null)
                    {
                        logger.Warning("Reports is Null");
                    }
                }
                else
                {
                    result = new UsageStore();
                }
            }
            catch (Exception ex)
            {
                result = new UsageStore();

                logger.Warning(ex, "Error Loading Usage: {0}; Deleting File", storePath);

                try
                {
                    storePath.DeleteIfExists();
                }
                catch{}
            }

            //TODO: Figure out these values
            //result.Model.Lang = CultureInfo.InstalledUICulture.IetfLanguageTag;
            //result.Model.AppVersion = AssemblyVersionInformation.Version;

            //TODO: Get Unity Version
            //result.Model.UnityVersion
            //result.Model.VSVersion = vsservices.VSVersion;

            return result;
        }

        private void SaveUsage(UsageStore store)
        {
            if (!Enabled)
            {
                return;
            }

            var pathString = storePath.ToString();
            logger.Trace("SaveUsage: \"{0}\"", pathString);

            try
            {
                var json = SimpleJson.SerializeObject(store);
                storePath.WriteAllText(json, Encoding.UTF8);
            }
            catch (Exception ex)
            {
                logger.Error(ex, "SaveUsage Error: \"{0}\"", pathString);
            }
        }

        private void RunTimer()
        {
            // The timer first ticks after 3 minutes to allow things to settle down after startup.
            // This will be changed to 8 hours after the first tick by the TimerTick method.
            timer = new Timer(TimeSpan.FromMinutes(3).TotalMilliseconds);
            timer.Elapsed += TimerTick;
            timer.Start();
        }
            
        private void TimerTick(object sender, ElapsedEventArgs e)
        {
            TimerTick().Catch((Action<Task, Exception>)((task, exception) => {
                logger.Error(exception, "TimerTicker Error: {0}", exception.Message);
            })).Forget();
        }

        private async Task TimerTick()
        {
            logger.Trace("TimerTick");

            await Initialize();
            var usageStore = await LoadUsage();

            if (firstRun)
            {
                timer.Interval = TimeSpan.FromHours(8).TotalMilliseconds;
                firstRun = false;

                if (!Enabled)
                {
                    logger.Warning("Tracking Disabled");
                    return;
                }
            }

            if (client == null)
            {
                logger.Warning("MetricsClient is null; stopping timer");
                if (timer != null)
                {
                    timer.Enabled = false;
                    timer = null;
                }
                return;
            }

            if (!Enabled)
            {
                logger.Warning("Tracking Disabled");
                return;
            }

            if (usageStore.LastUpdated.Date != DateTimeOffset.UtcNow.Date)
            {
                await SendUsage(usageStore);
            }
        }

        private async Task SendUsage(UsageStore usage)
        {
            logger.Trace("Sending Usage");

            var currentTimeOffset = DateTimeOffset.UtcNow;
            var currentDate = currentTimeOffset.Date;

            var extractReports = usage.Model.SelectReports(currentDate);
            if (!extractReports.Any())
            {
                logger.Trace("No items to send");
            }
            else
            {
                var success = false;
                try
                {
                    await client.PostUsage(userTrackingId, extractReports);
                    success = true;
                }
                catch (Exception ex)
                {
                    logger.Warning(ex, "Error Sending Usage");
                }

                if (success)
                {
                    usage.Model.RemoveReports(currentDate);
                }
            }

            usage.LastUpdated = currentTimeOffset;
            SaveUsage(usage);
        }

        public async Task IncrementLaunchCount()
        {
            var usageStore = await LoadUsage();

            var usage = usageStore.Model.GetCurrentUsage();
            usage.NumberOfStartups++;

            logger.Trace("IncrementLaunchCount Date:{0} NumberOfStartups:{1}", usage.Date, usage.NumberOfStartups);

            SaveUsage(usageStore);
        }

        public bool Enabled { get; set; }
    }
}

{'fraction_non_alphanumeric': 0.08637681159420289, 'fraction_numerical': 0.002463768115942029, 'mean_word_length': 1.2202380952380953, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6256267', 'n_tokens_mistral': 3378, 'n_tokens_neox': 3133, 'n_words': 746}