protected override string ExtractIntent(Item item) { try { Phrase phrase = new Phrase(item.Name); if (phrase.Task != null) { string verb = phrase.Task.Verb.ToLower(); string noun = phrase.Task.Article.ToLower(); Intent intent = Storage.NewSuggestionsContext.Intents.FirstOrDefault(i => i.Verb == verb && i.Noun == noun); if (intent != null) return intent.WorkflowType; } } catch (Exception ex) { TraceLog.TraceException("Could not initialize NLP engine", ex); } return base.ExtractIntent(item); }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } User user = UserContext.GetUser(item.UserID, true); if (user == null) { TraceLog.TraceError("Could not find the user associated with Item " + item.Name); return Status.Error; } ADGraphAPI adApi = new ADGraphAPI(); string adRefreshToken = null; // check for FB and/or AD credentials UserCredential cred = user.GetCredential(UserCredential.FacebookConsent); if (cred != null && cred.AccessToken != null) { adApi.FacebookAccessToken = cred.AccessToken; } cred = user.GetCredential(UserCredential.CloudADConsent); if (cred != null && cred.RenewalToken != null) { adRefreshToken = cred.RenewalToken; } if (adApi.FacebookAccessToken == null && adRefreshToken == null) { // user not having either token is not an error condition, but there is no way to generate suggestions // just move forward from this state return Status.Complete; } // if a refresh token exists for AD, get an access token from Azure ACS for the Azure AD service if (adRefreshToken != null) { try { AccessTokenRequestWithRefreshToken request = new AccessTokenRequestWithRefreshToken(new Uri(AzureOAuthConfiguration.GetTokenUri())) { RefreshToken = adRefreshToken, ClientId = AzureOAuthConfiguration.GetClientIdentity(), ClientSecret = AzureOAuthConfiguration.ClientSecret, Scope = AzureOAuthConfiguration.RelyingPartyRealm, }; OAuthMessage message = OAuthClient.GetAccessToken(request); AccessTokenResponse authzResponse = message as AccessTokenResponse; adApi.ADAccessToken = authzResponse.AccessToken; // workaround for ACS trashing the refresh token if (!String.IsNullOrEmpty(authzResponse.RefreshToken)) { TraceLog.TraceInfo("Storing new CloudAD refresh token"); user.AddCredential(UserCredential.CloudADConsent, authzResponse.AccessToken, null, authzResponse.RefreshToken); UserContext.SaveChanges(); } } catch (Exception ex) { TraceLog.TraceException("Could not contact ACS to get an access token", ex); // Facebook credentials are not available if (adApi.FacebookAccessToken == null) return Status.Pending; // could be a temporary outage, do not move off this state } } // extract a subject hint if one hasn't been discovered yet string subjectHint = GetInstanceData(workflowInstance, ActivityVariables.SubjectHint); if (String.IsNullOrEmpty(subjectHint)) { try { Phrase phrase = new Phrase(item.Name); if (phrase.Task != null) { subjectHint = phrase.Task.Subject; if (!String.IsNullOrWhiteSpace(subjectHint)) StoreInstanceData(workflowInstance, ActivityVariables.SubjectHint, subjectHint); } } catch (Exception ex) { TraceLog.TraceException("Could not initialize NLP engine", ex); } } // get contacts from Cloud AD and Facebook via the AD Graph Person service // TODO: also get local contacts from the Contacts folder try { var results = adApi.Query(subjectHint ?? ""); foreach (var subject in results) { // serialize an existing contact corresponding to the subject, // or generate a new serialized contact if one wasn't found Item contact = MakeContact(workflowInstance, item, subject); suggestionList[contact.Name] = JsonSerializer.Serialize(contact); } } catch (Exception ex) { TraceLog.TraceException("Could not contact Person Service", ex); return Status.Error; } // inexact match return Status.Pending; }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } // if an intent was already matched, return it now var intentFV = item.GetFieldValue(ExtendedFieldNames.Intent); if (intentFV != null && !String.IsNullOrWhiteSpace(intentFV.Value)) { var wt = SuggestionsContext.WorkflowTypes.FirstOrDefault(t => t.Type == intentFV.Value); if (wt != null) { suggestionList[wt.Type] = wt.Type; return Status.Complete; } } // run NLP engine over the task name to extract intent (verb / noun) as well as a subject hint string name = item.Name; string verb = null; string noun = null; string subject = null; try { Phrase phrase = new Phrase(name); if (phrase.Task != null) { verb = phrase.Task.Verb.ToLower(); noun = phrase.Task.Article.ToLower(); subject = phrase.Task.Subject; if (!String.IsNullOrWhiteSpace(subject)) StoreInstanceData(workflowInstance, ActivityVariables.SubjectHint, subject); } } catch (Exception ex) { TraceLog.TraceException("Could not initialize NLP engine", ex); } // if NLP failed (e.g. data file not found), do "poor man's NLP" - assume a structure like <verb> <noun> [{for,with} <subject>] if (verb == null) { string sentence = name.ToLower(); // remove extra whitespace and filler words StringBuilder sb = new StringBuilder(); foreach (var word in sentence.Split(new char[] { ' ', '\n', '\t' }, StringSplitOptions.RemoveEmptyEntries)) { bool add = true; foreach (var filler in "a;an;the".Split(';')) if (word == filler) { add = false; break; } if (add) sb.AppendFormat("{0} ", word); } sentence = sb.ToString().Trim(); // poor man's NLP - assume first word is verb, second word is noun string[] parts = sentence.Split(' '); if (parts.Length >= 2) { verb = parts[0]; noun = parts[1]; } if (parts.Length >= 4) { if (parts[2] == "for" || parts[2] == "with") { // capitalize and store the word following "for" in the SubjectHint workflow parameter subject = parts[3]; subject = subject.Substring(0, 1).ToUpper() + subject.Substring(1); StoreInstanceData(workflowInstance, ActivityVariables.SubjectHint, subject); } } } // try to find an intent that exactly matches the verb/noun extracted by the NLP step Intent intent = SuggestionsContext.Intents.FirstOrDefault(i => i.Verb == verb && i.Noun == noun); if (intent != null) { var wt = SuggestionsContext.WorkflowTypes.FirstOrDefault(t => t.Type == intent.WorkflowType); if (wt != null) { suggestionList[intent.WorkflowType] = wt.Type; return Status.Complete; } } // get a list of all approximate matches and surface as suggestions to the user var intentList = SuggestionsContext.Intents.Where(i => i.Verb == verb || i.Noun == noun); foreach (var i in intentList) suggestionList[i.WorkflowType] = i.WorkflowType; // if there are no suggestions, we can terminate this workflow if (suggestionList.Count == 0) { TraceLog.TraceError("No possible intents were found, terminating workflow"); return Status.Error; } return Status.Pending; }