Beispiel #1
0
        protected override async Task TaskActionAsync(Guid?id)
        {
            var parameters = new Dictionary <string, object>();

            parameters.Add("Method", "TaskActionAsync");
            if (id.HasValue)
            {
                parameters.Add("Job ID", id);
            }

            //  Attempt to convert the tweet response JSON string into a SearchStreamResult object.
            SearchStreamResult searchStreamResult;

            try
            {
                searchStreamResult = JsonConvert.DeserializeObject <SearchStreamResult>(_tweetResponse);
            }
            catch (Exception exception)
            {
                // Throws exception, if it's unable to convert it into a SearchStreamResult object.
                throw new ApiException("Unable to convert JSON to type SearchStreamResult.", exception, JObject.Parse(_tweetResponse));
            }

            if (searchStreamResult != null && searchStreamResult.Tweet == null)
            {
                // No tweet, so throw an exception.
                throw new ApiException("Tweet does not have an instance of Tweet.", JObject.Parse(_tweetResponse));
            }

            if (searchStreamResult != null && searchStreamResult.Tweet != null && (searchStreamResult.Tweet.ReferencedTweets == null || searchStreamResult.Tweet.ReferencedTweets.Count == 0))
            {
                var tweetParameters = new Dictionary <string, object>();
                foreach (var param in parameters)
                {
                    tweetParameters.Add(param.Key, param.Value);
                }
                tweetParameters.Add("Twitter API Tweet Id", searchStreamResult.Tweet.Id);

                _logger.LogWithParameters(LogLevel.Information, "Start importing incoming realtime tweet", tweetParameters);

                Tweet tweet = null;
                List <DashboardTweet> dashboardTweets = null;

                // Create a new scope from the service provider.
                using (var scope = _serviceProvider.CreateScope())
                {
                    // Gets the services freom the scope.
                    var blashDbContext        = scope.ServiceProvider.GetService <BlashDbContext>();
                    var dashboardService      = scope.ServiceProvider.GetService <IDashboardService>();
                    var dashboardTweetService = scope.ServiceProvider.GetService <IDashboardTweetService>();
                    var tweetService          = scope.ServiceProvider.GetService <ITweetService>();
                    var authorService         = scope.ServiceProvider.GetService <IAuthorService>();

                    var dashboards = new List <Dashboard>();
                    IList <DashboardTweet> dashboardTweetsToDelete = null;

                    using (var dbContextTransaction = await blashDbContext.Database.BeginTransactionAsync())
                    {
                        // Go through each rule that matches a tweet from the Twitter API.
                        foreach (var matchingRule in searchStreamResult.MatchingRules)
                        {
                            var dashboard = await dashboardService.GetByTwitterRuleAsync(matchingRule.Id); // Dashboard may have been deleted, so check it still exists.

                            if (dashboard == null)
                            {
                                // No dashboard exists, so continue with the next rule.
                                _logger.LogWithParameters(LogLevel.Information, "Dashboard no longer exists so finish processing.", tweetParameters);
                                continue;
                            }
                            dashboards.Add(dashboard); // Add to the list of dashboards.
                        }

                        if (dashboards == null || dashboards.Count == 0)
                        {
                            // If there are no dashboards assoicated with the tweet, finish with the task.
                            return;
                        }

                        // Create or update the tweet to the Blash database.
                        tweet = await TaskExtensions.CreateUpdateTweetAsync(scope, _logger, searchStreamResult.Tweet, searchStreamResult.Includes.Users, searchStreamResult.Includes.Media);

                        if (tweet == null)
                        {
                            return;
                        }

                        // Add it into a dashboard/tweet relationship.
                        dashboardTweets = null;

                        foreach (var dashboard in dashboards)
                        {
                            if (dashboardTweets == null)
                            {
                                dashboardTweets = new List <DashboardTweet>();
                            }

                            // Add all the relationships associated with the tweet.
                            dashboardTweets.Add(await TaskExtensions.CreateDashboardTweetRelationshipAsync(scope, _logger, dashboard.Id, tweet.Id));

                            // Ensure that we only have have the maximum number of tweets per dashboard as defined in appsettings.json under Api > Tweets > MaxPerDashboard.
                            dashboardTweetsToDelete = await dashboardTweetService.GetByDashboardAndAfterPositionAsync(dashboard.Id, _apiConfiguration.Value.Tweets?.MaxPerDashboard ?? 10);

                            if (dashboardTweetsToDelete != null && dashboardTweetsToDelete.Count > 0)
                            {
                                // Delete any dashboard/tweets that exceed the maximum number of tweets allowed for a dashboard.
                                await dashboardTweetService.DeleteMultipleAsync(dashboardTweetsToDelete.Select(dashboardTweet => dashboardTweet.Id).ToList());
                            }

                            // Make sure tweets are removed that aren't needed.
                            await tweetService.DeleteMissingTweetsFromDashboardAsync();

                            await authorService.DeleteMissingTweetsAsync();
                        }

                        // Commit the transaction to the database.
                        await dbContextTransaction.CommitAsync();
                    }

                    // Send the fact that a tweet has been created to the hub's connected clinets through SignalR.
                    await _blashHub.CreateTweetAsync(new CreateTweetResult { Data = new List <CreateTweetData> {
                                                                                 new CreateTweetData(tweet, dashboardTweets)
                                                                             } }, _cancellationToken);

                    if (dashboardTweetsToDelete != null && dashboardTweetsToDelete.Count > 0)
                    {
                        // Send the fact that tweets have been deleted from dashboards to the hub's connected clinets through SignalR.
                        await _blashHub.DeleteDashboardTweetAsync(dashboardTweetsToDelete, _cancellationToken);
                    }
                }

                _logger.LogWithParameters(LogLevel.Information, "Finish importing incoming realtime tweet", tweetParameters);
            }
        }
        /// <summary>
        /// Performs the recent tweets from the Twitter API for each dashboard.
        /// </summary>
        /// <param name="id">The job identifier.</param>
        /// <returns>An instance of <see cref="Task"/>.</returns>
        protected override async Task TaskActionAsync(Guid?id)
        {
            var parameters = new Dictionary <string, object>();

            parameters.Add("Method", "TaskActionAsync");
            if (id.HasValue)
            {
                parameters.Add("Job ID", id);
            }

            _logger.LogWithParameters(LogLevel.Information, "Start importing recent tweets from Twitter API", parameters);

            using (var scope = _serviceProvider.CreateScope())
            {
                // Gets the services freom the scope.
                var dashboardService      = scope.ServiceProvider.GetService <IDashboardService>();
                var dashboardTweetService = scope.ServiceProvider.GetService <IDashboardTweetService>();
                var authorService         = scope.ServiceProvider.GetService <IAuthorService>();
                var tweetService          = scope.ServiceProvider.GetService <ITweetService>();
                var blashDbContext        = scope.ServiceProvider.GetService <BlashDbContext>();

                var maxResults = _apiConfiguration.Value.Tweets.MaxPerDashboard.HasValue ? _apiConfiguration.Value.Tweets.MaxPerDashboard.Value : 100;

                var updatedTweetIds = new List <int>();

                List <Dashboard> dashboards = null;

                using (var dbContextTransaction = await blashDbContext.Database.BeginTransactionAsync())
                {
                    _logger.LogWithParameters(LogLevel.Debug, "Get all dashboards from database", parameters);
                    dashboards = _dashboard != null ? new List <Dashboard> {
                        _dashboard
                    } : await dashboardService.GetAllAsync();                                                                    // Get all dashboards (or one if we have specified a dashboard).

                    _logger.LogWithParameters(LogLevel.Debug, string.Format("{0} dashboard{1} returned from the database", dashboards.Count, dashboards.Count() != 1 ? "s" : ""), parameters);

                    foreach (var dashboard in dashboards)
                    {
                        _logger.LogWithParameters(LogLevel.Information, "Start importing recent tweets for dashboard", parameters);

                        TweetResult recentTweets = null;


                        var recentTweetSearchParameters = new Dictionary <string, object>();
                        foreach (var param in parameters)
                        {
                            recentTweetSearchParameters.Add(param.Key, param.Value);
                        }
                        recentTweetSearchParameters.Add("Search Query", dashboard.SearchQuery);
                        recentTweetSearchParameters.Add("MaxResults", maxResults);

                        // Get recent tweets based on the dashboard's search query.
                        _logger.LogWithParameters(LogLevel.Debug, "Get recent tweets from Twitter API.", recentTweetSearchParameters);
                        try
                        {
                            recentTweets = await _twitterApiTweetService.GetRecentTweetsAsync(dashboard.SearchQuery, maxResults);
                        }
                        catch (Exception)
                        {
                            // Set as null if the API throws an exception.
                            recentTweets = null;
                        }
                        _logger.LogWithParameters(LogLevel.Debug, string.Format("{0} recent tweet{1} returned from the Twitter API.", recentTweets != null && recentTweets.Tweets != null ? recentTweets.Tweets.Count : "0", recentTweets == null || recentTweets.Tweets == null || recentTweets.Tweets.Count() != 1 ? "s" : ""), recentTweetSearchParameters);

                        if (recentTweets == null || recentTweets.Tweets == null || recentTweets.Tweets.Count == 0)
                        {
                            continue;
                        }

                        foreach (var recentTweet in recentTweets.Tweets)
                        {
                            // Go through each tweet returned from the Twitter API.
                            var recentTweetParameters = new Dictionary <string, object>();
                            foreach (var param in recentTweetSearchParameters)
                            {
                                recentTweetParameters.Add(param.Key, param.Value);
                            }
                            recentTweetParameters.Add("Twitter API Tweet Id", recentTweet.Id);

                            _logger.LogWithParameters(LogLevel.Information, "Start importing tweet", recentTweetParameters);

                            if (recentTweet.ReferencedTweets != null && recentTweet.ReferencedTweets.Count > 0)
                            {
                                continue;
                            }

                            // Does tweet exist in the databse.
                            var tweet = await TaskExtensions.CreateUpdateTweetAsync(scope, _logger, recentTweet, recentTweets.Includes.Users, recentTweets.Includes.Media);

                            if (tweet == null)
                            {
                                continue;
                            }

                            // Keep a list of all the updated tweets.
                            updatedTweetIds.Add(tweet.Id);

                            // Update the dashboard & tweet relationship in the database.
                            await TaskExtensions.CreateDashboardTweetRelationshipAsync(scope, _logger, dashboard.Id, tweet.Id);

                            _logger.LogWithParameters(LogLevel.Debug, string.Format("Tweet has been added to Dashboard (id: '{0}').", dashboard.Id), recentTweetParameters);

                            _logger.LogWithParameters(LogLevel.Information, "Finish importing tweet", recentTweetParameters);
                        }
                    }
                    // Commit the transaction.
                    await dbContextTransaction.CommitAsync();
                }


                // Delete any tweets that have not been updated.
                if (_dashboard == null)
                {
                    using (var dbContextTransaction = blashDbContext.Database.BeginTransaction())
                    {
                        await dashboardTweetService.DeleteNonUpdatedTweetsAsync(updatedTweetIds); // Remove non-updated dashboard & tweet relationships.

                        await tweetService.DeleteMissingTweetsFromDashboardAsync();               // Delete any tweets that don't belong to a dashboard.

                        await authorService.DeleteMissingTweetsAsync();                           // Delete any authors that don't belong to any tweets.

                        await dbContextTransaction.CommitAsync();                                 // Commit the transaction to the database.
                    }
                }

                // Send the updated information to the clients connected through SignalR
                await _blashHub.RecentTweetsSyncAsync(await ApiExtensions.GetDashboardAndTweetsAsync(dashboards, tweetService), _dashboard == null, _cancellationToken);
            }


            _logger.LogWithParameters(LogLevel.Information, "Finish importing recent tweets from Twitter API", parameters);
        }