/// <summary>
        /// Callback on insert of Watson results - using the transaction submitted but NOT committed
        ///
        /// For each action, use the most likely sentiment (highest SentimentScore)
        /// Use Net Promoter score (promoters - detractors), normalized to [0, 1000] where 500 is neutral
        /// </summary>
        /// <param name="transaction">data associated with the watson transaction</param>
        public static void TicketSentimentStrategy(DataContext db, ActionToAnalyze actionToAnalyze, ActionSentimentScore maxScore)
        {
            if (actionToAnalyze.IsAgent)
            {
                return;
            }

            try
            {
                // normalize to [0, 1000]
                double actionScore = Convert.ToDouble(ToneSentiment.ToneSentiments[maxScore.SentimentID].SentimentMultiplier.Value) * Convert.ToDouble(maxScore.SentimentScore);
                actionScore = 500 * actionScore + 500;

                // submit TicketSentiment
                TicketSentiment score = null;
                CreateTicketSentiment(db, actionToAnalyze, actionScore, out score);
                score.TicketSentimentScore = (int)Math.Round(score.AverageActionSentiment);
                score.SetSentimentID(maxScore.SentimentID);
                db.SubmitChanges();
            }
            catch (Exception e2)
            {
                WatsonEventLog.WriteEntry("Exception caught at select from ACtionsToAnalyze or HttpPOST:", e2);
                Console.WriteLine(e2.ToString());
            }
        }
        public void DoRebuild()
        {
            try
            {
                string connectionString = ConfigurationManager.AppSettings.Get("ConnectionString");
                using (SqlConnection connection = new SqlConnection(connectionString))
                    using (DataContext db = new DataContext(connection))
                    {
                        db.ObjectTrackingEnabled = false; // read-only linq to sql

                        Initialize(db);

                        // load the highest confidence sentiment for each client action
                        MaxActionSentiment[] actionSentiments = LoadActionSentiments(db);
                        Array.Sort(actionSentiments, (lhs, rhs) => lhs.TicketID.CompareTo(rhs.TicketID));
                        foreach (MaxActionSentiment actionSentiment in actionSentiments)
                        {
                            // new TicketSentiment?
                            double actionScore = ToTicketScore(actionSentiment.SentimentID, actionSentiment.MaxSentimentScore);
                            if (!_ticketSentiments.ContainsKey(actionSentiment.TicketID))
                            {
                                _ticketSentiments[actionSentiment.TicketID] = CreateTicketSentiment(actionSentiment, db, actionScore);
                                continue;
                            }

                            // update existing TicketSentiment
                            TicketSentiment sentiment = _ticketSentiments[actionSentiment.TicketID];
                            int             count     = sentiment.ActionSentimentCount;
                            sentiment.AverageActionSentiment = (count * sentiment.AverageActionSentiment + actionScore) / (count + 1);
                            sentiment.ActionSentimentCount   = count + 1;
                            sentiment.TicketSentimentScore   = (int)Math.Round(sentiment.AverageActionSentiment);
                            sentiment.SetSentimentID(actionSentiment.SentimentID);
                        }

                        int insertCount = 0;
                        foreach (KeyValuePair <int, TicketSentiment> pair in _ticketSentiments)
                        {
                            pair.Value.Insert(db);
                            if (++insertCount >= 1000)
                            {
                                Console.Write('.');
                                insertCount = 0;
                            }
                        }
                    }
            }
            catch (Exception e)
            {
                WatsonEventLog.WriteEntry("Exception in RebuildTicketSentimentsTable", e);
                Console.WriteLine(e.ToString());
            }
        }
        /// <summary>
        /// first action sentiment creates the ticket sentiment
        /// </summary>
        TicketSentiment CreateTicketSentiment(MaxActionSentiment actionSentiment, DataContext db, double actionScore)
        {
            TicketSentiment ticketSentiment = new TicketSentiment()
            {
                TicketID             = actionSentiment.TicketID,
                OrganizationID       = actionSentiment.OrganizationID,
                IsAgent              = actionSentiment.IsAgent,
                TicketDateCreated    = actionSentiment.DateCreated,
                TicketSentimentScore = (int)Math.Round(actionScore),
                Sad                    = false,
                Frustrated             = false,
                Satisfied              = false,
                Excited                = false,
                Polite                 = false,
                Impolite               = false,
                Sympathetic            = false,
                AverageActionSentiment = actionScore,
                ActionSentimentCount   = 1
            };

            ticketSentiment.SetSentimentID(actionSentiment.SentimentID);
            return(ticketSentiment);
        }