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

Add fwddata bridge Semantic domains and Parts of speech #886

Merged
merged 38 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3e1cd70
define parts of speech models in MiniLcm, implement tests and updates…
hahn-kev Jun 12, 2024
a74c983
remove duplicate field, fix overloaded GetEntries function causing bu…
hahn-kev Jun 14, 2024
e5e339a
add WritingSystemTests.cs, and make the project loader fixture a coll…
hahn-kev Jun 14, 2024
1921a1b
reduce allocations in ContributeExemplars by ~50% by using spans of c…
hahn-kev Jun 14, 2024
d3b25f2
use string contains with StringComparison instead of calling ToLowerI…
hahn-kev Jun 14, 2024
c351dcd
optimize ws lookup when populating exemplars by using a frozen dictio…
hahn-kev Jun 14, 2024
8ebf4f9
Populate UI with parts-of-speech
myieye Jun 18, 2024
2a7692b
Search improvements
myieye Jun 18, 2024
614dbd5
Index dictionary by first grapheme instead of first char
myieye Jun 20, 2024
dcf1970
Revert "Index dictionary by first grapheme instead of first char", be…
myieye Jun 20, 2024
ce8a06d
Fix exemplar lookup not always working
myieye Jun 20, 2024
cfebb61
Add loading indicators
myieye Jun 20, 2024
66504ac
Improve index character overlay size
myieye Jun 20, 2024
07794fb
Option type cleanup
myieye Jun 20, 2024
9834ea5
Add Sandbox to reproduce open bug
myieye Jun 20, 2024
abb1bac
Populate semantic domain dropdown (WIP)
myieye Jun 20, 2024
ddc08c8
Make entry list always fully visible.
myieye Jun 20, 2024
9e3f96c
Prevent unnecessary change events
myieye Jun 20, 2024
97cefeb
Fix type error
myieye Jun 20, 2024
9eba512
Use less peculiar icon
myieye Jun 20, 2024
ef7d13d
Display search keyboard shortcut
myieye Jun 20, 2024
ad568a2
Redesign home page
myieye Jun 20, 2024
e6e5572
Resize keys
myieye Jun 20, 2024
834925f
Fix Part of speech changes not being persisted
myieye Jun 21, 2024
08727e9
Uppercase index exemplars to prevent duplicates
myieye Jun 21, 2024
a9667a5
Store selected-entry, selected-index-char and search in URL
myieye Jun 21, 2024
1804a40
Add transition so increased debounce is not so noticeable.
myieye Jun 21, 2024
9a15ec6
Add save status indicator
myieye Jun 21, 2024
86af499
Layout and mobile fixes
myieye Jun 21, 2024
487d5ef
Cheap fix for semantic domain list being way to big
myieye Jun 21, 2024
f376895
Format selected semantic domains
myieye Jun 21, 2024
db913e8
gracefully handle null order by text
hahn-kev Jul 1, 2024
23add28
rework CrdtMultiOptionField to support editing object lists
hahn-kev Jul 1, 2024
4834db7
add parts of speech to CrdtLexboxApi.cs to fix failing tests
hahn-kev Jul 1, 2024
6506b4e
add parts of speech and semantic domains to the lf classic api
hahn-kev Jul 2, 2024
e75d5a0
disable server garbage collection to reduce memory usage. Disable sin…
hahn-kev Jul 2, 2024
c80bd6b
apply some suggested feedback from review.
hahn-kev Jul 4, 2024
169bad2
normalize exemplar to NFD before using it for comparison
hahn-kev Jul 5, 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
7 changes: 7 additions & 0 deletions LexBox.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crdt.Core", "backend\harmon
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FwDataMiniLcmBridge", "backend\FwDataMiniLcmBridge\FwDataMiniLcmBridge.csproj", "{279197B6-EC06-4DE0-94F8-625379C3AD83}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FwDataMiniLcmBridge.Tests", "backend\FwDataMiniLcmBridge.Tests\FwDataMiniLcmBridge.Tests.csproj", "{B0299A49-C0B2-4553-A72E-1670D4CB5138}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -101,6 +103,10 @@ Global
{279197B6-EC06-4DE0-94F8-625379C3AD83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{279197B6-EC06-4DE0-94F8-625379C3AD83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{279197B6-EC06-4DE0-94F8-625379C3AD83}.Release|Any CPU.Build.0 = Release|Any CPU
{B0299A49-C0B2-4553-A72E-1670D4CB5138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0299A49-C0B2-4553-A72E-1670D4CB5138}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0299A49-C0B2-4553-A72E-1670D4CB5138}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0299A49-C0B2-4553-A72E-1670D4CB5138}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{E8BB768B-C3DC-4BE6-9B9F-82319E05AF86} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
Expand All @@ -111,5 +117,6 @@ Global
{740C8FF5-8006-4047-8C52-53873C2DD7C4} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
{8B54FFB5-0BDF-403E-83CC-A3B3861EC507} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
{279197B6-EC06-4DE0-94F8-625379C3AD83} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
{B0299A49-C0B2-4553-A72E-1670D4CB5138} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
EndGlobalSection
EndGlobal
34 changes: 34 additions & 0 deletions backend/FwDataMiniLcmBridge.Tests/Fixtures/ProjectLoaderFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using FwDataMiniLcmBridge.Api;
using Microsoft.Extensions.DependencyInjection;

namespace FwDataMiniLcmBridge.Tests.Fixtures;

public class ProjectLoaderFixture : IDisposable
{
public const string Name = "ProjectLoaderCollection";
private readonly FwDataFactory _fwDataFactory;
private readonly ServiceProvider _serviceProvider;

public ProjectLoaderFixture()
{
//todo make mock of IProjectLoader so we can load from test projects
var provider = new ServiceCollection().AddFwDataBridge().BuildServiceProvider();
_serviceProvider = provider;
_fwDataFactory = provider.GetRequiredService<FwDataFactory>();
}

public FwDataMiniLcmApi CreateApi(string projectName)
{
return _fwDataFactory.GetFwDataMiniLcmApi(projectName, false);
}

public void Dispose()
{
_serviceProvider.Dispose();
}
}

[CollectionDefinition(ProjectLoaderFixture.Name)]
public class ProjectLoaderCollection : ICollectionFixture<ProjectLoaderFixture>
{
}
34 changes: 34 additions & 0 deletions backend/FwDataMiniLcmBridge.Tests/FwDataMiniLcmBridge.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
<PackageReference Include="xunit" Version="2.5.3"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit"/>
<Using Include="FluentAssertions"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FwDataMiniLcmBridge\FwDataMiniLcmBridge.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="TestData\" />
</ItemGroup>

</Project>
47 changes: 47 additions & 0 deletions backend/FwDataMiniLcmBridge.Tests/PartOfSpeechTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using FwDataMiniLcmBridge.Tests.Fixtures;
using MiniLcm;

namespace FwDataMiniLcmBridge.Tests;

[Collection(ProjectLoaderFixture.Name)]
public class PartOfSpeechTests(ProjectLoaderFixture fixture)
{
[Fact]
public async Task GetPartsOfSpeech_ReturnsAllPartsOfSpeech()
{
var api = fixture.CreateApi("sena-3");
var partOfSpeeches = await api.GetPartsOfSpeech().ToArrayAsync();
partOfSpeeches.Should().AllSatisfy(po => po.Id.Should().NotBe(Guid.Empty));
}

[Fact]
public async Task Sense_HasPartOfSpeech()
{
var api = fixture.CreateApi("sena-3");
var entry = await api.GetEntries().FirstAsync(e => e.Senses.Any(s => !string.IsNullOrEmpty(s.PartOfSpeech)));
var sense = entry.Senses.First(s => !string.IsNullOrEmpty(s.PartOfSpeech));
sense.PartOfSpeech.Should().NotBeNullOrEmpty();
sense.PartOfSpeechId.Should().NotBeNull();
}

[Fact]
public async Task Sense_UpdatesPartOfSpeech()
{
var api = fixture.CreateApi("sena-3");
var entry = await api.GetEntries().FirstAsync(e => e.Senses.Any(s => !string.IsNullOrEmpty(s.PartOfSpeech)));
var sense = entry.Senses.First(s => !string.IsNullOrEmpty(s.PartOfSpeech));
var newPartOfSpeech = await api.GetPartsOfSpeech().FirstAsync(po => po.Id != sense.PartOfSpeechId);

var update = api.CreateUpdateBuilder<Sense>()
.Set(s => s.PartOfSpeech, newPartOfSpeech.Name["en"])//this won't actually update the part of speech, but it shouldn't cause an issue either.
.Set(s => s.PartOfSpeechId, newPartOfSpeech.Id)
.Build();
await api.UpdateSense(entry.Id, sense.Id, update);

entry = await api.GetEntry(entry.Id);
ArgumentNullException.ThrowIfNull(entry);
var updatedSense = entry.Senses.First(s => s.Id == sense.Id);
updatedSense.PartOfSpeechId.Should().Be(newPartOfSpeech.Id);
updatedSense.PartOfSpeech.Should().NotBe(sense.PartOfSpeech);//the part of speech here is whatever the default is for the project, not english.
}
}
75 changes: 75 additions & 0 deletions backend/FwDataMiniLcmBridge.Tests/SemanticDomainTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using FwDataMiniLcmBridge.Tests.Fixtures;
using MiniLcm;

namespace FwDataMiniLcmBridge.Tests;

[Collection(ProjectLoaderFixture.Name)]
public class SemanticDomainTests(ProjectLoaderFixture fixture)
{
[Fact]
public async Task GetSemanticDomains_ReturnsAllSemanticDomains()
{
var api = fixture.CreateApi("sena-3");
var semanticDomains = await api.GetSemanticDomains().ToArrayAsync();
semanticDomains.Should().AllSatisfy(sd =>
{
sd.Id.Should().NotBe(Guid.Empty);
sd.Name.Values.Should().NotBeEmpty();
sd.Code.Should().NotBeEmpty();
});
}

[Fact]
public async Task Sense_HasSemanticDomains()
{
var api = fixture.CreateApi("sena-3");
var entry = await api.GetEntries().FirstAsync(e => e.Senses.Any(s => s.SemanticDomains.Any()));
var sense = entry.Senses.First(s => s.SemanticDomains.Any());
sense.SemanticDomains.Should().NotBeEmpty();
sense.SemanticDomains.Should().AllSatisfy(sd =>
{
sd.Id.Should().NotBe(Guid.Empty);
sd.Name.Values.Should().NotBeEmpty();
sd.Code.Should().NotBeEmpty();
});
}

[Fact]
public async Task Sense_AddSemanticDomain()
{
var api = fixture.CreateApi("sena-3");
var entry = await api.GetEntries().FirstAsync(e => e.Senses.Any(s => s.SemanticDomains.Any()));
var sense = entry.Senses.First(s => s.SemanticDomains.Any());
var currentSemanticDomain = sense.SemanticDomains.First();
var newSemanticDomain = await api.GetSemanticDomains().FirstAsync(sd => sd.Id != currentSemanticDomain.Id);

var update = api.CreateUpdateBuilder<Sense>()
.Add(s => s.SemanticDomains, newSemanticDomain)
.Build();
await api.UpdateSense(entry.Id, sense.Id, update);

entry = await api.GetEntry(entry.Id);
ArgumentNullException.ThrowIfNull(entry);
var updatedSense = entry.Senses.First(s => s.Id == sense.Id);
updatedSense.SemanticDomains.Select(sd => sd.Id).Should().Contain(newSemanticDomain.Id);
}

[Fact]
public async Task Sense_RemoveSemanticDomain()
{
var api = fixture.CreateApi("sena-3");
var entry = await api.GetEntries().FirstAsync(e => e.Senses.Any(s => s.SemanticDomains.Any()));
var sense = entry.Senses.First(s => s.SemanticDomains.Any());
var domainToRemove = sense.SemanticDomains[0];

var update = api.CreateUpdateBuilder<Sense>()
.Remove(s => s.SemanticDomains, 0)
.Build();
await api.UpdateSense(entry.Id, sense.Id, update);

entry = await api.GetEntry(entry.Id);
ArgumentNullException.ThrowIfNull(entry);
var updatedSense = entry.Senses.First(s => s.Id == sense.Id);
updatedSense.SemanticDomains.Select(sd => sd.Id).Should().NotContain(domainToRemove.Id);
}
}
22 changes: 22 additions & 0 deletions backend/FwDataMiniLcmBridge.Tests/WritingSystemTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using FwDataMiniLcmBridge.Tests.Fixtures;

namespace FwDataMiniLcmBridge.Tests;

[Collection(ProjectLoaderFixture.Name)]
public class WritingSystemTests(ProjectLoaderFixture fixture)
{
[Fact]
public async Task GetWritingSystems_DoesNotReturnNullOrEmpty()
{
var writingSystems = await fixture.CreateApi("sena-3").GetWritingSystems();
writingSystems.Vernacular.Should().NotBeNullOrEmpty();
writingSystems.Analysis.Should().NotBeNullOrEmpty();
}

[Fact]
public async Task GetWritingSystems_ReturnsExemplars()
{
var writingSystems = await fixture.CreateApi("sena-3").GetWritingSystems();
writingSystems.Vernacular.Should().Contain(ws => ws.Exemplars.Any());
}
}
Loading
Loading