Skip to content

Commit

Permalink
Word audio 2 (#24)
Browse files Browse the repository at this point in the history
- added audio generation on word creation
- added logs
- added results for azure blob
  • Loading branch information
NicklausBrain authored Sep 15, 2024
1 parent 12461b6 commit b0f0294
Show file tree
Hide file tree
Showing 16 changed files with 325 additions and 144 deletions.
4 changes: 2 additions & 2 deletions My1kWordsEe/Components/Pages/Word.razor
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
@using My1kWordsEe.Services.Cqs;

@inject IJSRuntime JS
@inject EnsureWordCommand EnsureWordCommand
@inject CreateSampleCommand CreateSampleCommand
@inject GetOrAddSampleWordCommand EnsureWordCommand
@inject AddSampleSentenceCommand CreateSampleCommand

@code {
[Parameter]
Expand Down
9 changes: 6 additions & 3 deletions My1kWordsEe/Models/Ee1kWords.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace My1kWordsEe.Models
{
/// <summary>
/// Represents a collection of 1k most common Estonian words.
/// </summary>
public class Ee1kWords
{
public Ee1kWords()
Expand Down Expand Up @@ -32,7 +35,7 @@ public Ee1kWords WithSearch(string search)
Search = search,
SelectedWords = AllWords
.Where(w => w.Value.Contains(search, StringComparison.InvariantCultureIgnoreCase)
|| _allWordsDiacriticsFree[w.Value].Contains(search, StringComparison.InvariantCultureIgnoreCase))
|| AllWordsDiacriticsFree[w.Value].Contains(search, StringComparison.InvariantCultureIgnoreCase))
.ToArray()
};
}
Expand All @@ -56,10 +59,10 @@ public Ee1kWords WithSelectedWord(string selectedWord)

public static readonly EeWord[] AllWords = Load1kEeWords();

private static readonly IReadOnlyDictionary<string, string> _allWordsDiacriticsFree =
private static readonly IReadOnlyDictionary<string, string> AllWordsDiacriticsFree =
AllWords.ToDictionary(w => w.Value, q => RemoveDiacritics(q.Value));

static string RemoveDiacritics(string stIn)
private static string RemoveDiacritics(string stIn)
{
string stFormD = stIn.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
Expand Down
4 changes: 4 additions & 0 deletions My1kWordsEe/Models/EeWord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

namespace My1kWordsEe.Models
{
/// <summary>
/// currently serves as view model for the 1k words page
/// todo: must be unified with <see cref="SampleWord"/>
/// </summary>
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(EePartOfSpeech))]
public record EeWord
Expand Down
3 changes: 3 additions & 0 deletions My1kWordsEe/Models/PartsOfSpeech.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public enum EePartOfSpeech
Kaassõna
}

/// <summary>
/// Standard English parts of speech
/// </summary>
public enum EnPartOfSpeech
{
Noun,
Expand Down
30 changes: 24 additions & 6 deletions My1kWordsEe/Models/SampleSentence.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
namespace My1kWordsEe.Models
{
public class SampleSentence
/// <summary>
/// Sample sentence illustrating the use of a give Estonian word
/// </summary>
public record SampleSentence
{
public string EeWord { get; set; }
/// <summary>
/// Target Estonian word
/// </summary>
public string EeWord { get; init; }

Check warning on line 11 in My1kWordsEe/Models/SampleSentence.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EeWord' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public string EeSentence { get; set; }
/// <summary>
/// Illustrative sentence in Estonian
/// </summary>
public string EeSentence { get; init; }

Check warning on line 16 in My1kWordsEe/Models/SampleSentence.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EeSentence' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public string EnSentence { get; set; }
/// <summary>
/// Translation of the illustrative sentence in English
/// </summary>
public string EnSentence { get; init; }

Check warning on line 21 in My1kWordsEe/Models/SampleSentence.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EnSentence' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public Uri EeAudioUrl { get; set; }
/// <summary>
/// Sentence spoken in Estonian
/// </summary>
public Uri EeAudioUrl { get; init; }

Check warning on line 26 in My1kWordsEe/Models/SampleSentence.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EeAudioUrl' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public Uri ImageUrl { get; set; }
/// <summary>
/// Image associated with the sentence
/// </summary>
public Uri ImageUrl { get; init; }

Check warning on line 31 in My1kWordsEe/Models/SampleSentence.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'ImageUrl' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
}
}
14 changes: 13 additions & 1 deletion My1kWordsEe/Models/SampleWord.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
namespace My1kWordsEe.Models
{
/// <summary>
/// A word of the Estonian language with the respective translations and usage examples
/// </summary>
public record SampleWord
{
private readonly string eeWord = "";
Expand All @@ -21,18 +24,27 @@ public string EeWord
public string EnWord { get; init; }

Check warning on line 24 in My1kWordsEe/Models/SampleWord.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EnWord' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

/// <summary>
/// Alternatives to EnWord
/// Alternative translations to English
/// </summary>
public string[] EnWords
{
get => this.enWords;
init => this.enWords = value ?? this.enWords;
}

/// <summary>
/// Explaining the word in English
/// </summary>
public string EnExplanation { get; init; }

Check warning on line 38 in My1kWordsEe/Models/SampleWord.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EnExplanation' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

/// <summary>
/// Sample pronunciation of the word
/// </summary>
public Uri EeAudioUrl { get; init; }

Check warning on line 43 in My1kWordsEe/Models/SampleWord.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EeAudioUrl' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

/// <summary>
/// Word usage examples
/// </summary>
public SampleSentence[] Samples
{
get => this.samples;
Expand Down
15 changes: 10 additions & 5 deletions My1kWordsEe/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ public static void Main(string[] args)
}

builder.Services.AddSingleton(new StabilityAiService(stabilityAiKey));
builder.Services.AddSingleton((p) => new OpenAiService(p.GetRequiredService<ILogger<OpenAiService>>(), openAiKey));
builder.Services.AddSingleton(new AzureBlobService(azureBlobConnectionString));
builder.Services.AddSingleton(new TartuNlpService());
builder.Services.AddSingleton<EnsureWordCommand>();
builder.Services.AddSingleton<CreateSampleCommand>();
builder.Services.AddSingleton((p) => new OpenAiService(
p.GetRequiredService<ILogger<OpenAiService>>(), openAiKey));
builder.Services.AddSingleton((p) => new AzureBlobService(
p.GetRequiredService<ILogger<AzureBlobService>>(), azureBlobConnectionString));
builder.Services.AddSingleton((p) => new TartuNlpService(
p.GetRequiredService<ILogger<TartuNlpService>>()));
builder.Services.AddSingleton<GetOrAddSampleWordCommand>();
builder.Services.AddSingleton<AddSampleSentenceCommand>();
builder.Services.AddSingleton<AddSampleWordCommand>();
builder.Services.AddSingleton<AddAudioCommand>();

// Add services to the container.
builder.Services.AddRazorComponents()
Expand Down
24 changes: 24 additions & 0 deletions My1kWordsEe/Services/Cqs/AddAudioCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CSharpFunctionalExtensions;

using My1kWordsEe.Services.Db;

namespace My1kWordsEe.Services.Cqs
{
public class AddAudioCommand
{
private readonly TartuNlpService tartuNlpService;
private readonly AzureBlobService azureBlobService;

public AddAudioCommand(
TartuNlpService tartuNlpService,
AzureBlobService azureBlobService)
{
this.tartuNlpService = tartuNlpService;
this.azureBlobService = azureBlobService;
}

public Task<Result<Uri>> Invoke(string text) =>
this.tartuNlpService.GetSpeech(text).Bind(
this.azureBlobService.SaveAudio);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@

namespace My1kWordsEe.Services.Cqs
{
public class CreateSampleCommand
public class AddSampleSentenceCommand
{
private readonly AzureBlobService azureBlobService;
private readonly OpenAiService openAiService;
private readonly TartuNlpService tartuNlpService;
private readonly AddAudioCommand addAudioCommand;
private readonly StabilityAiService stabilityAiService;

public CreateSampleCommand(
public AddSampleSentenceCommand(
AzureBlobService azureBlobService,
OpenAiService openAiService,
TartuNlpService tartuNlpService,
AddAudioCommand createAudioCommand,
StabilityAiService stabilityAiService)
{
this.azureBlobService = azureBlobService;
this.openAiService = openAiService;
this.tartuNlpService = tartuNlpService;
this.addAudioCommand = createAudioCommand;
this.stabilityAiService = stabilityAiService;
}

Expand Down Expand Up @@ -64,12 +64,11 @@ public async Task<Result<SampleWord>> Invoke(SampleWord word)
}

private Task<Result<Uri>> GenerateImage(Sentence sentence) =>
this.openAiService.GetDallEPrompt(sentence.En)
.Bind(this.stabilityAiService.GenerateImage)
.Bind(image => Result.Of(this.azureBlobService.SaveImage(image)));
this.openAiService.GetDallEPrompt(sentence.En).Bind(
this.stabilityAiService.GenerateImage).Bind(
this.azureBlobService.SaveImage);

private Task<Result<Uri>> GenerateSpeech(Sentence sentence) =>
this.tartuNlpService.GetSpeech(sentence.Ee)
.Bind(speech => Result.Of(this.azureBlobService.SaveAudio(speech)));
this.addAudioCommand.Invoke(sentence.Ee);
}
}
47 changes: 47 additions & 0 deletions My1kWordsEe/Services/Cqs/AddSampleWordCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using CSharpFunctionalExtensions;

using My1kWordsEe.Models;
using My1kWordsEe.Services.Db;

namespace My1kWordsEe.Services.Cqs
{
public class AddSampleWordCommand
{
private readonly OpenAiService openAiService;
private readonly AzureBlobService azureBlobService;
private readonly AddAudioCommand addAudioCommand;

public AddSampleWordCommand(
OpenAiService openAiService,
AzureBlobService azureBlobService,
AddAudioCommand createAudioCommand)
{
this.azureBlobService = azureBlobService;
this.openAiService = openAiService;
this.addAudioCommand = createAudioCommand;
}

public async Task<Result<SampleWord>> Invoke(string eeWord)
{
(await openAiService.GetWordMetadata(eeWord)).Deconstruct(
out bool _,
out bool isAiFailure,
out SampleWord sampleWord,
out string aiError);

if (isAiFailure)
{
return Result.Failure<SampleWord>(aiError);
}

(await this.addAudioCommand.Invoke(eeWord)).Deconstruct(
out bool isAudioSaved,
out bool _,
out Uri audioUri);

sampleWord = isAudioSaved ? sampleWord with { EeAudioUrl = audioUri } : sampleWord;

return (await azureBlobService.SaveWordData(sampleWord)).Bind(_ => Result.Of(sampleWord));
}
}
}
41 changes: 0 additions & 41 deletions My1kWordsEe/Services/Cqs/EnsureWordCommand.cs

This file was deleted.

42 changes: 42 additions & 0 deletions My1kWordsEe/Services/Cqs/GetOrAddSampleWordCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using CSharpFunctionalExtensions;

using My1kWordsEe.Models;
using My1kWordsEe.Services.Db;

namespace My1kWordsEe.Services.Cqs
{
public class GetOrAddSampleWordCommand
{
private readonly AzureBlobService azureBlobService;
private readonly AddSampleWordCommand addSampleWordCommand;

public GetOrAddSampleWordCommand(
AzureBlobService azureBlobService,
AddSampleWordCommand addSampleWordCommand)
{
this.azureBlobService = azureBlobService;
this.addSampleWordCommand = addSampleWordCommand;
}

public async Task<Result<SampleWord>> Invoke(string eeWord)
{
(await azureBlobService.GetWordData(eeWord)).Deconstruct(
out bool _,
out bool isBlobAccessFailure,
out Maybe<SampleWord> savedWord,
out string blobAccessError);

if (isBlobAccessFailure)
{
return Result.Failure<SampleWord>(blobAccessError);
}

if (savedWord.HasValue)
{
return savedWord.Value;
}

return await this.addSampleWordCommand.Invoke(eeWord);
}
}
}
Loading

0 comments on commit b0f0294

Please sign in to comment.