private async Task <QuantifierResult> QuantifyPullRequest(PullRequestEventPayload payload)
        {
            var gitHubClientAdapter =
                await gitHubClientAdapterFactory.GetGitHubClientAdapterAsync(
                    payload.Installation.Id,
                    new Uri(payload.PullRequest.HtmlUrl).DnsSafeHost);

            var quantifierInput = await GetQuantifierInputFromPullRequest(payload, gitHubClientAdapter);

            var context = await GetContextFromRepoIfPresent(payload, gitHubClientAdapter);

            var quantifyClient         = new QuantifyClient(context);
            var quantifierClientResult = await quantifyClient.Compute(quantifierInput);

            await ApplyLabelToPullRequest(
                payload,
                gitHubClientAdapter,
                quantifierClientResult,
                quantifyClient.Context.Thresholds.Select(t => t.Label));

            var quantifierContextLink = !string.IsNullOrWhiteSpace(context)
                ? $"{payload.Repository.HtmlUrl}/blob/{payload.Repository.DefaultBranch}/prquantifier.yaml"
                : string.Empty;

            await UpdateCommentOnPullRequest(
                payload,
                gitHubClientAdapter,
                quantifierClientResult,
                quantifierContextLink);

            return(quantifierClientResult);
        }
        private async Task <QuantifierResult> QuantifyPullRequest(PullRequestEventPayload payload)
        {
            var gitHubClientAdapter =
                await gitHubClientAdapterFactory.GetGitHubClientAdapterAsync(
                    payload.Installation.Id,
                    new Uri(payload.PullRequest.HtmlUrl).DnsSafeHost);

            // get pull request
            var pullRequest = await gitHubClientAdapter.GetPullRequestAsync(
                payload.Repository.Id,
                payload.PullRequest.Number);

            // get pull request files
            var pullRequestFiles = await gitHubClientAdapter.GetPullRequestFilesAsync(
                payload.Repository.Id,
                payload.PullRequest.Number);

            // convert to quantifier input
            var quantifierInput = new QuantifierInput();

            foreach (var pullRequestFile in pullRequestFiles)
            {
                if (pullRequestFile.Patch == null)
                {
                    continue;
                }

                var changeType = GitChangeType.Modified;
                switch (pullRequestFile.Status)
                {
                case "modified":
                    break;

                case "added":
                    changeType = GitChangeType.Added;
                    break;

                case "deleted":
                    changeType = GitChangeType.Deleted;
                    break;
                }

                var fileExtension = !string.IsNullOrWhiteSpace(pullRequestFile.FileName)
                    ? new FileInfo(pullRequestFile.FileName).Extension
                    : string.Empty;
                var gitFilePatch = new GitFilePatch(
                    pullRequestFile.FileName,
                    fileExtension)
                {
                    ChangeType           = changeType,
                    AbsoluteLinesAdded   = pullRequestFile.Additions,
                    AbsoluteLinesDeleted = pullRequestFile.Deletions,
                    DiffContent          = pullRequestFile.Patch,
                };
                quantifierInput.Changes.Add(gitFilePatch);
            }

            // get context if present
            string context = null;

            try
            {
                var rawContext = await gitHubClientAdapter.GetRawFileAsync(
                    payload.Repository.Owner.Login,
                    payload.Repository.Name,
                    "/prquantifier.yaml");

                context = Encoding.UTF8.GetString(rawContext);
            }
            catch (NotFoundException)
            {
            }
            catch
            {
                // ignored
            }

            var quantifyClient         = new QuantifyClient(context);
            var quantifierClientResult = await quantifyClient.Compute(quantifierInput);

            // create a new label in the repository if doesn't exist
            try
            {
                var existingLabel = await gitHubClientAdapter.GetLabelAsync(
                    payload.Repository.Id,
                    quantifierClientResult.Label);
            }
            catch (NotFoundException)
            {
                // create new label
                var color = Color.FromName(quantifierClientResult.Color);
                await gitHubClientAdapter.CreateLabelAsync(
                    payload.Repository.Id,
                    new NewLabel(quantifierClientResult.Label, ConvertToHex(color)));
            }

            // apply label to pull request
            await gitHubClientAdapter.ApplyLabelAsync(
                payload.Repository.Id,
                payload.PullRequest.Number,
                new[] { quantifierClientResult.Label });

            // create a comment on the issue
            var defaultBranch         = payload.Repository.DefaultBranch;
            var quantifierContextLink = $"{payload.Repository.HtmlUrl}/blob/{defaultBranch}/prquantifier.yaml";
            var comment = await quantifierClientResult.ToMarkdownCommentAsync(
                payload.Repository.HtmlUrl,
                quantifierContextLink,
                payload.PullRequest.HtmlUrl,
                payload.PullRequest.User.Login);

            await gitHubClientAdapter.CreateIssueCommentAsync(
                payload.Repository.Id,
                payload.PullRequest.Number,
                comment);

            return(quantifierClientResult);
        }
        public async Task HandleEvent(string gitHubEvent)
        {
            var payload =
                new Octokit.Internal.SimpleJsonSerializer().Deserialize <InstallationEventPayload>(gitHubEvent);

            if (Enum.TryParse(
                    payload.Action,
                    true,
                    out GitHubEventActions parsedAction) && parsedAction != GitHubEventActions.Created)
            {
                logger.LogInformation("Ignoring installation event with {action} action", payload.Action);
                return;
            }

            logger.LogInformation("Handling installation event for {account}", payload.Installation.Account.Login);
            foreach (var payloadRepository in payload.Repositories)
            {
                if (payloadRepository.Fork)
                {
                    logger.LogInformation(
                        "Ignoring forked repository {account}/{repository}",
                        payload.Installation.Account.Login,
                        payloadRepository.Name);
                    continue;
                }

                var repoDirectory = Guid.NewGuid().ToString();
                var clonePath     = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), repoDirectory);

                try
                {
                    var dnsSafeHost         = new Uri(payload.Installation.HtmlUrl).DnsSafeHost;
                    var gitHubClientAdapter =
                        await gitHubClientAdapterFactory.GetGitHubClientAdapterAsync(
                            payload.Installation.Id,
                            dnsSafeHost);

                    var installationToken = gitHubClientAdapter.Credentials.Password;
                    logger.LogInformation(
                        "Cloning repository {account}/{repository}",
                        payload.Installation.Account.Login,
                        payloadRepository.Name);
                    Repository.Clone(
                        $"https://*****:*****@{dnsSafeHost}/{payload.Installation.Account.Login}/{payloadRepository.Name}.git",
                        clonePath);

                    await Tools.QuantifyRepositories.Program.Main(new[] { "-repoPath", clonePath });

                    using var streamReader = new StreamReader(fileSystem.Path.Combine(clonePath, $"{repoDirectory}_QuantifierResults.csv"));
                    using var csv          = new CsvReader(streamReader, CultureInfo.InvariantCulture);
                    csv.Context.RegisterClassMap <CommitStatsMap>();
                    var commitStats = csv.GetRecords <CommitStats>();
                    commitStats = commitStats.Select(
                        r =>
                    {
                        r.PartitionKey = $"{payload.Installation.Account.Login}-{payloadRepository.Name}";
                        r.RowKey       = r.CommitSha1;
                        return(r);
                    }).ToList();
                    var commitStatsMap = commitStats.ToDictionary(c => c.CommitSha1);

                    // get all closed pull requests
                    var closedPrs = await gitHubClientAdapter.GetClosedPullRequestsAsync(payloadRepository.Id);

                    foreach (var pr in closedPrs)
                    {
                        var prLeadTime = pr.MergedAt?.Subtract(pr.CreatedAt);
                        if (prLeadTime != null && commitStatsMap.TryGetValue(pr.MergeCommitSha, out var commitStat))
                        {
                            commitStat.PullRequestLeadTime = (TimeSpan)prLeadTime;
                            commitStat.PullRequestId       = pr.Id;
                            commitStat.PullRequestAuthor   = pr.User.Login;
                        }
                    }

                    // upload only the commits for which there was a PR
                    var commitStatsToUpload = commitStatsMap.Values.Where(c => c.PullRequestId != 0).ToList();

                    logger.LogInformation(
                        "Calculated {commitCount} commits to upload for {account}/{repository}",
                        commitStatsToUpload.Count,
                        payload.Installation.Account.Login,
                        payloadRepository.Name);
                    await blobStorage.CreateTableAsync(nameof(CommitStats));

                    await blobStorage.InsertOrReplaceTableEntitiesAsync(nameof(CommitStats), commitStatsToUpload);
                }
                catch (Exception e)
                {
                    logger.LogError(
                        e,
                        "Error during processing installation event for {account}/{repository}",
                        payload.Installation.Account.Login,
                        payloadRepository.Name);
                    throw;
                }
                finally
                {
                    fileSystem.DeleteDirectory(clonePath);
                }
            }
        }