示例#1
0
        private static void GetUploadDetails(
            IDatabaseCommandFactory databaseCommandFactory,
            int uploadId,
            out string uploadContent,
            out string userId,
            out PlayStyle playStyle)
        {
            const string CommandText       = @"
	            SELECT UploadContent, UserId, PlayStyle
	            FROM Uploads
	            WHERE Id = @UploadId"    ;
            var          commandParameters = new Dictionary <string, object>
            {
                { "@UploadId", uploadId },
            };

            using (var command = databaseCommandFactory.Create(
                       CommandText,
                       commandParameters))
                using (var reader = command.ExecuteReader())
                {
                    reader.Read();
                    uploadContent = reader["UploadContent"].ToString();
                    userId        = reader["UserId"].ToString();

                    if (!Enum.TryParse(reader["PlayStyle"].ToString(), out playStyle))
                    {
                        playStyle = default(PlayStyle);
                    }
                }
        }
 public ClanManager(
     IDatabaseCommandFactory databaseCommandFactory,
     HttpClient httpClient)
 {
     this.databaseCommandFactory = databaseCommandFactory;
     this.httpClient             = httpClient;
 }
示例#3
0
 internal DatabaseSession(IDatabaseCommandFactory databaseCommandFactory, ITransactionWrapper transactionWrapper, IDatabaseReaderFactory databaseReaderFactory, IConnectionHandler connectionHandler)
 {
     _databaseCommandFactory = databaseCommandFactory;
     _transactionWrapper = transactionWrapper;
     _databaseReaderFactory = databaseReaderFactory;
     _connectionHandler = connectionHandler;
 }
 public ClansApiController(
     IDatabaseCommandFactory databaseCommandFactory,
     UserManager <ApplicationUser> userManager)
 {
     this.databaseCommandFactory = databaseCommandFactory;
     this.userManager            = userManager;
 }
 public ClanManager(
     IDatabaseCommandFactory databaseCommandFactory,
     HttpClient httpClient)
 {
     _databaseCommandFactory = databaseCommandFactory;
     _httpClient             = httpClient;
 }
示例#6
0
            protected override void Arrange()
            {
                _options = new DeployDatabase
                {
                    Engine           = EngineType.PostgreSql,
                    DatabaseType     = DatabaseType.ODS,
                    ConnectionString = "Valid Connection String"
                };

                _databaseCommand = Stub <IDatabaseCommand>();

                A.CallTo(() => _databaseCommand.Execute(A <IOptions> ._))
                .Throws(new Exception());

                _databaseCommandFactory = Stub <IDatabaseCommandFactory>();

                A.CallTo(() => _databaseCommandFactory.CreateDatabaseCommands(A <EngineType> ._))
                .Returns(new List <IDatabaseCommand> {
                    _databaseCommand
                });

                _compositeSpecification = Stub <ICompositeSpecification>();

                A.CallTo(() => _compositeSpecification.IsSatisfiedBy(A <object> ._))
                .Returns(true);

                var compositeSpecifications = new List <ICompositeSpecification> {
                    _compositeSpecification
                };

                _result = -99;

                _sut = new ApplicationRunner(_options, _databaseCommandFactory, compositeSpecifications);
            }
示例#7
0
        private static async Task <(string uploadContent, string userId)> GetUploadDetailsAsync(
            IDatabaseCommandFactory databaseCommandFactory,
            int uploadId)
        {
            const string CommandText       = @"
	            SELECT UploadContent, UserId
	            FROM Uploads
	            WHERE Id = @UploadId"    ;
            var          commandParameters = new Dictionary <string, object>
            {
                { "@UploadId", uploadId },
            };

            using (var command = databaseCommandFactory.Create(
                       CommandText,
                       commandParameters))
                using (var reader = await command.ExecuteReaderAsync())
                {
                    reader.Read();

                    var uploadContent = reader["UploadContent"].ToString();
                    var userId        = reader["UserId"].ToString();

                    return(uploadContent, userId);
                }
        }
示例#8
0
        public UserSettings(
            IDatabaseCommandFactory databaseCommandFactory,
            string userId)
        {
            this.databaseCommandFactory = databaseCommandFactory;
            this.userId = userId;

            this.Fill();
        }
示例#9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CalculatorController"/> class.
 /// </summary>
 public CalculatorController(
     IDatabaseCommandFactory databaseCommandFactory,
     IUserSettingsProvider userSettingsProvider,
     UserManager <ApplicationUser> userManager)
 {
     this.databaseCommandFactory = databaseCommandFactory;
     this.userSettingsProvider   = userSettingsProvider;
     this.userManager            = userManager;
 }
示例#10
0
 /// <summary>
 /// Initializes a new instance of the <see cref="AdminController"/> class.
 /// </summary>
 /// <param name="databaseCommandFactory">The factory to create database commands.</param>
 /// <param name="uploadScheduler">The upload scheduler.</param>
 /// <param name="userManager">The user manager.</param>
 public AdminController(
     IDatabaseCommandFactory databaseCommandFactory,
     IUploadScheduler uploadScheduler,
     UserManager <ApplicationUser> userManager)
 {
     this.databaseCommandFactory = databaseCommandFactory;
     this.uploadScheduler        = uploadScheduler;
     this.userManager            = userManager;
 }
        /// <summary>
        /// Creates an <see cref="IDatabaseCommand"/> with the specified parameters.
        /// </summary>
        /// <param name="databaseCommandFactory">The command factory</param>
        /// <param name="commandText">The command text</param>
        /// <param name="parameters">The parameters to the command</param>
        /// <returns>An <see cref="IDatabaseCommand"/></returns>
        public static IDatabaseCommand Create(
            this IDatabaseCommandFactory databaseCommandFactory,
            string commandText,
            IDictionary <string, object> parameters)
        {
            var command = databaseCommandFactory.Create();

            command.CommandText = commandText;
            command.Parameters  = parameters;
            return(command);
        }
示例#12
0
        public ApplicationRunner(
            IOptions options,
            IDatabaseCommandFactory databaseCommandFactory,
            IEnumerable <ICompositeSpecification> specifications)
        {
            _options = Preconditions.ThrowIfNull(options, nameof(options));
            _databaseCommandFactory = Preconditions.ThrowIfNull(databaseCommandFactory, nameof(databaseCommandFactory));

            _specifications = Preconditions.ThrowIfNull(specifications, nameof(specifications))
                              .ToList();
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="DashboardController"/> class.
 /// </summary>
 public DashboardController(
     GameData gameData,
     TelemetryClient telemetryClient,
     IDatabaseCommandFactory databaseCommandFactory,
     IUserSettingsProvider userSettingsProvider,
     UserManager <ApplicationUser> userManager)
 {
     this.gameData               = gameData;
     this.telemetryClient        = telemetryClient;
     this.databaseCommandFactory = databaseCommandFactory;
     this.userSettingsProvider   = userSettingsProvider;
     this.userManager            = userManager;
 }
        /// <summary>
        /// Ensure the database schemas are created.
        /// </summary>
        /// <remarks>
        /// This is mostly for the in-memory database used in dev mode, but could be useful for new databases too.
        /// However, it's important to note that the tables created here may not be optimized. Indicies, foreign keys, etc may be missing.
        /// </remarks>
        /// <returns>Async task.</returns>
        private async Task EnsureDatabaseCreatedAsync(IServiceProvider serviceProvider)
        {
            // Handle the EntityFramework tables
            await serviceProvider.GetService <ApplicationDbContext>().Database.EnsureCreatedAsync();

            IDatabaseCommandFactory databaseCommandFactory = serviceProvider.GetService <IDatabaseCommandFactory>();

            // Get all existing tables so we know what already exists
            HashSet <string> existingTables = new(StringComparer.OrdinalIgnoreCase);

            using (IDatabaseCommand command = databaseCommandFactory.Create("SELECT Name FROM sys.Tables WHERE Type = N'U'"))
            {
                using (IDataReader reader = await command.ExecuteReaderAsync())
                {
                    while (reader.Read())
                    {
                        existingTables.Add(reader["Name"].ToString());
                    }
                }
            }

            // Read sql files and execute their contents in order if required.
            string[] tables = new[]
            {
                "Uploads",
                "AncientLevels",
                "ComputedStats",
                "OutsiderLevels",
                "UserFollows",
                "UserSettings",
                "Clans",
                "ClanMembers",
                "GameUsers",
            };
            IEnumerable <string> tableFiles = tables.Select(table => Path.Combine(_environment.ContentRootPath, "Services", "Database", "Schemas", table + ".sql"));

            foreach (string tableFile in tableFiles)
            {
                string tableName = Path.GetFileNameWithoutExtension(tableFile);
                if (!existingTables.Contains(tableName))
                {
                    string tableCreateCommand = File.ReadAllText(tableFile);
                    using (IDatabaseCommand command = databaseCommandFactory.Create(tableCreateCommand))
                    {
                        await command.ExecuteNonQueryAsync();
                    }
                }
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="CalculatorViewModel"/> class.
        /// </summary>
        public CalculatorViewModel(
            IDatabaseCommandFactory databaseCommandFactory,
            IUserSettingsProvider userSettingsProvider,
            int uploadId,
            ClaimsPrincipal user,
            UserManager <ApplicationUser> userManager)
        {
            var userId = userManager.GetUserId(user);

            this.UploadId = uploadId;

            string uploadUserId;

            var parameters = new Dictionary <string, object>
            {
                { "@UploadId", uploadId },
            };
            const string GetUploadUserIdCommandText = @"
	            SELECT UserId
                FROM Uploads
                WHERE Uploads.Id = @UploadId";

            using (var command = databaseCommandFactory.Create(
                       GetUploadUserIdCommandText,
                       parameters))
                using (var reader = command.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        uploadUserId = reader["UserId"].ToString();
                    }
                    else
                    {
                        return;
                    }
                }

            var uploadUserSettings = userSettingsProvider.Get(uploadUserId);

            var isUploadAnonymous = string.IsNullOrEmpty(uploadUserId);

            this.IsOwn       = string.Equals(userId, uploadUserId, StringComparison.OrdinalIgnoreCase);
            this.IsPublic    = isUploadAnonymous || uploadUserSettings.AreUploadsPublic;
            this.IsPermitted = this.IsOwn || this.IsPublic || user.IsInRole("Admin");

            this.SuggestedAncientIds = suggestedAncientIds;

            this.IsValid = true;
        }
 public UploadsController(
     IDatabaseCommandFactory databaseCommandFactory,
     GameData gameData,
     IUserSettingsProvider userSettingsProvider,
     UserManager <ApplicationUser> userManager,
     IClanManager clanManager,
     TelemetryClient telemetryClient)
 {
     this.databaseCommandFactory = databaseCommandFactory;
     this.gameData             = gameData;
     this.userSettingsProvider = userSettingsProvider;
     this.userManager          = userManager;
     this.clanManager          = clanManager;
     this.telemetryClient      = telemetryClient;
 }
示例#17
0
 public UserController(
     GameData gameData,
     TelemetryClient telemetryClient,
     IDatabaseCommandFactory databaseCommandFactory,
     IUserSettingsProvider userSettingsProvider,
     UserManager <ApplicationUser> userManager,
     IEmailSender emailSender,
     IClanManager clanManager)
 {
     _gameData               = gameData;
     _telemetryClient        = telemetryClient;
     _databaseCommandFactory = databaseCommandFactory;
     _userSettingsProvider   = userSettingsProvider;
     _userManager            = userManager;
     _emailSender            = emailSender;
     _clanManager            = clanManager;
 }
示例#18
0
            protected override void Arrange()
            {
                _options = new WhatIfExecution
                {
                    Engine           = EngineType.SqlServer,
                    DatabaseType     = DatabaseType.ODS,
                    ConnectionString = "Valid Connection String",
                    Features         = new List <string>
                    {
                        "Changes",
                        "Sample"
                    }
                };

                _databaseCommand = Stub <IDatabaseCommand>();

                _databaseCommandFactory = Stub <IDatabaseCommandFactory>();

                A.CallTo(() => _databaseCommandFactory.CreateDatabaseCommands(A <EngineType> ._))
                .Returns(new List <IDatabaseCommand> {
                    _databaseCommand
                });

                _compositeSpecification = Stub <ICompositeSpecification>();

                A.CallTo(() => _compositeSpecification.IsSatisfiedBy(A <object> ._))
                .Returns(true);

                A.CallTo(() => _databaseCommand.Execute(A <IOptions> ._))
                .Returns(
                    new DatabaseCommandResult
                {
                    IsSuccessful    = true,
                    RequiresUpgrade = false
                });

                var compositeSpecifications = new List <ICompositeSpecification> {
                    _compositeSpecification
                };

                _result = -99;

                _sut = new ApplicationRunner(_options, _databaseCommandFactory, compositeSpecifications);
            }
示例#19
0
 public AdminController(IDatabaseCommandFactory databaseCommandFactory)
 {
     this.databaseCommandFactory = databaseCommandFactory;
 }
 public DatabaseCommandBuilder(IDatabaseCommandFactory commandFactory)
 {
     _commandFactory = commandFactory;
 }
示例#21
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ProgressViewModel"/> class.
        /// </summary>
        public ProgressViewModel(
            GameData gameData,
            TelemetryClient telemetryClient,
            IDatabaseCommandFactory databaseCommandFactory,
            IUserSettingsProvider userSettingsProvider,
            ClaimsPrincipal user,
            UserManager <ApplicationUser> userManager,
            string progressUserName,
            string range)
        {
            this.ProgressUserName = progressUserName;

            var    userId         = userManager.GetUserId(user);
            string progressUserId = null;

            if (string.IsNullOrEmpty(progressUserName))
            {
                progressUserId = userId;
            }
            else
            {
                var progressUser = AsyncHelper.RunSynchronously(async() => await userManager.FindByNameAsync(progressUserName));
                if (progressUser != null)
                {
                    progressUserId = AsyncHelper.RunSynchronously(async() => await userManager.GetUserIdAsync(progressUser));
                }
            }

            if (string.IsNullOrEmpty(progressUserId))
            {
                // If we didn't get data, it's a user that doesn't exist
                return;
            }

            var progressUserSettings = userSettingsProvider.Get(progressUserId);

            if (!progressUserId.Equals(userId, StringComparison.OrdinalIgnoreCase) &&
                !progressUserSettings.AreUploadsPublic &&
                !user.IsInRole("Admin"))
            {
                // Not permitted
                return;
            }

            var userSettings = userSettingsProvider.Get(userId);

            this.RangeSelector = new GraphRangeSelectorViewModel(range);

            var data = new ProgressData(
                gameData,
                telemetryClient,
                databaseCommandFactory,
                progressUserId,
                this.RangeSelector.Start,
                this.RangeSelector.End);

            if (!data.IsValid)
            {
                return;
            }

            var prominentGraphs = new List <GraphViewModel>();

            this.TryAddGraph(prominentGraphs, "Souls Spent", data.SoulsSpentData, userSettings);
            this.TryAddGraph(prominentGraphs, "Titan Damage", data.TitanDamageData, userSettings);
            this.TryAddGraph(prominentGraphs, "Hero Souls Sacrificed", data.HeroSoulsSacrificedData, userSettings);
            this.TryAddGraph(prominentGraphs, "Total Ancient Souls", data.TotalAncientSoulsData, userSettings);
            this.TryAddGraph(prominentGraphs, "Transcendent Power", data.TranscendentPowerData, userSettings, 2);
            this.TryAddGraph(prominentGraphs, "Rubies", data.RubiesData, userSettings);
            this.TryAddGraph(prominentGraphs, "Highest Zone This Transcension", data.HighestZoneThisTranscensionData, userSettings);
            this.TryAddGraph(prominentGraphs, "Highest Zone Lifetime", data.HighestZoneLifetimeData, userSettings);
            this.TryAddGraph(prominentGraphs, "Ascensions This Transcension", data.AscensionsThisTranscensionData, userSettings);
            this.TryAddGraph(prominentGraphs, "Ascensions Lifetime", data.AscensionsLifetimeData, userSettings);

            var secondaryGraphs = new List <GraphViewModel>();

            foreach (var pair in data.OutsiderLevelData)
            {
                this.TryAddGraph(secondaryGraphs, pair.Key, pair.Value, userSettings);
            }

            foreach (var pair in data.AncientLevelData)
            {
                this.TryAddGraph(secondaryGraphs, pair.Key, pair.Value, userSettings);
            }

            this.ProminentGraphs = prominentGraphs;
            this.SecondaryGraphs = secondaryGraphs;
            this.IsValid         = true;
        }
示例#22
0
        /// <summary>
        /// Initializes a new instance of the <see cref="CompareViewModel"/> class.
        /// </summary>
        public CompareViewModel(
            GameData gameData,
            TelemetryClient telemetryClient,
            IDatabaseCommandFactory databaseCommandFactory,
            IUserSettings userSettings,
            string userId1,
            string userName1,
            string userId2,
            string userName2,
            string range)
        {
            this.UserName1 = userName1;
            this.UserName2 = userName2;

            this.RangeSelector = new GraphRangeSelectorViewModel(range);

            var userData1 = new ProgressData(
                gameData,
                telemetryClient,
                databaseCommandFactory,
                userId1,
                this.RangeSelector.Start,
                this.RangeSelector.End);
            var userData2 = new ProgressData(
                gameData,
                telemetryClient,
                databaseCommandFactory,
                userId2,
                this.RangeSelector.Start,
                this.RangeSelector.End);

            if (!userData1.IsValid || !userData2.IsValid)
            {
                return;
            }

            var prominentGraphs = new List <GraphViewModel>();

            this.TryAddGraph(prominentGraphs, "Souls Spent", this.UserName1, userData1.SoulsSpentData, this.UserName2, userData2.SoulsSpentData, userSettings);
            this.TryAddGraph(prominentGraphs, "Titan Damage", this.UserName1, userData1.TitanDamageData, this.UserName2, userData2.TitanDamageData, userSettings);
            this.TryAddGraph(prominentGraphs, "Hero Souls Sacrificed", this.UserName1, userData1.HeroSoulsSacrificedData, this.UserName2, userData2.HeroSoulsSacrificedData, userSettings);
            this.TryAddGraph(prominentGraphs, "Total Ancient Souls", this.UserName1, userData1.TotalAncientSoulsData, this.UserName2, userData2.TotalAncientSoulsData, userSettings);
            this.TryAddGraph(prominentGraphs, "Transcendent Power", this.UserName1, userData1.TranscendentPowerData, this.UserName2, userData2.TranscendentPowerData, userSettings, 2);
            this.TryAddGraph(prominentGraphs, "Rubies", this.UserName1, userData1.RubiesData, this.UserName2, userData2.RubiesData, userSettings);
            this.TryAddGraph(prominentGraphs, "Highest Zone This Transcension", this.UserName1, userData1.HighestZoneThisTranscensionData, this.UserName2, userData2.HighestZoneThisTranscensionData, userSettings);
            this.TryAddGraph(prominentGraphs, "Highest Zone Lifetime", this.UserName1, userData1.HighestZoneLifetimeData, this.UserName2, userData2.HighestZoneLifetimeData, userSettings);
            this.TryAddGraph(prominentGraphs, "Ascensions This Transcension", this.UserName1, userData1.AscensionsThisTranscensionData, this.UserName2, userData2.AscensionsThisTranscensionData, userSettings);
            this.TryAddGraph(prominentGraphs, "Ascensions Lifetime", this.UserName1, userData1.AscensionsLifetimeData, this.UserName2, userData2.AscensionsLifetimeData, userSettings);

            var secondaryGraphs = new List <GraphViewModel>();

            foreach (var pair in userData1.OutsiderLevelData)
            {
                this.TryAddGraph(secondaryGraphs, pair.Key, this.UserName1, pair.Value, this.UserName2, userData2.OutsiderLevelData.SafeGet(pair.Key), userSettings);
            }

            foreach (var pair in userData1.AncientLevelData)
            {
                this.TryAddGraph(secondaryGraphs, pair.Key, this.UserName1, pair.Value, this.UserName2, userData2.AncientLevelData.SafeGet(pair.Key), userSettings);
            }

            this.ProminentGraphs = prominentGraphs;
            this.SecondaryGraphs = secondaryGraphs;
            this.IsValid         = true;
        }
 public DbContextCommandBuilder(IDatabaseCommandFactory commandFactory)
     : base(commandFactory)
 {
 }
示例#24
0
 /// <summary>
 /// Initializes a new instance of the <see cref="UserSettingsProvider"/> class.
 /// </summary>
 public UserSettingsProvider(IDatabaseCommandFactory databaseCommandFactory)
 {
     this.databaseCommandFactory = databaseCommandFactory;
 }
示例#25
0
        /// <summary>
        /// Initializes a new instance of the <see cref="DashboardViewModel"/> class.
        /// </summary>
        public DashboardViewModel(
            GameData gameData,
            TelemetryClient telemetryClient,
            IDatabaseCommandFactory databaseCommandFactory,
            IUserSettingsProvider userSettingsProvider,
            ClaimsPrincipal user,
            UserManager <ApplicationUser> userManager)
        {
            var userId = userManager.GetUserId(user);

            var userSettings = userSettingsProvider.Get(userId);

            var startTime = DateTime.UtcNow.AddDays(-7);

            var data = new ProgressData(
                gameData,
                telemetryClient,
                databaseCommandFactory,
                userId,
                startTime,
                null);

            if (!data.IsValid)
            {
                return;
            }

            var dataSeries = data.SoulsSpentData;

            if (dataSeries.Count > 0)
            {
                this.ProgressSummaryGraph = new GraphData
                {
                    Chart = new Chart
                    {
                        Type = ChartType.Line,
                    },
                    Title = new Title
                    {
                        Text = "Souls Spent",
                    },
                    XAxis = new Axis
                    {
                        TickInterval  = 24 * 3600 * 1000, // one day
                        Type          = AxisType.Datetime,
                        TickWidth     = 0,
                        GridLineWidth = 1,
                        Labels        = new Labels
                        {
                            Align  = Align.Left,
                            X      = 3,
                            Y      = -3,
                            Format = "{value:%m/%d}",
                        },
                    },
                    YAxis = new Axis
                    {
                        Labels = new Labels
                        {
                            Align  = Align.Left,
                            X      = 3,
                            Y      = 16,
                            Format = "{value:.,0f}",
                        },
                        ShowFirstLabel = false,
                        Type           = GetYAxisType(dataSeries, userSettings),
                    },
                    Legend = new Legend
                    {
                        Enabled = false,
                    },
                    Series = new Series[]
                    {
                        new Series
                        {
                            Color = Colors.PrimarySeriesColor,
                            Data  = dataSeries
                                    .Select(datum => new Point
                            {
                                X = datum.Key.ToJavascriptTime(),
                                Y = datum.Value.ToString("F0"),
                            })
                                    .Concat(new[]
                            {
                                new Point
                                {
                                    X = DateTime.UtcNow.ToJavascriptTime(),
                                    Y = dataSeries.Last().Value.ToString("F0"),
                                },
                            })
                                    .ToList(),
                        },
                    },
                };
            }

            this.Follows = new List <string>();
            var parameters = new Dictionary <string, object>
            {
                { "@UserId", userId },
            };
            const string GetUserFollowsCommandText = @"
	            SELECT UserName
	            FROM UserFollows
	            INNER JOIN AspNetUsers
	            ON UserFollows.FollowUserId = AspNetUsers.Id
	            WHERE UserId = @UserId
	            ORDER BY UserName ASC"    ;

            using (var command = databaseCommandFactory.Create(
                       GetUserFollowsCommandText,
                       parameters))
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        this.Follows.Add(reader["UserName"].ToString());
                    }
                }

            this.IsValid = true;
        }
 /// <summary>
 /// Creates an <see cref="IDatabaseCommand"/> no parameters.
 /// </summary>
 /// <param name="databaseCommandFactory">The command factory</param>
 /// <param name="commandText">The command text</param>
 /// <returns>An <see cref="IDatabaseCommand"/></returns>
 public static IDatabaseCommand Create(
     this IDatabaseCommandFactory databaseCommandFactory,
     string commandText)
 {
     return(Create(databaseCommandFactory, commandText, emptyParameters));
 }
        /// <summary>
        /// Initializes a new instance of the <see cref="ProgressData"/> class.
        /// </summary>
        public ProgressData(
            GameData gameData,
            TelemetryClient telemetryClient,
            IDatabaseCommandFactory databaseCommandFactory,
            string userId,
            DateTime?startTime,
            DateTime?endTime)
        {
            var parameters = new Dictionary <string, object>
            {
                { "@UserId", userId },
                { "@StartTime", startTime },
                { "@EndTime", endTime },
            };

            const string GetProgressDataCommandText = @"
                -- Create a temp table that scopes the Uploads
                CREATE TABLE #ScopedUploads
                (
                    Id  INT NOT NULL, 
                    UploadTime  DATETIME2(0) NOT NULL, 
                );

                -- Populate temp table
                INSERT INTO #ScopedUploads (Id, UploadTime)
                SELECT Id, UploadTime
                FROM Uploads
                WHERE UserId = @UserId
                AND UploadTime >= ISNULL(@StartTime, '0001-01-01 00:00:00')
                AND UploadTime <= ISNULL(@EndTime, '9999-12-31 23:59:59');

                -- Computed Stats
                SELECT #ScopedUploads.UploadTime,
                       ComputedStats.TitanDamage,
                       ComputedStats.SoulsSpent,
                       ComputedStats.HeroSoulsSacrificed,
                       ComputedStats.TotalAncientSouls,
                       ComputedStats.TranscendentPower,
                       ComputedStats.Rubies,
                       ComputedStats.HighestZoneThisTranscension,
                       ComputedStats.HighestZoneLifetime,
                       ComputedStats.AscensionsThisTranscension,
                       ComputedStats.AscensionsLifetime
                FROM ComputedStats
                INNER JOIN #ScopedUploads
                ON ComputedStats.UploadId = #ScopedUploads.Id;

                -- Ancient Levels
                SELECT #ScopedUploads.UploadTime, AncientLevels.AncientId, AncientLevels.Level
                FROM AncientLevels
                INNER JOIN #ScopedUploads
                ON AncientLevels.UploadId = #ScopedUploads.Id;

                -- Outsider Levels
                SELECT #ScopedUploads.UploadTime, OutsiderLevels.OutsiderId, OutsiderLevels.Level
                FROM OutsiderLevels
                INNER JOIN #ScopedUploads
                ON OutsiderLevels.UploadId = #ScopedUploads.Id;

                -- Drop the temp table
                DROP TABLE #ScopedUploads;";

            using (var command = databaseCommandFactory.Create(
                       GetProgressDataCommandText,
                       parameters))
                using (var reader = command.ExecuteReader())
                {
                    this.TitanDamageData         = new SortedDictionary <DateTime, BigInteger>();
                    this.SoulsSpentData          = new SortedDictionary <DateTime, BigInteger>();
                    this.HeroSoulsSacrificedData = new SortedDictionary <DateTime, BigInteger>();
                    this.TotalAncientSoulsData   = new SortedDictionary <DateTime, double>();
                    this.TranscendentPowerData   = new SortedDictionary <DateTime, double>();
                    this.RubiesData = new SortedDictionary <DateTime, double>();
                    this.HighestZoneThisTranscensionData = new SortedDictionary <DateTime, double>();
                    this.HighestZoneLifetimeData         = new SortedDictionary <DateTime, double>();
                    this.AscensionsThisTranscensionData  = new SortedDictionary <DateTime, double>();
                    this.AscensionsLifetimeData          = new SortedDictionary <DateTime, double>();

                    while (reader.Read())
                    {
                        // The DateTime is a datetime2 which has no timezone so comes out as DateTimeKind.Unknown. Se need to specify the kind so it gets serialized correctly.
                        var uploadTime = DateTime.SpecifyKind(Convert.ToDateTime(reader["UploadTime"]), DateTimeKind.Utc);

                        this.TitanDamageData.AddOrUpdate(uploadTime, reader["TitanDamage"].ToString().ToBigInteger());
                        this.SoulsSpentData.AddOrUpdate(uploadTime, reader["SoulsSpent"].ToString().ToBigInteger());
                        this.HeroSoulsSacrificedData.AddOrUpdate(uploadTime, reader["HeroSoulsSacrificed"].ToString().ToBigInteger());
                        this.TotalAncientSoulsData.AddOrUpdate(uploadTime, Convert.ToDouble(reader["TotalAncientSouls"]));
                        this.TranscendentPowerData.AddOrUpdate(uploadTime, 100 * Convert.ToDouble(reader["TranscendentPower"]));
                        this.RubiesData.AddOrUpdate(uploadTime, Convert.ToDouble(reader["Rubies"]));
                        this.HighestZoneThisTranscensionData.AddOrUpdate(uploadTime, Convert.ToDouble(reader["HighestZoneThisTranscension"]));
                        this.HighestZoneLifetimeData.AddOrUpdate(uploadTime, Convert.ToDouble(reader["HighestZoneLifetime"]));
                        this.AscensionsThisTranscensionData.AddOrUpdate(uploadTime, Convert.ToDouble(reader["AscensionsThisTranscension"]));
                        this.AscensionsLifetimeData.AddOrUpdate(uploadTime, Convert.ToDouble(reader["AscensionsLifetime"]));
                    }

                    if (!reader.NextResult())
                    {
                        return;
                    }

                    this.AncientLevelData = new SortedDictionary <string, IDictionary <DateTime, BigInteger> >(StringComparer.OrdinalIgnoreCase);
                    while (reader.Read())
                    {
                        // The DateTime is a datetime2 which has no timezone so comes out as DateTimeKind.Unknown. Se need to specify the kind so it gets serialized correctly.
                        var uploadTime = DateTime.SpecifyKind(Convert.ToDateTime(reader["UploadTime"]), DateTimeKind.Utc);
                        var ancientId  = Convert.ToInt32(reader["AncientId"]);
                        var level      = reader["Level"].ToString().ToBigInteger();

                        if (!gameData.Ancients.TryGetValue(ancientId, out var ancient))
                        {
                            telemetryClient.TrackEvent("Unknown Ancient", new Dictionary <string, string> {
                                { "AncientId", ancientId.ToString() }
                            });
                            continue;
                        }

                        if (!this.AncientLevelData.TryGetValue(ancient.Name, out var levelData))
                        {
                            levelData = new SortedDictionary <DateTime, BigInteger>();
                            this.AncientLevelData.Add(ancient.Name, levelData);
                        }

                        levelData.AddOrUpdate(uploadTime, level);
                    }

                    if (!reader.NextResult())
                    {
                        return;
                    }

                    this.OutsiderLevelData = new SortedDictionary <string, IDictionary <DateTime, double> >(StringComparer.OrdinalIgnoreCase);
                    while (reader.Read())
                    {
                        // The DateTime is a datetime2 which has no timezone so comes out as DateTimeKind.Unknown. Se need to specify the kind so it gets serialized correctly.
                        var uploadTime = DateTime.SpecifyKind(Convert.ToDateTime(reader["UploadTime"]), DateTimeKind.Utc);
                        var outsiderId = Convert.ToInt32(reader["OutsiderId"]);
                        var level      = Convert.ToDouble(reader["Level"]);

                        if (!gameData.Outsiders.TryGetValue(outsiderId, out var outsider))
                        {
                            telemetryClient.TrackEvent("Unknown Outsider", new Dictionary <string, string> {
                                { "OutsiderId", outsiderId.ToString() }
                            });
                            continue;
                        }

                        if (!this.OutsiderLevelData.TryGetValue(outsider.Name, out var levelData))
                        {
                            levelData = new SortedDictionary <DateTime, double>();
                            this.OutsiderLevelData.Add(outsider.Name, levelData);
                        }

                        levelData.AddOrUpdate(uploadTime, level);
                    }
                }

            this.IsValid = true;
        }