Beispiel #1
0
        /// <summary> Pack Actions to Messages </summary>
        private static void PackActionsToMessages(List <Task> asyncTransactionsInProcess, DataContext db, Table <ActionSentiment> actionSentimentTable, ActionToAnalyze[] actions)
        {
            // Pack Actions into Messages
            WatsonMessage message = new WatsonMessage();

            foreach (ActionToAnalyze actionToAnalyze in actions)
            {
                // ActionSentiment already exists or emtpy?
                bool duplicateActionSentiment = actionSentimentTable.Where(u => u.ActionID == actionToAnalyze.ActionID).Any();
                if (duplicateActionSentiment)
                {
                    WatsonEventLog.WriteEntry("duplicate ActionID in ActionSentiment table " + actionToAnalyze.ActionID, EventLogEntryType.Error);
                }
                if (duplicateActionSentiment || (actionToAnalyze.WatsonText().Length == 0))
                {
                    actionToAnalyze.DeleteOnSubmit(db);
                    db.SubmitChanges();
                    continue;
                }

                // add action to message?
                Task task = PackActionToMessage(actionToAnalyze, ref message);
                if (task != null)
                {
                    asyncTransactionsInProcess.Add(task);   // wait for watson results
                    //break;    // uncomment to test with a single HTTP post
                }
            }

            // Send the remainder message if mostly full
            if (message.UtteranceCount > 40)
            {
                SendMessage(ref message);
            }
        }
        /// <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());
            }
        }
Beispiel #3
0
        static void Main(string[] args)
        {
            if (args.Contains("RebuildTicketSentimentsTable"))
            {
                WatsonEventLog.WriteEntry("RebuildTicketSentimentsTable started...");
                RebuildTicketSentimentsTable ticketSentiments = new RebuildTicketSentimentsTable(); // we can recreate TicketSentiments from ActionSentiments
                ticketSentiments.DoRebuild();
                WatsonEventLog.WriteEntry("RebuildTicketSentimentsTable completed.");
                return;
            }

            using (var service = new WatsonToneAnalyzerService())
            {
                if (!Environment.UserInteractive)
                {
                    ServiceBase.Run(service);   // running as a service
                }
                else
                {
                    // run from console
                    service.StartTimer();

                    Console.WriteLine("Press any key to stop...");
                    Console.ReadKey(true);

                    service.StopTimer();
                }
            }
        }
Beispiel #4
0
        static void PublishActionSentiment(ActionToAnalyze actionToAnalyze)
        {
            // Process The ActionToAnalyze results
            WatsonTransaction transaction = null;  // Transaction that can be rolled back

            try
            {
                // 1. Insert ActionSentiment and ActionSentimentScores
                // 2. run the TicketSentimentStrategy to create TicketSentimentScore
                // 3. delete the ActionToAnalyze
                _singleThreadedTransactions.WaitOne();  // connection does not support parallel transactions
                transaction = new WatsonTransaction();
                transaction.RecordWatsonResults(actionToAnalyze);
                transaction.Commit();
            }
            catch (Exception e2)
            {
                if (transaction != null)
                {
                    transaction.Rollback();
                }
                WatsonEventLog.WriteEntry("Watson analysis failed - system will retry", e2);
                Console.WriteLine(e2.ToString());
            }
            finally
            {
                if (transaction != null)
                {
                    transaction.Dispose();
                }
                _singleThreadedTransactions.ReleaseMutex();
            }
        }
Beispiel #5
0
        /// <summary>
        /// Post to IBM watson with Authorization and JSON formatted utterances to process
        /// </summary>
        static async Task HTTP_POST(WatsonMessage message)
        {
            if (message.Empty)
            {
                return;
            }

            //Create Json Readable String with user input:
            string result      = String.Empty;
            string jsonContent = String.Empty;

            try
            {
                using (HttpClient client = new HttpClient())
                {
                    // credentials
                    var Auth      = WatsonUsername + ":" + WatsonPassword;
                    var byteArray = Encoding.ASCII.GetBytes(Auth);
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));

                    // JSON content
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                    // Make Post call and await response
                    jsonContent = message.ToJSON();
                    using (var response = await client.PostAsJsonAsync(WatsonGatewayUrl, JObject.Parse(jsonContent)))
                    {
                        HttpContent content = response.Content;
                        result = await content.ReadAsStringAsync() ?? " ";

                        UtteranceToneList watsonResponse = JsonConvert.DeserializeObject <UtteranceToneList>(result);

                        // publish results to DB
                        message.PublishWatsonResponse(watsonResponse);
                        _actionsAnalyzed += message.ActionCount;
                    }
                }
            }
            catch (Exception ex)
            {
                WatsonEventLog.WriteEntry("********************: Error during watson analysis:", ex);
                if (!String.IsNullOrEmpty(jsonContent))
                {
                    WatsonEventLog.WriteEntry(jsonContent);
                }
                if (!String.IsNullOrEmpty(result))
                {
                    WatsonEventLog.WriteEntry(result);
                }
                Console.WriteLine(ex.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());
            }
        }
Beispiel #7
0
        public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
        {
            // only do the Action table query every 20 minutes to catch what we might have missed?
            TimeSpan timeSince = DateTime.UtcNow - _lastQueryTime;

            if (timeSince.TotalMinutes >= WatsonQueryIntervalMinutes)
            {
                WatsonEventLog.WriteEntry("Query for ActionsToAnalyze");
                ActionsToAnalyzer.FindActionsToAnalyze();
                _lastQueryTime = DateTime.UtcNow;
                WatsonEventLog.WriteEntry("Actions Analyzed " + WatsonAnalyzer.ActionsAnalyzed.ToString());
            }

            WatsonAnalyzer.AnalyzeActions();
            if (_timerEnable)
            {
                _timer.Start();
            }
        }
Beispiel #8
0
        /// <summary>
        /// Get the actions to analyze (dbo.ActionToAnalyze) and post to Watson on the BlueMix account
        /// </summary>
        static public void AnalyzeActions()
        {
            // without this the HTTP message to Watson returns 405 - failure on send
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

            // keep a handle to all the async transactions we start
            List <Task> asyncTransactionsInProcess = new List <Task>();

            try
            {
                //opens a sqlconnection at the specified location
                string connectionString = ConfigurationManager.AppSettings.Get("ConnectionString");
                using (SqlConnection connection = new SqlConnection(connectionString))
                    using (DataContext db = new DataContext(connection))
                    {
                        // initialize static data
                        ToneSentiment.Initialize(db);
                        ActionToAnalyze.Initialize(db);

                        // read ActionToAnalyze
                        Table <ActionSentiment>      actionSentimentTable = db.GetTable <ActionSentiment>();
                        Table <ActionToAnalyze>      actionToAnalyzeTable = db.GetTable <ActionToAnalyze>();
                        IQueryable <ActionToAnalyze> actionToAnalyzeQuery = from action in actionToAnalyzeTable select action;
                        ActionToAnalyze[]            actions = actionToAnalyzeQuery.ToArray();

                        // Pack the actions to analyze into watson messages
                        PackActionsToMessages(asyncTransactionsInProcess, db, actionSentimentTable, actions);

                        // ...the remainder can wait to be packed into a call when we have more
                    }
            }
            catch (Exception e2)
            {
                WatsonEventLog.WriteEntry("Exception in AnalyzeActions", e2);
                Console.WriteLine(e2.ToString());
            }
            finally
            {
                // wait until all the tone chat messages have been received and recorded
                Task.WaitAll(asyncTransactionsInProcess.ToArray(), 5 * 60 * 1000);  // 5 minute timeout just in case...
            }
        }
        /// <summary> Delete ActionToAnalyze </summary>
        public void DeleteOnSubmit(DataContext db)
        {
            Table <ActionToAnalyze> table = db.GetTable <ActionToAnalyze>();

            try
            {
                // linq classes have an attach state to the DB table row
                if (table.GetOriginalEntityState(this) == null)
                {
                    table.Attach(this); // must be attached to delete
                }
            }
            catch (Exception e2)
            {
                WatsonEventLog.WriteEntry("Exception with table.Attach - ", e2);
                Console.WriteLine(e2.ToString());
            }

            table.DeleteOnSubmit(this);
        }
Beispiel #10
0
        public static void FindActionsToAnalyze()
        {
            try
            {
                string connectionString = ConfigurationManager.AppSettings.Get("ConnectionString");
                using (SqlConnection sqlConnection1 = new SqlConnection(connectionString))
                    using (DataContext db = new DataContext(sqlConnection1))
                    {
                        var results = db.ExecuteQuery <ActionGetForWatson>("Exec " + "dbo.ActionsGetForWatson");
                        Table <ActionToAnalyze> table = db.GetTable <ActionToAnalyze>();
                        foreach (ActionGetForWatson a in results)
                        {
                            ActionToAnalyze actionToAnalyze = new ActionToAnalyze()
                            {
                                ActionID          = a.ActionID,
                                TicketID          = a.TicketID,
                                UserID            = a.UserID,
                                OrganizationID    = a.OrganizationID,
                                IsAgent           = a.IsAgent == 1,
                                DateCreated       = a.DateCreated,
                                ActionDescription = ActionToAnalyze.CleanString(a.ActionDescription), // clean the text of HTML and special characters
                            };

                            if (!table.Where(u => u.ActionID == actionToAnalyze.ActionID).Any())
                            {
                                table.InsertOnSubmit(actionToAnalyze);
                            }
                        }

                        db.SubmitChanges();
                    }
            }
            catch (Exception e)
            {
                WatsonEventLog.WriteEntry("Exception while reading from action table:", e);
                Console.WriteLine(e.ToString());
            }
        }
        /// <summary>
        /// Create the ActionSentiment for the ActionID
        /// </summary>
        /// <param name="db">context for insert/submit</param>
        /// <param name="actionToAnalyze">action to analyze</param>
        /// <returns></returns>
        static ActionSentiment InsertActionSentiment(DataContext db, ActionToAnalyze actionToAnalyze)
        {
            // already exists?
            Table <ActionSentiment> table = db.GetTable <ActionSentiment>();

            if (table.Where(u => u.ActionID == actionToAnalyze.ActionID).Any())
            {
                WatsonEventLog.WriteEntry("duplicate ActionID in ActionSentiment table " + actionToAnalyze.ActionID, EventLogEntryType.Error);
            }

            // Insert
            ActionSentiment sentiment = new ActionSentiment
            {
                ActionID       = actionToAnalyze.ActionID,
                TicketID       = actionToAnalyze.TicketID,
                UserID         = actionToAnalyze.UserID,
                OrganizationID = actionToAnalyze.OrganizationID,
                IsAgent        = actionToAnalyze.IsAgent,
                DateCreated    = DateTime.UtcNow
            };

            table.InsertOnSubmit(sentiment);
            return(sentiment);
        }