Skip to content

Commit

Permalink
Updated T4 templates (microsoft#173)
Browse files Browse the repository at this point in the history
* Updated t4 templates with a new version that dynamically determiens the path to the json file.

* Reverted launchSettings to the original

* Merged latest from master

* Moved response management logic into ResponseManager.
Moved resusable tt code into a t4 include file.
Created several tests.

* Changed things so we don't assume en is the default language and remove the .en from the json files.

* Updated tt files and regenrated code.
Renamed .en.json to .json
  • Loading branch information
gabog authored and darrenj committed Oct 27, 2018
1 parent 402e703 commit fb3e36c
Show file tree
Hide file tree
Showing 96 changed files with 1,077 additions and 5,113 deletions.
13 changes: 11 additions & 2 deletions solutions/Virtual-Assistant/src/csharp/VirtualAssistant.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualAssistant", "assista
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Solutions", "Microsoft.Bot.Solutions\Microsoft.Bot.Solutions.csproj", "{0106AD2E-C570-42DF-B54B-3ACA50D58D80}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skills", "skills", "{1F423A0A-64D9-400C-B2B0-3AC378A5AB02}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skills", "Skills", "{1F423A0A-64D9-400C-B2B0-3AC378A5AB02}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalendarSkill", "skills\calendarskill\CalendarSkill.csproj", "{6BD03130-4931-410F-83EB-03E37E17E2B9}"
EndProject
Expand All @@ -23,12 +23,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PointOfInterestSkill", "ski
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToDoSkill", "skills\todoskill\ToDoSkill.csproj", "{3DE6A4B2-9241-4605-9E8C-BD17A1F71502}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "test", "{546AC410-598F-49FD-9583-F912DF35BDA5}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{546AC410-598F-49FD-9583-F912DF35BDA5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFramework", "skills\tests\testframework\TestFramework.csproj", "{1B4F6C4B-C813-476D-84C5-D9FD51678065}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmailSkillTest", "skills\tests\emailskilltest\EmailSkillTest.csproj", "{699BA894-1429-4DB8-ABF6-826E3F143F49}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{DFCBFFD7-4882-4C3B-9E21-83EE29CE59BF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Solutions.Tests", "tests\Microsoft.Bot.Solutions.Tests\Microsoft.Bot.Solutions.Tests.csproj", "{18366252-CEA5-4FB9-A022-ED957B491237}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -67,6 +71,10 @@ Global
{699BA894-1429-4DB8-ABF6-826E3F143F49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{699BA894-1429-4DB8-ABF6-826E3F143F49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{699BA894-1429-4DB8-ABF6-826E3F143F49}.Release|Any CPU.Build.0 = Release|Any CPU
{18366252-CEA5-4FB9-A022-ED957B491237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18366252-CEA5-4FB9-A022-ED957B491237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18366252-CEA5-4FB9-A022-ED957B491237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18366252-CEA5-4FB9-A022-ED957B491237}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -79,6 +87,7 @@ Global
{546AC410-598F-49FD-9583-F912DF35BDA5} = {1F423A0A-64D9-400C-B2B0-3AC378A5AB02}
{1B4F6C4B-C813-476D-84C5-D9FD51678065} = {546AC410-598F-49FD-9583-F912DF35BDA5}
{699BA894-1429-4DB8-ABF6-826E3F143F49} = {546AC410-598F-49FD-9583-F912DF35BDA5}
{18366252-CEA5-4FB9-A022-ED957B491237} = {DFCBFFD7-4882-4C3B-9E21-83EE29CE59BF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7569C2D0-1323-45B5-8CFF-3ECACD9E0B82}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Bot.Schema;

namespace Microsoft.Bot.Solutions.Dialogs
{
using Newtonsoft.Json;
Expand All @@ -13,6 +15,9 @@ public class Reply
[JsonProperty("speak")]
public string Speak { get; set; }

/// <summary>
/// Gets or sets the <see cref="Activity.Text"/> property of an <see cref="Activity"/>.
/// </summary>
[JsonProperty("text")]
public string Text { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using Newtonsoft.Json;

namespace Microsoft.Bot.Solutions.Dialogs
{
public class ResponseManager
{
private const string _defaultLocaleKey = "Default";
private readonly string _defaultJsonFile;
private readonly string _extraLanguageJsonFileSearchPattern;
private readonly string _jsonFilePath;
private Dictionary<string, Dictionary<string, BotResponse>> _jsonResponses;

/// <summary>
/// Initializes a new instance of the <see cref="ResponseManager"/> class.
/// </summary>
/// <param name="resourcePath">The full path to the resource files.</param>
/// <param name="resourceName">The name of the resources (e.g: MyResponses).</param>
public ResponseManager(string resourcePath, string resourceName)
{
_defaultJsonFile = resourceName + ".json";
_extraLanguageJsonFileSearchPattern = resourceName + ".*.json";
_jsonFilePath = resourcePath;
}

private Dictionary<string, Dictionary<string, BotResponse>> JsonResponses
{
get
{
if (_jsonResponses != null)
{
return _jsonResponses;
}

_jsonResponses = LoadResponses();

return _jsonResponses;
}
}

public virtual BotResponse GetBotResponse([CallerMemberName] string propertyName = null)
{
var locale = CultureInfo.CurrentUICulture.Name;
var key = GetJsonResponseKeyForLocale(locale, propertyName);

// try parent language
if (key == null)
{
locale = CultureInfo.CurrentUICulture.Name.Split("-")[0].ToLower();
key = GetJsonResponseKeyForLocale(locale, propertyName);

// fall back to default
if (key == null)
{
locale = _defaultLocaleKey;
key = GetJsonResponseKeyForLocale(locale, propertyName);
}
}

var botResponse = JsonResponses[locale][key ?? throw new KeyNotFoundException($"Unable to find response \"{propertyName}\".")];
return JsonConvert.DeserializeObject<BotResponse>(JsonConvert.SerializeObject(botResponse));
}

protected virtual Dictionary<string, Dictionary<string, BotResponse>> LoadResponses()
{
var jsonResponses = new Dictionary<string, Dictionary<string, BotResponse>>();

var jsonFiles = new List<string>(Directory.GetFiles(_jsonFilePath, _extraLanguageJsonFileSearchPattern));

var defaultFile = Path.Combine(_jsonFilePath, _defaultJsonFile);
if (!File.Exists(defaultFile))
{
throw new FileNotFoundException($"Unable to find \"{_defaultJsonFile}\" under \"{_jsonFilePath}\".", Path.Combine(_jsonFilePath, _extraLanguageJsonFileSearchPattern));
}

jsonFiles.Add(defaultFile);

foreach (var file in jsonFiles)
{
try
{
string jsonData;
using (var sr = new StreamReader(file, Encoding.GetEncoding("iso-8859-1")))
{
jsonData = sr.ReadToEnd();
}

var responses = JsonConvert.DeserializeObject<Dictionary<string, BotResponse>>(jsonData);

var fileInfo = new FileInfo(file);
var localeKey = string.Equals(fileInfo.Name, _defaultJsonFile, StringComparison.CurrentCultureIgnoreCase) ? _defaultLocaleKey : fileInfo.Name.Split(".")[1].ToLower();
jsonResponses.Add(localeKey, responses);
}
catch (JsonSerializationException ex)
{
throw new JsonSerializationException($"Error deserializing {file}. {ex.Message}", ex);
}
}

return jsonResponses;
}

private string GetJsonResponseKeyForLocale(string locale, string propertyName)
{
if (JsonResponses.ContainsKey(locale))
{
return JsonResponses[locale].ContainsKey(propertyName) ? propertyName : null;
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>Microsoft.Bot.Solutions</AssemblyName>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
Expand Down Expand Up @@ -51,14 +52,17 @@
<None Update="Resources\CommonResponses.zh.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Resources\CommonResponses.en.json">
<None Update="Resources\CommonResponses.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Resources\CommonResponses.tt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<LastGenOutput>CommonResponses.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="Resources\ResponsesTemplate.t4">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,107 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.

// https://docs.microsoft.com/en-us/visualstudio/modeling/t4-include-directive?view=vs-2017
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.IO;
using System.Runtime.CompilerServices;
using Microsoft.Bot.Solutions.Dialogs;

namespace Microsoft.Bot.Solutions.Resources
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.Bot.Solutions.Dialogs;
using Newtonsoft.Json;

/// <summary>
/// Contains bot responses.
/// </summary>
public static class CommonResponses
{
private const string _jsonFileName = "CommonResponses.*.json";
private static readonly ResponseManager _responseManager;

private static Dictionary<string, Dictionary<string, BotResponse>> _jsonResponses;
static CommonResponses()
{
var dir = Path.GetDirectoryName(typeof(CommonResponses).Assembly.Location);
var resDir = Path.Combine(dir, @"Resources");
_responseManager = new ResponseManager(resDir, "CommonResponses");
}

// Generated code:
// This code runs in the text json:
// Generated accessors
public static BotResponse ConfirmUserInfo => GetBotResponse();

public static BotResponse ConfirmSaveInfoFailed => GetBotResponse();

public static BotResponse ErrorMessage => GetBotResponse();

public static BotResponse SkillAuthenticationTitle => GetBotResponse();

public static BotResponse SkillAuthenticationPrompt => GetBotResponse();

private static Dictionary<string, Dictionary<string, BotResponse>> JsonResponses
{
get
{
if (_jsonResponses != null)
{
return _jsonResponses;
}

_jsonResponses = new Dictionary<string, Dictionary<string, BotResponse>>();
var dir = Path.GetDirectoryName(typeof(CommonResponses).Assembly.Location);
var resDir = Path.Combine(dir, "Resources");

var jsonFiles = Directory.GetFiles(resDir, _jsonFileName);
foreach (var file in jsonFiles)
{
var jsonData = File.ReadAllText(file);
var jsonResponses = JsonConvert.DeserializeObject<Dictionary<string, BotResponse>>(jsonData);
var key = new FileInfo(file).Name.Split(".")[1].ToLower();
if (_jsonResponses.ContainsKey(key))
{
_jsonResponses[key] = jsonResponses;
}
else
{
_jsonResponses.Add(key, jsonResponses);
}
}

return _jsonResponses;
}
}


private static BotResponse GetBotResponse([CallerMemberName] string propertyName = null)
{
var locale = CultureInfo.CurrentUICulture.Name;
var theK = GetJsonResponseKeyForLocale(locale, propertyName);

// fall back to parent language
if (theK == null)
{
locale = CultureInfo.CurrentUICulture.Name.Split("-")[0].ToLower();
theK = GetJsonResponseKeyForLocale(locale, propertyName);

// fall back to en
if (theK == null)
{
locale = "en";
theK = GetJsonResponseKeyForLocale(locale, propertyName);
}
}

var botResponse = JsonResponses[locale][theK ?? throw new ArgumentNullException(nameof(propertyName))];
return JsonConvert.DeserializeObject<BotResponse>(JsonConvert.SerializeObject(botResponse));
}

private static string GetJsonResponseKeyForLocale(string locale, string propertyName)
{
try
{
if (JsonResponses.ContainsKey(locale))
{
return JsonResponses[locale].ContainsKey(propertyName) ?
JsonResponses[locale].Keys.FirstOrDefault(k => string.Compare(k, propertyName, StringComparison.CurrentCultureIgnoreCase) == 0) :
null;
}

return null;
}
catch (KeyNotFoundException)
{
return null;
}
return _responseManager.GetBotResponse(propertyName);
}
}
}
Loading

0 comments on commit fb3e36c

Please sign in to comment.