Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export semantic domains in English #2948

Merged
merged 30 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
134b974
Export semantic domains in English
imnasnainaec Feb 14, 2024
4b67aeb
Display sem doms in proj's sem dom lang
imnasnainaec Feb 14, 2024
03d7609
Fix bug
imnasnainaec Feb 14, 2024
b1176f7
Add GetAllSemanticDomainNames controller test
imnasnainaec Feb 15, 2024
801774c
Add SenseCard tests
imnasnainaec Feb 15, 2024
5b8f51e
Remove unused import
imnasnainaec Feb 15, 2024
6ff3584
Fix punctuation
imnasnainaec Feb 15, 2024
a6732f7
Make unified ProjectSettingProps
imnasnainaec Feb 15, 2024
11db41e
Retract mistaken change
imnasnainaec Feb 15, 2024
8062beb
Tidy
imnasnainaec Feb 15, 2024
d2ffdb8
Unify sem dom name updating in a wrapper component
imnasnainaec Feb 16, 2024
faf11eb
Roll back DomainChip changes
imnasnainaec Feb 16, 2024
c20aa65
Fall back to ui lang if proj sem dom lang not specified
imnasnainaec Feb 19, 2024
14f6b31
Fix tests
imnasnainaec Feb 19, 2024
52220fa
Simplify new tests
imnasnainaec Feb 22, 2024
e134357
[ProjectActions] Clean up tests
imnasnainaec Feb 22, 2024
882eb06
Trigger sem dom update when ui lang or proj changes
imnasnainaec Feb 23, 2024
1aa5bf2
Merge branch 'master' into sem-dom-lang
imnasnainaec Feb 23, 2024
001688d
Avoid some unnecessary sem-dom updates
imnasnainaec Feb 26, 2024
a9ab823
Revert unneeded change
imnasnainaec Feb 26, 2024
5e8d112
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 5, 2024
d6de404
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 8, 2024
06dc02f
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 8, 2024
fd5ef18
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 20, 2024
c6c5e86
Fix line missed in merge
imnasnainaec Mar 20, 2024
33cc167
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 21, 2024
6224711
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 21, 2024
d160892
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 25, 2024
32b2536
Merge branch 'master' into sem-dom-lang
imnasnainaec Mar 26, 2024
12bc104
Clean up double-negative
imnasnainaec Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading