├── .gitignore ├── SchoolMenuSkill.Tests ├── Models │ └── MenuScheduleTests.cs ├── Properties │ └── AssemblyInfo.cs └── SchoolMenuSkill.Tests.csproj ├── SchoolMenuSkill.sln └── SchoolMenuSkill ├── App_Data └── menu.json ├── App_Start └── WebApiConfig.cs ├── Controllers └── HomeController.cs ├── Global.asax ├── Global.asax.cs ├── Helpers └── DateTimeExtensions.cs ├── Models ├── Menu.cs └── MenuSchedule.cs ├── Properties ├── AssemblyInfo.cs └── PublishProfiles │ └── SchoolMenuSkill20161226041913 - Web Deploy.pubxml ├── Providers ├── DateTimeProvider.cs └── IDateTimeProvider.cs ├── SchoolMenuSkill.csproj ├── Speechlet ├── MenuIntentSchema.json ├── MenuSpeechlet.cs └── MenuUtterances.txt ├── Web.Debug.config ├── Web.Release.config ├── Web.config └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | [Dd]ebug/ 2 | [Rr]elease/ 3 | [Bb]in/ 4 | [Oo]bj/ 5 | *.suo 6 | *.sln.cache 7 | *.user 8 | TestResults/ 9 | *.tmp 10 | *[Rr]e[Ss]harper.user 11 | _ReSharper.*/ 12 | StyleCop.Cache 13 | 14 | .DS_Store 15 | Thumbs.db 16 | __MACOSX/ 17 | 18 | # Generated 19 | .sass-cache/ 20 | node_modules/ 21 | 22 | #VS 2015 23 | .vs/config/applicationhost.config 24 | 25 | #NuGet 26 | **/packages/* 27 | 28 | -------------------------------------------------------------------------------- /SchoolMenuSkill.Tests/Models/MenuScheduleTests.cs: -------------------------------------------------------------------------------- 1 | namespace SchoolMenuSkill.Tests.Models 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SchoolMenuSkill.Models; 7 | using SchoolMenuSkill.Providers; 8 | 9 | [TestClass] 10 | public class MenuScheduleTests 11 | { 12 | [TestMethod] 13 | public void MenuSchedule_GetsMenuForToday() 14 | { 15 | // Arrange 16 | var schedule = CreateSchedule(); 17 | 18 | // Act 19 | var date = GetTestDate(); 20 | var menu = schedule.GetMenuForDate(date); 21 | 22 | // Assert 23 | Assert.AreEqual( 24 | "On Monday 5th December, the menu is Pasta with tomato sauce, then Fish with Spinach, finishing with Ice cream", 25 | menu.ToString(date, GetDateTimeProvider())); 26 | } 27 | 28 | [TestMethod] 29 | public void MenuSchedule_GetsMenuForNextDay() 30 | { 31 | // Arrange 32 | var schedule = CreateSchedule(); 33 | 34 | // Act 35 | var date = GetTestDate().AddDays(1); 36 | var menu = schedule.GetMenuForDate(date); 37 | 38 | // Assert 39 | Assert.AreEqual( 40 | "On Tuesday 6th December, the menu will be Rice, then Chicken with Peas, finishing with Mousse", 41 | menu.ToString(date, GetDateTimeProvider())); 42 | } 43 | 44 | [TestMethod] 45 | public void MenuSchedule_GetsMenuForDayInFirstWeek() 46 | { 47 | // Arrange 48 | var schedule = CreateSchedule(); 49 | 50 | // Act 51 | var date = GetTestDate().AddDays(2); 52 | var menu = schedule.GetMenuForDate(date); 53 | 54 | // Assert 55 | Assert.AreEqual( 56 | "On Wednesday 7th December, the menu will be Spaghetti, then Beef with Carrots, finishing with Cake", 57 | menu.ToString(date, GetDateTimeProvider())); 58 | } 59 | 60 | [TestMethod] 61 | public void MenuSchedule_GetsMenuForDayInSecondWeek() 62 | { 63 | // Arrange 64 | var schedule = CreateSchedule(); 65 | 66 | // Act 67 | var date = GetTestDate().AddDays(7).AddDays(2); 68 | var menu = schedule.GetMenuForDate(date); 69 | 70 | // Assert 71 | Assert.AreEqual( 72 | "On Wednesday 14th December, the menu will be Spaghetti, then Beef with Carrots, finishing with Cake", 73 | menu.ToString(date, GetDateTimeProvider())); 74 | } 75 | 76 | [TestMethod] 77 | public void MenuSchedule_GetsMenuForDayInThirdWeek() 78 | { 79 | // Arrange 80 | var schedule = CreateSchedule(); 81 | 82 | // Act 83 | var date = GetTestDate().AddDays(14).AddDays(2); 84 | var menu = schedule.GetMenuForDate(date); 85 | 86 | // Assert 87 | Assert.AreEqual( 88 | "On Wednesday 21st December, the menu will be Spaghetti, then Beef with Carrots, finishing with Cake", 89 | menu.ToString(date, GetDateTimeProvider())); 90 | } 91 | 92 | [TestMethod] 93 | public void MenuSchedule_ReturnNullForDatePriorToInitialMenuDate() 94 | { 95 | // Arrange 96 | var schedule = CreateSchedule(); 97 | 98 | // Act 99 | var date = GetTestDate().AddDays(-5); 100 | var menu = schedule.GetMenuForDate(date); 101 | 102 | // Assert 103 | Assert.IsNull(menu); 104 | } 105 | 106 | private static MenuSchedule CreateSchedule() 107 | { 108 | return new MenuSchedule 109 | { 110 | InitialDate = GetTestDate(), 111 | Menu = new List 112 | { 113 | new Menu 114 | { 115 | WeekNumber = 1, 116 | Day = "Monday", 117 | Primo = "Pasta with tomato sauce", 118 | Secondo = "Fish", 119 | Contorno = "Spinach", 120 | Dolce = "Ice cream" 121 | }, 122 | new Menu 123 | { 124 | WeekNumber = 1, 125 | Day = "Tuesday", 126 | Primo = "Rice", 127 | Secondo = "Chicken", 128 | Contorno = "Peas", 129 | Dolce = "Mousse" 130 | }, 131 | new Menu 132 | { 133 | WeekNumber = 1, 134 | Day = "Wednesday", 135 | Primo = "Spaghetti", 136 | Secondo = "Beef", 137 | Contorno = "Carrots", 138 | Dolce = "Cake" 139 | }, 140 | new Menu 141 | { 142 | WeekNumber = 1, 143 | Day = "Thursday", 144 | Primo = "Pasta with ragu", 145 | Secondo = "Pork", 146 | Contorno = "Mushrooms", 147 | Dolce = "Creme caramel" 148 | }, 149 | new Menu 150 | { 151 | WeekNumber = 1, 152 | Day = "Friday", 153 | Primo = "Risotto", 154 | Secondo = "Nut roast", 155 | Contorno = "Sweetcorn", 156 | Dolce = "Fruit" 157 | } 158 | } 159 | }; 160 | } 161 | 162 | private static DateTime GetTestDate() 163 | { 164 | return new DateTime(2016, 12, 5); 165 | } 166 | 167 | private IDateTimeProvider GetDateTimeProvider() 168 | { 169 | return new StubDateTimeProvider(); 170 | } 171 | 172 | private class StubDateTimeProvider : IDateTimeProvider 173 | { 174 | public DateTime Now() 175 | { 176 | return GetTestDate(); 177 | } 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /SchoolMenuSkill.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SchoolMenuSkill.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SchoolMenuSkill.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4f0b4c52-4ad6-4f1f-8306-43935a24a984")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SchoolMenuSkill.Tests/SchoolMenuSkill.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {4F0B4C52-4AD6-4F1F-8306-43935A24A984} 7 | Library 8 | Properties 9 | SchoolMenuSkill.Tests 10 | SchoolMenuSkill.Tests 11 | v4.5.2 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {4eb71e02-7c71-4474-b287-b6de5d4e24e9} 59 | SchoolMenuSkill 60 | 61 | 62 | 63 | 64 | 65 | 66 | False 67 | 68 | 69 | False 70 | 71 | 72 | False 73 | 74 | 75 | False 76 | 77 | 78 | 79 | 80 | 81 | 82 | 89 | -------------------------------------------------------------------------------- /SchoolMenuSkill.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchoolMenuSkill", "SchoolMenuSkill\SchoolMenuSkill.csproj", "{4EB71E02-7C71-4474-B287-B6DE5D4E24E9}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchoolMenuSkill.Tests", "SchoolMenuSkill.Tests\SchoolMenuSkill.Tests.csproj", "{4F0B4C52-4AD6-4F1F-8306-43935A24A984}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {4EB71E02-7C71-4474-B287-B6DE5D4E24E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {4EB71E02-7C71-4474-B287-B6DE5D4E24E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {4EB71E02-7C71-4474-B287-B6DE5D4E24E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {4EB71E02-7C71-4474-B287-B6DE5D4E24E9}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {4F0B4C52-4AD6-4F1F-8306-43935A24A984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {4F0B4C52-4AD6-4F1F-8306-43935A24A984}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {4F0B4C52-4AD6-4F1F-8306-43935A24A984}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {4F0B4C52-4AD6-4F1F-8306-43935A24A984}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /SchoolMenuSkill/App_Data/menu.json: -------------------------------------------------------------------------------- 1 | { 2 | "initialDate": "2017-05-08T00:00:00+00:00", 3 | "menu": [ 4 | { 5 | "week": 1, 6 | "day": "Tuesday", 7 | "primo": "Focaccia", 8 | "secondo": "Cooked ham", 9 | "contorno": "Salad and carrots", 10 | "dolce": "Fresh fruit" 11 | }, 12 | { 13 | "week": 1, 14 | "day": "Wednesday", 15 | "primo": "Tortelloni with ricotta and spinach", 16 | "secondo": "Grilled chicken breast", 17 | "contorno": "Tomatoes and peas", 18 | "dolce": "Fruit salad" 19 | }, 20 | { 21 | "week": 1, 22 | "day": "Thursday", 23 | "primo": "Rice salad", 24 | "secondo": "Mozzarella", 25 | "contorno": "Tomatoes and cappuccio", 26 | "dolce": "Yoghurt" 27 | }, 28 | { 29 | "week": 1, 30 | "day": "Friday", 31 | "primo": "Spaghetti with tuna and tomato", 32 | "secondo": "Fish fingers", 33 | "contorno": "Tomatoes and cappuccio", 34 | "dolce": "Apricot tart" 35 | }, 36 | { 37 | "week": 2, 38 | "day": "Monday", 39 | "primo": "Gnocchetti sardi with tomatoes", 40 | "secondo": "Vitello tonnato", 41 | "contorno": "Steamed potatoes and carrots", 42 | "dolce": "Fresh fruit" 43 | }, 44 | { 45 | "week": 2, 46 | "day": "Tuesday", 47 | "primo": "Lasagne with ragù", 48 | "secondo": "Grana cheese", 49 | "contorno": "Tomato salad", 50 | "dolce": "Banana" 51 | }, 52 | { 53 | "week": 2, 54 | "day": "Wednesday", 55 | "primo": "Rice with shrimp", 56 | "secondo": "Roast turkey", 57 | "contorno": "Salid and carrots", 58 | "dolce": "Budino" 59 | }, 60 | { 61 | "week": 2, 62 | "day": "Thursday", 63 | "primo": "Gnocchi with ragù ", 64 | "secondo": "Piatto unico", 65 | "contorno": "Tomatoes and cappuccio", 66 | "dolce": "Yoghurt" 67 | }, 68 | { 69 | "week": 2, 70 | "day": "Friday", 71 | "primo": "Caserecce with pesto ", 72 | "secondo": "Tuna", 73 | "contorno": "Canellini bean salad", 74 | "dolce": "Apple" 75 | }, 76 | { 77 | "week": 3, 78 | "day": "Monday", 79 | "primo": "Pasta salad", 80 | "secondo": "Egg", 81 | "contorno": "Steamed potatoes and carrots", 82 | "dolce": "Apple" 83 | }, 84 | { 85 | "week": 3, 86 | "day": "Tuesday", 87 | "primo": "Pizza margherita", 88 | "secondo": "Turkey ham", 89 | "contorno": "Courgette and salad", 90 | "dolce": "Banana" 91 | }, 92 | { 93 | "week": 3, 94 | "day": "Wednesday", 95 | "primo": "Spaghetti with tomato", 96 | "secondo": "Baked chicken", 97 | "contorno": "Salad and cherry tomato gratin", 98 | "dolce": "Fruit salad" 99 | }, 100 | { 101 | "week": 3, 102 | "day": "Thursday", 103 | "primo": "Tortelloni with ricotta and spinach", 104 | "secondo": "Hamburger", 105 | "contorno": "Tomatoes and beans", 106 | "dolce": "Torta margherita " 107 | }, 108 | { 109 | "week": 3, 110 | "day": "Friday", 111 | "primo": "Rice and tomato", 112 | "secondo": "Fish cutlet", 113 | "contorno": "Salad and tomato", 114 | "dolce": "Yoghurt" 115 | }, 116 | { 117 | "week": 4, 118 | "day": "Monday", 119 | "primo": "Gnocchetti sardi al pomodoro", 120 | "secondo": "Asiago cheese", 121 | "contorno": "Salad and carrots", 122 | "dolce": "Apple" 123 | }, 124 | { 125 | "week": 4, 126 | "day": "Tuesday", 127 | "primo": "Fusilli with pesto", 128 | "secondo": "Vitello tonnato ", 129 | "contorno": "Salad and steamed potatoes", 130 | "dolce": "Fresh fruit" 131 | }, 132 | { 133 | "week": 4, 134 | "day": "Wednesday", 135 | "primo": "Gnocchi with pomodoro", 136 | "secondo": "Grilled turkey", 137 | "contorno": "Tomatoes and cappuccio", 138 | "dolce": "Fresh fruit" 139 | }, 140 | { 141 | "week": 4, 142 | "day": "Thursday", 143 | "primo": "Caserecce with tomato and basil", 144 | "secondo": "Straccetti of beef", 145 | "contorno": "Salad and baked potatoes", 146 | "dolce": "Budino" 147 | }, 148 | { 149 | "week": 4, 150 | "day": "Friday", 151 | "primo": "Rice with shrimp", 152 | "secondo": "Salmon", 153 | "contorno": "Tomatoes and carrots", 154 | "dolce": "Banana" 155 | }, 156 | { 157 | "week": 1, 158 | "day": "Monday", 159 | "primo": "Tagliatelle with ragù", 160 | "secondo": "Frittata", 161 | "contorno": "Salad and peporonata", 162 | "dolce": "Apple" 163 | } 164 | ] 165 | } -------------------------------------------------------------------------------- /SchoolMenuSkill/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | 6 | namespace SchoolMenuSkill 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | 14 | // Web API routes 15 | config.MapHttpAttributeRoutes(); 16 | 17 | config.Routes.MapHttpRoute( 18 | name: "DefaultApi", 19 | routeTemplate: "api/{controller}/{id}", 20 | defaults: new { id = RouteParameter.Optional } 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | namespace SchoolMenuSkill.Controllers 2 | { 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using System.Web.Http; 7 | using SchoolMenuSkill.Speechlet; 8 | 9 | public class HomeController : ApiController 10 | { 11 | [Route("api/test")] 12 | [HttpGet] 13 | public string Test() 14 | { 15 | return "Hello"; 16 | } 17 | 18 | [Route("api/menu")] 19 | [HttpPost] 20 | public async Task Menu() 21 | { 22 | var speechlet = new MenuSpeechlet(); 23 | return await speechlet.GetResponseAsync(Request); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="SchoolMenuSkill.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace SchoolMenuSkill 4 | { 5 | public class WebApiApplication : System.Web.HttpApplication 6 | { 7 | protected void Application_Start() 8 | { 9 | GlobalConfiguration.Configure(WebApiConfig.Register); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Helpers/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace SchoolMenuSkill.Helpers 7 | { 8 | public static class DateTimeExtensions 9 | { 10 | /// 11 | /// Formats a date with the th, st, nd, or rd extenion. 12 | /// 13 | /// 14 | /// Hat-tip: http://stackoverflow.com/a/21926632/489433 15 | /// 16 | public static string ToStringWithSuffix(this DateTime dateTime, string format, string suffixPlaceHolder = "$") 17 | { 18 | if (format.LastIndexOf("d", StringComparison.Ordinal) == -1 || format.Count(x => x == 'd') > 2) 19 | { 20 | return dateTime.ToString(format); 21 | } 22 | 23 | string suffix; 24 | switch (dateTime.Day) 25 | { 26 | case 1: 27 | case 21: 28 | case 31: 29 | suffix = "st"; 30 | break; 31 | case 2: 32 | case 22: 33 | suffix = "nd"; 34 | break; 35 | case 3: 36 | case 23: 37 | suffix = "rd"; 38 | break; 39 | default: 40 | suffix = "th"; 41 | break; 42 | } 43 | 44 | var formatWithSuffix = format.Insert(format.LastIndexOf("d", StringComparison.InvariantCultureIgnoreCase) + 1, suffixPlaceHolder); 45 | var date = dateTime.ToString(formatWithSuffix); 46 | 47 | return date.Replace(suffixPlaceHolder, suffix); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /SchoolMenuSkill/Models/Menu.cs: -------------------------------------------------------------------------------- 1 | namespace SchoolMenuSkill.Models 2 | { 3 | using System; 4 | using Newtonsoft.Json; 5 | using SchoolMenuSkill.Helpers; 6 | using SchoolMenuSkill.Providers; 7 | 8 | public class Menu 9 | { 10 | [JsonProperty("week")] 11 | public int WeekNumber { get; set; } 12 | 13 | [JsonProperty("day")] 14 | public string Day { get; set; } 15 | 16 | [JsonProperty("primo")] 17 | public string Primo { get; set; } 18 | 19 | [JsonProperty("secondo")] 20 | public string Secondo { get; set; } 21 | 22 | [JsonProperty("contorno")] 23 | public string Contorno { get; set; } 24 | 25 | [JsonProperty("dolce")] 26 | public string Dolce { get; set; } 27 | 28 | public string ToString(DateTime date, IDateTimeProvider dateTimeProvider = null) 29 | { 30 | dateTimeProvider = dateTimeProvider ?? new DateTimeProvider(); 31 | 32 | string verbTense; 33 | if (date.Date == dateTimeProvider.Now().Date) 34 | { 35 | verbTense = "is"; 36 | } 37 | else if (date.Date < dateTimeProvider.Now().Date) 38 | { 39 | verbTense = "was"; 40 | } 41 | else 42 | { 43 | verbTense = "will be"; 44 | } 45 | 46 | return $"On {Day} {date.ToStringWithSuffix("d MMMM")}, the menu {verbTense} {Primo}, then {Secondo} with {Contorno}, finishing with {Dolce}"; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /SchoolMenuSkill/Models/MenuSchedule.cs: -------------------------------------------------------------------------------- 1 | namespace SchoolMenuSkill.Models 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using Newtonsoft.Json; 6 | 7 | public class MenuSchedule 8 | { 9 | [JsonProperty("initialDate")] 10 | public DateTime InitialDate { get; set; } 11 | 12 | [JsonProperty("menu")] 13 | public List Menu { get; set; } 14 | 15 | public Menu GetMenuForDate(DateTime date) 16 | { 17 | var daysBetween = (date - InitialDate).TotalDays; 18 | if (daysBetween < 0) 19 | { 20 | return null; 21 | } 22 | 23 | // Count forward to find index of menu for the requested date 24 | var dayOfWeek = DayOfWeek.Monday; 25 | var menuIndex = 0; 26 | for (var i = 0; i < daysBetween; i++) 27 | { 28 | dayOfWeek = IncrementDayOfWeek(dayOfWeek); 29 | 30 | // If a week-day, advance to next menu item 31 | if (IsWeekDay(dayOfWeek)) 32 | { 33 | menuIndex++; 34 | 35 | // Reset to first item in menu once we've got to the end 36 | if (menuIndex == Menu.Count) 37 | { 38 | menuIndex = 0; 39 | } 40 | } 41 | } 42 | 43 | return Menu[menuIndex]; 44 | } 45 | 46 | private static DayOfWeek IncrementDayOfWeek(DayOfWeek dayOfWeek) 47 | { 48 | if (dayOfWeek == DayOfWeek.Saturday) 49 | { 50 | return DayOfWeek.Sunday; 51 | } 52 | 53 | return (DayOfWeek)((int)dayOfWeek + 1); 54 | } 55 | 56 | private static bool IsWeekDay(DayOfWeek dayOfWeek) 57 | { 58 | return !(dayOfWeek == DayOfWeek.Saturday || dayOfWeek == DayOfWeek.Sunday); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /SchoolMenuSkill/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SchoolMenuSkill")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SchoolMenuSkill")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("397c24a6-0242-4ebb-8d9e-6cf4dcb6c9a2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Properties/PublishProfiles/SchoolMenuSkill20161226041913 - Web Deploy.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | MSDeploy 9 | AzureWebSite 10 | Release 11 | Any CPU 12 | http://schoolmenuskill20161226041913.azurewebsites.net 13 | True 14 | False 15 | schoolmenuskill20161226041913.scm.azurewebsites.net:443 16 | SchoolMenuSkill20161226041913 17 | 18 | True 19 | WMSVC 20 | True 21 | $SchoolMenuSkill20161226041913 22 | <_SavePWD>True 23 | <_DestinationType>AzureWebSite 24 | 25 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Providers/DateTimeProvider.cs: -------------------------------------------------------------------------------- 1 | namespace SchoolMenuSkill.Providers 2 | { 3 | using System; 4 | 5 | public class DateTimeProvider : IDateTimeProvider 6 | { 7 | public DateTime Now() 8 | { 9 | return DateTime.Now; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /SchoolMenuSkill/Providers/IDateTimeProvider.cs: -------------------------------------------------------------------------------- 1 | namespace SchoolMenuSkill.Providers 2 | { 3 | using System; 4 | 5 | public interface IDateTimeProvider 6 | { 7 | DateTime Now(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /SchoolMenuSkill/SchoolMenuSkill.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 2.0 9 | {4EB71E02-7C71-4474-B287-B6DE5D4E24E9} 10 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 11 | Library 12 | Properties 13 | SchoolMenuSkill 14 | SchoolMenuSkill 15 | v4.5.2 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | ..\packages\AlexaSkillsKit.NET.1.5.1\lib\net45\AlexaSkillsKit.dll 43 | True 44 | 45 | 46 | ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll 47 | True 48 | 49 | 50 | 51 | True 52 | ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll 53 | 54 | 55 | ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll 56 | True 57 | 58 | 59 | ..\packages\Swashbuckle.Core.5.3.1\lib\net40\Swashbuckle.Core.dll 60 | 61 | 62 | ..\packages\System.IdentityModel.Tokens.Jwt.4.0.0\lib\net45\System.IdentityModel.Tokens.Jwt.dll 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | ..\packages\WebActivatorEx.2.0.6\lib\net40\WebActivatorEx.dll 86 | 87 | 88 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 89 | 90 | 91 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 92 | 93 | 94 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | Global.asax 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | Web.config 123 | 124 | 125 | Web.config 126 | 127 | 128 | 129 | 130 | 10.0 131 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | True 141 | True 142 | 4173 143 | / 144 | http://localhost:4173/ 145 | False 146 | False 147 | 148 | 149 | False 150 | 151 | 152 | 153 | 154 | 161 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Speechlet/MenuIntentSchema.json: -------------------------------------------------------------------------------- 1 | { 2 | "intents": [ 3 | { 4 | "intent": "MenuForDateIntent", 5 | "slots": [ 6 | { 7 | "name": "Date", 8 | "type": "AMAZON.DATE" 9 | } 10 | ] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /SchoolMenuSkill/Speechlet/MenuSpeechlet.cs: -------------------------------------------------------------------------------- 1 | namespace SchoolMenuSkill.Speechlet 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using AlexaSkillsKit.Slu; 8 | using AlexaSkillsKit.Speechlet; 9 | using AlexaSkillsKit.UI; 10 | using Newtonsoft.Json; 11 | using SchoolMenuSkill.Helpers; 12 | using SchoolMenuSkill.Models; 13 | 14 | public class MenuSpeechlet : SpeechletAsync 15 | { 16 | private static MenuSchedule _menuSchedule; 17 | 18 | private const string DateKey = "Date"; 19 | private const string MenuForDateIntent = "MenuForDateIntent"; 20 | 21 | public override Task OnLaunchAsync(LaunchRequest launchRequest, Session session) 22 | { 23 | return Task.FromResult(GetWelcomeResponse()); 24 | } 25 | 26 | private SpeechletResponse GetWelcomeResponse() 27 | { 28 | var output = "Welcome to the English International School menu app. Please request the menu for a given date."; 29 | return BuildSpeechletResponse("Welcome", output, false); 30 | } 31 | 32 | public async override Task OnIntentAsync(IntentRequest intentRequest, Session session) 33 | { 34 | // Get intent from the request object. 35 | var intent = intentRequest.Intent; 36 | var intentName = intent?.Name; 37 | 38 | // Note: If the session is started with an intent, no welcome message will be rendered; 39 | // rather, the intent specific response will be returned. 40 | if (MenuForDateIntent.Equals(intentName)) 41 | { 42 | return await GetMenuResponse(intent, session); 43 | } 44 | 45 | throw new SpeechletException("Invalid Intent"); 46 | } 47 | 48 | public override Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session) 49 | { 50 | return Task.FromResult(0); 51 | } 52 | 53 | public override Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session) 54 | { 55 | return Task.FromResult(0); 56 | } 57 | 58 | private async Task GetMenuResponse(Intent intent, Session session) 59 | { 60 | // Retrieve date from the intent slot 61 | var dateSlot = intent.Slots[DateKey]; 62 | 63 | // Create response 64 | string output; 65 | DateTime date; 66 | var endSession = false; 67 | if (dateSlot != null && DateTime.TryParse(dateSlot.Value, out date)) 68 | { 69 | // Retrieve and return the menu response 70 | if (_menuSchedule == null) 71 | { 72 | _menuSchedule = await LoadMenuSchedule(); 73 | } 74 | 75 | var menu = _menuSchedule.GetMenuForDate(date); 76 | if (menu != null) 77 | { 78 | output = _menuSchedule.GetMenuForDate(date).ToString(date); 79 | endSession = true; 80 | } 81 | else 82 | { 83 | output = $"Sorry, no menu is available for {date.ToStringWithSuffix("d MMMM")}. Please try another date or say quit to exit"; 84 | } 85 | } 86 | else 87 | { 88 | // Render an error since we don't know what the date requested is 89 | output = "I'm not sure which date you require, please try again or say quit to exit."; 90 | } 91 | 92 | // Return response, passing flag for whether to end the conversation 93 | return BuildSpeechletResponse(intent.Name, output, endSession); 94 | } 95 | 96 | private async Task LoadMenuSchedule() 97 | { 98 | var filePath = HttpContext.Current.Server.MapPath("/App_Data/menu.json"); 99 | using (var reader = new StreamReader(filePath)) 100 | { 101 | var json = await reader.ReadToEndAsync(); 102 | return JsonConvert.DeserializeObject(json); 103 | } 104 | } 105 | 106 | /// 107 | /// Creates and returns the visual and spoken response with shouldEndSession flag 108 | /// 109 | /// Title for the companion application home card 110 | /// Output content for speech and companion application home card 111 | /// Should the session be closed 112 | /// SpeechletResponse spoken and visual response for the given input 113 | private SpeechletResponse BuildSpeechletResponse(string title, string output, bool shouldEndSession) 114 | { 115 | // Create the Simple card content 116 | var card = new SimpleCard 117 | { 118 | Title = $"SessionSpeechlet - {title}", 119 | Content = $"SessionSpeechlet - {output}" 120 | }; 121 | 122 | // Create the plain text output 123 | var speech = new PlainTextOutputSpeech { Text = output }; 124 | 125 | // Create the speechlet response 126 | var response = new SpeechletResponse 127 | { 128 | ShouldEndSession = shouldEndSession, 129 | OutputSpeech = speech, 130 | Card = card 131 | }; 132 | return response; 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /SchoolMenuSkill/Speechlet/MenuUtterances.txt: -------------------------------------------------------------------------------- 1 | MenuForDateIntent what is the menu for {Date} 2 | MenuForDateIntent what was the menu for {Date} 3 | MenuForDateIntent what are they eating on {Date} 4 | MenuForDateIntent what were they eating on {Date} 5 | MenuForDateIntent what are they eating {Date} 6 | MenuForDateIntent what were they eating {Date} 7 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /SchoolMenuSkill/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /SchoolMenuSkill/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------