Skip to content
This repository was archived by the owner on Jun 30, 2022. It is now read-only.

Commit eeba9a3

Browse files
authored
Chenmo/refine create calendar flow (#480)
* add skip in create flow * skip some ask when user offer details already * change create test to fit nre change * fix typo
1 parent 6737e69 commit eeba9a3

File tree

9 files changed

+164
-57
lines changed

9 files changed

+164
-57
lines changed

solutions/Virtual-Assistant/src/csharp/skills/calendarskill/CalendarSkill.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@
148148
<Content Update="Dialogs\CreateEvent\Resources\CreateEventResponses.json">
149149
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
150150
</Content>
151+
<Content Update="Dialogs\CreateEvent\Resources\CreateEventWhiteList.json">
152+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
153+
</Content>
154+
<Content Update="Dialogs\CreateEvent\Resources\CreateEventWhiteList.zh.json">
155+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
156+
</Content>
151157
<Content Update="Dialogs\DeleteEvent\Resources\DeleteEventResponses.zh.json">
152158
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
153159
</Content>

solutions/Virtual-Assistant/src/csharp/skills/calendarskill/CalendarSkillState.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public CalendarSkillState()
4444
MoveTimeSpan = 0;
4545
AskParameterContent = string.Empty;
4646
RecurrencePattern = string.Empty;
47+
CreateHasDetail = false;
4748
}
4849

4950
public User User { get; set; }
@@ -130,6 +131,8 @@ public CalendarSkillState()
130131

131132
public string RecurrencePattern { get; set; }
132133

134+
public bool CreateHasDetail { get; set; }
135+
133136
public TimeZoneInfo GetUserTimeZone()
134137
{
135138
if ((UserInfo != null) && (UserInfo.Timezone != null))
@@ -175,6 +178,7 @@ public void Clear()
175178
MoveTimeSpan = 0;
176179
AskParameterContent = string.Empty;
177180
RecurrencePattern = string.Empty;
181+
CreateHasDetail = false;
178182
}
179183

180184
public class UserInformation

solutions/Virtual-Assistant/src/csharp/skills/calendarskill/Dialogs/CreateEvent/CreateEventDialog.cs

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public CreateEventDialog(
106106
{
107107
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
108108

109-
if (string.IsNullOrEmpty(state.Title))
109+
if (string.IsNullOrEmpty(state.Title) && !state.CreateHasDetail)
110110
{
111111
var userNameString = state.Attendees.ToSpeechString(CommonStrings.And, li => li.DisplayName ?? li.Address);
112112
return await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = sc.Context.Activity.CreateReply(CreateEventResponses.NoTitle, ResponseBuilder, new StringDictionary() { { "UserName", userNameString } }) }, cancellationToken);
@@ -128,16 +128,31 @@ public CreateEventDialog(
128128
try
129129
{
130130
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
131-
if (sc.Result != null)
131+
if (sc.Result != null || state.CreateHasDetail)
132132
{
133133
if (string.IsNullOrEmpty(state.Title))
134134
{
135-
sc.Context.Activity.Properties.TryGetValue("OriginText", out var content);
136-
state.Title = content != null ? content.ToString() : sc.Context.Activity.Text;
135+
if (state.CreateHasDetail)
136+
{
137+
state.Title = CreateEventWhiteList.GetDefaultTitle();
138+
}
139+
else
140+
{
141+
sc.Context.Activity.Properties.TryGetValue("OriginText", out var content);
142+
string title = content != null ? content.ToString() : sc.Context.Activity.Text;
143+
if (CreateEventWhiteList.IsSkip(title))
144+
{
145+
state.Title = CreateEventWhiteList.GetDefaultTitle();
146+
}
147+
else
148+
{
149+
state.Title = title;
150+
}
151+
}
137152
}
138153
}
139154

140-
if (string.IsNullOrEmpty(state.Content))
155+
if (string.IsNullOrEmpty(state.Content) && !state.CreateHasDetail)
141156
{
142157
return await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = sc.Context.Activity.CreateReply(CreateEventResponses.NoContent) }, cancellationToken);
143158
}
@@ -191,12 +206,16 @@ public CreateEventDialog(
191206
try
192207
{
193208
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
194-
if (sc.Result != null)
209+
if (sc.Result != null && !state.CreateHasDetail)
195210
{
196211
if (string.IsNullOrEmpty(state.Content))
197212
{
198213
sc.Context.Activity.Properties.TryGetValue("OriginText", out var content);
199-
state.Content = content != null ? content.ToString() : sc.Context.Activity.Text;
214+
string merged_content = content != null ? content.ToString() : sc.Context.Activity.Text;
215+
if (!CreateEventWhiteList.IsSkip(merged_content))
216+
{
217+
state.Content = merged_content;
218+
}
200219
}
201220
}
202221

@@ -257,7 +276,7 @@ public CreateEventDialog(
257276
{
258277
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
259278

260-
if (state.Location == null)
279+
if (state.Location == null && !state.CreateHasDetail)
261280
{
262281
return await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = sc.Context.Activity.CreateReply(CreateEventResponses.NoLocation) }, cancellationToken);
263282
}
@@ -278,7 +297,7 @@ public CreateEventDialog(
278297
try
279298
{
280299
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
281-
if (state.Location == null && sc.Result != null)
300+
if (state.Location == null && sc.Result != null && !state.CreateHasDetail)
282301
{
283302
sc.Context.Activity.Properties.TryGetValue("OriginText", out var content);
284303
var luisResult = state.LuisResult;
@@ -289,7 +308,8 @@ public CreateEventDialog(
289308
var promptRecognizerResult = ConfirmRecognizerHelper.ConfirmYesOrNo(userInput, sc.Context.Activity.Locale);
290309

291310
// Enable the user to skip providing the location if they say something matching the Cancel intent, say something matching the ConfirmNo recognizer or something matching the NoLocation intent
292-
if (topIntent == General.Intent.Cancel.ToString() || (promptRecognizerResult.Succeeded && promptRecognizerResult.Value == false) || topIntent == Calendar.Intent.NoLocation.ToString())
311+
//if (topIntent == General.Intent.Cancel.ToString() || (promptRecognizerResult.Succeeded && promptRecognizerResult.Value == false) || topIntent == Calendar.Intent.NoLocation.ToString())
312+
if (CreateEventWhiteList.IsSkip(userInput))
293313
{
294314
state.Location = string.Empty;
295315
}
@@ -653,6 +673,12 @@ public CreateEventDialog(
653673
{
654674
try
655675
{
676+
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
677+
if (state.CreateHasDetail)
678+
{
679+
return await sc.NextAsync(cancellationToken: cancellationToken);
680+
}
681+
656682
if (((UpdateDateTimeDialogOptions)sc.Options).Reason == UpdateDateTimeDialogOptions.UpdateReason.NotFound)
657683
{
658684
return await sc.PromptAsync(Actions.DateTimePrompt, new PromptOptions { Prompt = sc.Context.Activity.CreateReply(CreateEventResponses.NoStartDate) }, cancellationToken);
@@ -672,6 +698,12 @@ public CreateEventDialog(
672698
try
673699
{
674700
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
701+
if (state.CreateHasDetail)
702+
{
703+
DateTime datetime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, state.GetUserTimeZone());
704+
state.StartDate.Add(datetime);
705+
}
706+
else
675707
if (sc.Result != null)
676708
{
677709
IList<DateTimeResolution> dateTimeResolutions = sc.Result as List<DateTimeResolution>;
@@ -821,7 +853,7 @@ public CreateEventDialog(
821853
try
822854
{
823855
var state = await Accessor.GetAsync(sc.Context, cancellationToken: cancellationToken);
824-
if (state.Duration > 0 || state.EndTime.Any() || state.EndDate.Any())
856+
if (state.Duration > 0 || state.EndTime.Any() || state.EndDate.Any() || state.CreateHasDetail)
825857
{
826858
return await sc.NextAsync(cancellationToken: cancellationToken);
827859
}
@@ -883,6 +915,11 @@ public CreateEventDialog(
883915
state.Duration = (int)ts.TotalSeconds;
884916
}
885917

918+
if (state.Duration <= 0 && state.CreateHasDetail)
919+
{
920+
state.Duration = 1800;
921+
}
922+
886923
if (state.Duration <= 0 && sc.Result != null)
887924
{
888925
sc.Context.Activity.Properties.TryGetValue("OriginText", out var content);

solutions/Virtual-Assistant/src/csharp/skills/calendarskill/Dialogs/CreateEvent/Resources/CreateEventResponses.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
"replies": [
44

55
{
6-
"text": "Alright, I will send an invite to {UserName}, what is the subject of the meeting?",
7-
"speak": "Alright, I will send an invite to {UserName}, what is the subject of the meeting?"
6+
"text": "Alright, I will send an invite to {UserName}, what is the subject of the meeting? Or you can say skip to use the default title.",
7+
"speak": "Alright, I will send an invite to {UserName}, what is the subject of the meeting? Or you can say skip to use the default title."
88
}
99
],
1010
"inputHint": "expectingInput"
1111
},
1212
"NoContent": {
1313
"replies": [
1414
{
15-
"text": "What would you like to add in the meeting details?",
16-
"speak": "What would you like to add in the meeting details?"
15+
"text": "What would you like to add in the meeting details? Or skip it.",
16+
"speak": "What would you like to add in the meeting details? Or skip it."
1717
}
1818
],
1919
"inputHint": "expectingInput"
@@ -22,8 +22,8 @@
2222
"replies": [
2323

2424
{
25-
"text": "What is the location for the meeting?",
26-
"speak": "What is the location for the meeting?"
25+
"text": "What is the location for the meeting? Or skip it.",
26+
"speak": "What is the location for the meeting? Or skip it."
2727
}
2828
],
2929
"inputHint": "expectingInput"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.IO;
5+
using System.Reflection;
6+
using System.Text;
7+
using Newtonsoft.Json;
8+
9+
namespace CalendarSkill.Dialogs.CreateEvent.Resources
10+
{
11+
public static class CreateEventWhiteList
12+
{
13+
private const string DefaultCulture = "en";
14+
private static Random Random;
15+
16+
private class WhiteList
17+
{
18+
[JsonProperty("SkipPhrases")]
19+
public List<string> SkipPhrases;
20+
21+
[JsonProperty("DefaultTitle")]
22+
public List<string> DefaultTitle;
23+
}
24+
25+
private static Dictionary<string, WhiteList> WhiteLists;
26+
27+
static CreateEventWhiteList()
28+
{
29+
Random = new Random();
30+
WhiteLists = new Dictionary<string, WhiteList>();
31+
var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
32+
var resDir = Path.Combine(dir, @"Dialogs\CreateEvent\Resources\");
33+
34+
StreamReader sr = new StreamReader(resDir + "CreateEventWhiteList.json", Encoding.Default);
35+
WhiteLists.Add("en", JsonConvert.DeserializeObject<WhiteList>(sr.ReadToEnd()));
36+
37+
sr = new StreamReader(resDir + "CreateEventWhiteList.zh.json", Encoding.Default);
38+
WhiteLists.Add("zh", JsonConvert.DeserializeObject<WhiteList>(sr.ReadToEnd()));
39+
40+
var locale = CultureInfo.CurrentUICulture.Name;
41+
}
42+
43+
public static bool IsSkip(string input)
44+
{
45+
var locale = CultureInfo.CurrentUICulture.Name.Split("-")[0].ToLower();
46+
47+
if (!WhiteLists.ContainsKey(locale))
48+
{
49+
locale = DefaultCulture;
50+
}
51+
52+
return WhiteLists[locale].SkipPhrases.Contains(input);
53+
}
54+
55+
public static string GetDefaultTitle()
56+
{
57+
var locale = CultureInfo.CurrentUICulture.Name.Split("-")[0].ToLower();
58+
59+
if (!WhiteLists.ContainsKey(locale))
60+
{
61+
locale = DefaultCulture;
62+
}
63+
64+
int rand = Random.Next(0, WhiteLists[locale].DefaultTitle.Count);
65+
return WhiteLists[locale].DefaultTitle[rand];
66+
}
67+
}
68+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"SkipPhrases": [
3+
"Skip",
4+
"skip",
5+
"no",
6+
"No"
7+
],
8+
"DefaultTitle": [
9+
"Meeting"
10+
]
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"SkipPhrases": [
3+
"跳过",
4+
"空着"
5+
],
6+
"DefaultTitle": [
7+
"会议"
8+
]
9+
}

solutions/Virtual-Assistant/src/csharp/skills/calendarskill/Dialogs/Shared/CalendarSkillDialog.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,13 +455,16 @@ protected async Task DigestCalendarLuisResult(DialogContext dc, Calendar luisRes
455455
case Calendar.Intent.FindMeetingRoom:
456456
case Calendar.Intent.CreateCalendarEntry:
457457
{
458+
state.CreateHasDetail = false;
458459
if (entity.Subject != null)
459460
{
461+
state.CreateHasDetail = true;
460462
state.Title = GetSubjectFromEntity(entity);
461463
}
462464

463465
if (entity.ContactName != null)
464466
{
467+
state.CreateHasDetail = true;
465468
state.AttendeesNameList = GetAttendeesFromEntity(entity, luisResult.Text, state.AttendeesNameList);
466469
}
467470

@@ -471,12 +474,14 @@ protected async Task DigestCalendarLuisResult(DialogContext dc, Calendar luisRes
471474
var date = GetTimeFromDateTimeString(dateString, dc.Context.Activity.Locale, state.GetUserTimeZone(), true);
472475
if (date != null)
473476
{
477+
state.CreateHasDetail = true;
474478
state.StartDate = date;
475479
}
476480

477481
date = GetTimeFromDateTimeString(dateString, dc.Context.Activity.Locale, state.GetUserTimeZone(), false);
478482
if (date != null)
479483
{
484+
state.CreateHasDetail = true;
480485
state.EndDate = date;
481486
}
482487
}
@@ -487,6 +492,7 @@ protected async Task DigestCalendarLuisResult(DialogContext dc, Calendar luisRes
487492
var date = GetDateFromDateTimeString(dateString, dc.Context.Activity.Locale, state.GetUserTimeZone());
488493
if (date != null)
489494
{
495+
state.CreateHasDetail = true;
490496
state.EndDate = date;
491497
}
492498
}
@@ -497,12 +503,14 @@ protected async Task DigestCalendarLuisResult(DialogContext dc, Calendar luisRes
497503
var time = GetTimeFromDateTimeString(timeString, dc.Context.Activity.Locale, state.GetUserTimeZone(), true);
498504
if (time != null)
499505
{
506+
state.CreateHasDetail = true;
500507
state.StartTime = time;
501508
}
502509

503510
time = GetTimeFromDateTimeString(timeString, dc.Context.Activity.Locale, state.GetUserTimeZone(), false);
504511
if (time != null)
505512
{
513+
state.CreateHasDetail = true;
506514
state.EndTime = time;
507515
}
508516
}
@@ -513,6 +521,7 @@ protected async Task DigestCalendarLuisResult(DialogContext dc, Calendar luisRes
513521
var time = GetTimeFromDateTimeString(timeString, dc.Context.Activity.Locale, state.GetUserTimeZone());
514522
if (time != null)
515523
{
524+
state.CreateHasDetail = true;
516525
state.EndTime = time;
517526
}
518527
}
@@ -522,17 +531,20 @@ protected async Task DigestCalendarLuisResult(DialogContext dc, Calendar luisRes
522531
int duration = GetDurationFromEntity(entity, dc.Context.Activity.Locale);
523532
if (duration != -1)
524533
{
534+
state.CreateHasDetail = true;
525535
state.Duration = duration;
526536
}
527537
}
528538

529539
if (entity.MeetingRoom != null)
530540
{
541+
state.CreateHasDetail = true;
531542
state.Location = GetMeetingRoomFromEntity(entity);
532543
}
533544

534545
if (entity.Location != null)
535546
{
547+
state.CreateHasDetail = true;
536548
state.Location = GetLocationFromEntity(entity);
537549
}
538550

0 commit comments

Comments
 (0)