Skip to content

Commit

Permalink
Export semantic domains in English (#2948)
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec authored Mar 26, 2024
1 parent 87e1ac9 commit 3c646a2
Show file tree
Hide file tree
Showing 50 changed files with 536 additions and 253 deletions.
18 changes: 18 additions & 0 deletions Backend.Tests/Controllers/SemanticDomainControllerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Backend.Tests.Mocks;
using BackendFramework.Controllers;
using BackendFramework.Interfaces;
Expand Down Expand Up @@ -39,6 +40,23 @@ public void Setup()
_semDomController = new SemanticDomainController(_semDomRepository);
}

[Test]
public void GetAllSemanticDomainNamesFound()
{
var treeNodes = new List<SemanticDomainTreeNode> { new(_semDom) };
((SemanticDomainRepositoryMock)_semDomRepository).SetNextResponse(treeNodes);
var names = ((OkObjectResult)_semDomController.GetAllSemanticDomainNames(Lang).Result).Value;
Assert.That(names, Has.Count.EqualTo(1));
Assert.That(((Dictionary<string, string>)names!)[Id], Is.EqualTo(Name));
}

[Test]
public void GetAllSemanticDomainNamesNotFound()
{
var names = ((OkObjectResult)_semDomController.GetAllSemanticDomainNames(Lang).Result).Value;
Assert.That(names, Has.Count.EqualTo(0));
}

[Test]
public void GetSemanticDomainFullDomainFound()
{
Expand Down
18 changes: 1 addition & 17 deletions Backend.Tests/Mocks/SemanticDomainRepositoryMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,10 @@ namespace Backend.Tests.Mocks
public class SemanticDomainRepositoryMock : ISemanticDomainRepository
{
private object? _responseObj;
private List<string>? _validLangs;

public Task<List<SemanticDomainTreeNode>?> GetAllSemanticDomainTreeNodes(string lang)
{
if (_validLangs is null)
{
return Task.FromResult((List<SemanticDomainTreeNode>?)_responseObj);
}

List<SemanticDomainTreeNode>? semDoms = null;
if (_validLangs.Contains(lang))
{
semDoms = new() { new(new SemanticDomain { Lang = lang }) };
}
return Task.FromResult(semDoms);
return Task.FromResult((List<SemanticDomainTreeNode>?)_responseObj);
}

public Task<SemanticDomainFull?> GetSemanticDomainFull(string id, string lang)
Expand All @@ -44,10 +33,5 @@ internal void SetNextResponse(object? response)
{
_responseObj = response;
}

internal void SetValidLangs(List<string>? validLangs)
{
_validLangs = validLangs;
}
}
}
32 changes: 0 additions & 32 deletions Backend.Tests/Services/LiftServiceTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System.Collections.Generic;
using System.IO;
using Backend.Tests.Mocks;
using BackendFramework.Interfaces;
using BackendFramework.Models;
using BackendFramework.Services;
using NUnit.Framework;

Expand Down Expand Up @@ -64,34 +61,5 @@ public void StoreRetrieveDeleteImportTest()
Assert.That(_liftService.DeleteImport(UserId), Is.True);
Assert.That(_liftService.RetrieveImport(UserId), Is.Null);
}

[Test]
public void CreateLiftRangesTest()
{
List<SemanticDomain> frDoms = new() { new() { Lang = "fr" }, new() };
List<SemanticDomain> ptDoms = new() { new(), new() { Lang = "pt" } };
List<SemanticDomain> zzDoms = new() { new() { Lang = "zz" } };
List<Word> projWords = new()
{
// First semantic domain of the second sense of a word
new() { Senses = new() { new(), new() { SemanticDomains = frDoms } } },
// Second semantic domain of the first sense of a word
new() { Senses = new() { new() { SemanticDomains = ptDoms }, new() } },
// Semantic domain with unsupported language
new() { Senses = new() { new() { SemanticDomains = zzDoms } } }
};

((SemanticDomainRepositoryMock)_semDomRepo).SetValidLangs(new() { "en", "fr", "pt" });
var langs = _liftService.CreateLiftRanges(projWords, new(), FileName).Result;
Assert.That(langs, Has.Count.EqualTo(2));
Assert.That(langs, Does.Contain("fr"));
Assert.That(langs, Does.Contain("pt"));

var liftRangesText = File.ReadAllText(FileName);
Assert.That(liftRangesText, Does.Not.Contain("\"en\""));
Assert.That(liftRangesText, Does.Contain("\"fr\""));
Assert.That(liftRangesText, Does.Contain("\"pt\""));
Assert.That(liftRangesText, Does.Not.Contain("\"zz\""));
}
}
}
15 changes: 15 additions & 0 deletions Backend/Controllers/SemanticDomainController.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BackendFramework.Interfaces;
using BackendFramework.Models;
Expand All @@ -19,6 +21,19 @@ public SemanticDomainController(ISemanticDomainRepository semDomRepo)
_semDomRepo = semDomRepo;
}

/// <summary>
/// Returns a dictionary mapping domain ids to names in the specified language (fallback: "en").
/// </summary>
[HttpGet("allDomainNames", Name = "GetAllSemanticDomainNames")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Dictionary<string, string>))]
public async Task<IActionResult> GetAllSemanticDomainNames(string lang)
{
var semDoms = await _semDomRepo.GetAllSemanticDomainTreeNodes(lang)
?? await _semDomRepo.GetAllSemanticDomainTreeNodes("en")
?? new();
return Ok(semDoms.ToDictionary(x => x.Id, x => x.Name));
}

/// <summary> Returns <see cref="SemanticDomainFull"/> with specified id and in specified language </summary>
[HttpGet("domainFull", Name = "GetSemanticDomainFull")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SemanticDomainFull))]
Expand Down
2 changes: 1 addition & 1 deletion Backend/Interfaces/ILiftService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface ILiftService
ILiftMerger GetLiftImporterExporter(string projectId, string vernLang, IWordRepository wordRepo);
Task<bool> LdmlImport(string dirPath, IProjectRepository projRepo, Project project);
Task<string> LiftExport(string projectId, IWordRepository wordRepo, IProjectRepository projRepo);
Task<List<string>> CreateLiftRanges(List<Word> projWords, List<SemanticDomain> projDoms, string rangesDest);
Task CreateLiftRanges(List<SemanticDomain> projDoms, string rangesDest);

// Methods to store, retrieve, and delete an export string in a common dictionary.
void StoreExport(string userId, string filePath);
Expand Down
48 changes: 12 additions & 36 deletions Backend/Services/LiftService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ public async Task<string> LiftExport(
// So the words found in allWords with no matching guid in activeWords are exported as 'deleted'.
var deletedWords = allWords.Where(
x => activeWords.All(w => w.Guid != x.Guid)).DistinctBy(w => w.Guid).ToList();
var semDomNames = (await _semDomRepo.GetAllSemanticDomainTreeNodes("en") ?? new())
.ToDictionary(x => x.Id, x => x.Name);
foreach (var wordEntry in activeWords)
{
var id = MakeSafeXmlAttribute(wordEntry.Vernacular) + "_" + wordEntry.Guid;
Expand All @@ -321,7 +323,7 @@ public async Task<string> LiftExport(

AddNote(entry, wordEntry);
AddVern(entry, wordEntry, proj.VernacularWritingSystem.Bcp47);
AddSenses(entry, wordEntry);
AddSenses(entry, wordEntry, semDomNames);
await AddAudio(entry, wordEntry.Audio, audioDir, projectId, projSpeakers);

liftWriter.Add(entry);
Expand All @@ -334,7 +336,7 @@ public async Task<string> LiftExport(

AddNote(entry, wordEntry);
AddVern(entry, wordEntry, proj.VernacularWritingSystem.Bcp47);
AddSenses(entry, wordEntry);
AddSenses(entry, wordEntry, semDomNames);
await AddAudio(entry, wordEntry.Audio, audioDir, projectId, projSpeakers);

liftWriter.AddDeletedEntry(entry);
Expand Down Expand Up @@ -367,7 +369,7 @@ public async Task<string> LiftExport(
// Export semantic domains to lift-ranges
if (proj.SemanticDomains.Count != 0 || CopyLiftRanges(proj.Id, rangesDest) is null)
{
await CreateLiftRanges(allWords, proj.SemanticDomains, rangesDest);
await CreateLiftRanges(proj.SemanticDomains, rangesDest);
}

// Export character set to ldml.
Expand Down Expand Up @@ -408,11 +410,8 @@ public async Task<string> LiftExport(
return rangesSrc;
}

/// <summary> Export semantic domains to lift-ranges </summary>
/// <exception cref="ExportException"> If fails to load needed semantic domain list </exception>
/// <returns> List of languages found in project sem-doms and included in the lift-ranges file </returns>
public async Task<List<string>> CreateLiftRanges(
List<Word> projWords, List<SemanticDomain> projDoms, string rangesDest)
/// <summary> Export English semantic domains (along with any custom domains) to lift-ranges. </summary>
public async Task CreateLiftRanges(List<SemanticDomain> projDoms, string rangesDest)
{
await using var liftRangesWriter = XmlWriter.Create(rangesDest, new XmlWriterSettings
{
Expand All @@ -425,21 +424,8 @@ public async Task<List<string>> CreateLiftRanges(
liftRangesWriter.WriteStartElement("range");
liftRangesWriter.WriteAttributeString("id", "semantic-domain-ddp4");

var wordLangs = projWords
.SelectMany(w => w.Senses.SelectMany(s => s.SemanticDomains.Select(d => d.Lang))).Distinct();
var exportLangs = new List<string>();
foreach (var lang in wordLangs)
{
var semDoms = await _semDomRepo.GetAllSemanticDomainTreeNodes(lang);
if (semDoms is not null && semDoms.Count > 0)
{
exportLangs.Add(lang);
foreach (var sd in semDoms)
{
WriteRangeElement(liftRangesWriter, sd.Id, sd.Guid, sd.Name, sd.Lang);
}
}
}
(await _semDomRepo.GetAllSemanticDomainTreeNodes("en"))?
.ForEach(sd => { WriteRangeElement(liftRangesWriter, sd.Id, sd.Guid, sd.Name, sd.Lang); });

// Pull from new semantic domains in project
foreach (var sd in projDoms)
Expand All @@ -456,7 +442,6 @@ public async Task<List<string>> CreateLiftRanges(

await liftRangesWriter.FlushAsync();
liftRangesWriter.Close();
return exportLangs;
}

/// <summary> Adds <see cref="Note"/> of a word to be written out to lift </summary>
Expand Down Expand Up @@ -486,7 +471,7 @@ private static void AddVern(LexEntry entry, Word wordEntry, string vernacularBcp
}

/// <summary> Adds each <see cref="Sense"/> of a word to be written out to lift </summary>
private static void AddSenses(LexEntry entry, Word wordEntry)
private static void AddSenses(LexEntry entry, Word wordEntry, Dictionary<string, string> semDomNames)
{
var activeSenses = wordEntry.Senses.Where(
s => s.Accessibility == Status.Active || s.Accessibility == Status.Protected).ToList();
Expand Down Expand Up @@ -540,7 +525,8 @@ private static void AddSenses(LexEntry entry, Word wordEntry)
foreach (var semDom in currentSense.SemanticDomains)
{
var orc = new OptionRefCollection();
orc.Add(semDom.Id + " " + semDom.Name);
semDomNames.TryGetValue(semDom.Id, out string? name);
orc.Add(semDom.Id + " " + name ?? semDom.Name);
lexSense.Properties.Add(new KeyValuePair<string, IPalasoDataObjectProperty>(
LexSense.WellKnownProperties.SemanticDomainDdp4, orc));
}
Expand Down Expand Up @@ -658,16 +644,6 @@ private static void WriteRangeElement(
liftRangesWriter.WriteEndElement(); //end range element
}

[Serializable]
public class ExportException : Exception
{
public ExportException() { }

public ExportException(string msg) : base(msg) { }

protected ExportException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

private sealed class LiftMerger : ILiftMerger
{
private readonly string _projectId;
Expand Down
2 changes: 1 addition & 1 deletion public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@
"analysis": "Analysis",
"analysisLanguage": "Analysis Language",
"semanticDomains": "Semantic Domains",
"semanticDomainsDefault": "(Default to browser language)",
"semanticDomainsDefault": "(Default to user-interface language)",
"languages": "Project Languages",
"bcp47": "BCP 47 Code",
"name": "Name",
Expand Down
Loading

0 comments on commit 3c646a2

Please sign in to comment.