diff --git a/backend/FwHeadless/FwHeadless.csproj b/backend/FwHeadless/FwHeadless.csproj
index 335b1e00b..82dcf0521 100644
--- a/backend/FwHeadless/FwHeadless.csproj
+++ b/backend/FwHeadless/FwHeadless.csproj
@@ -24,6 +24,8 @@
+
+
diff --git a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
index 7c400993c..50f2a2aa6 100644
--- a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
+++ b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
@@ -1,4 +1,4 @@
-using System.Collections.Frozen;
+using System.Collections.Frozen;
using System.Reflection;
using System.Text;
using FwDataMiniLcmBridge.Api.UpdateProxy;
@@ -246,6 +246,17 @@ public Task DeletePartOfSpeech(Guid id)
return Task.CompletedTask;
}
+ internal SemanticDomain FromLcmSemanticDomain(ICmSemanticDomain semanticDomain)
+ {
+ return new SemanticDomain
+ {
+ Id = semanticDomain.Guid,
+ Name = FromLcmMultiString(semanticDomain.Name),
+ Code = semanticDomain.Abbreviation.UiString ?? "",
+ Predefined = true, // TODO: Look up in a GUID list of predefined data
+ };
+ }
+
public IAsyncEnumerable GetSemanticDomains()
{
return
@@ -253,15 +264,16 @@ public IAsyncEnumerable GetSemanticDomains()
.AllInstances()
.OrderBy(p => p.Abbreviation.UiString)
.ToAsyncEnumerable()
- .Select(semanticDomain => new SemanticDomain
- {
- Id = semanticDomain.Guid,
- Name = FromLcmMultiString(semanticDomain.Name),
- Code = semanticDomain.Abbreviation.UiString ?? ""
- });
+ .Select(FromLcmSemanticDomain);
}
- public Task CreateSemanticDomain(SemanticDomain semanticDomain)
+ public Task GetSemanticDomain(Guid id)
+ {
+ var semDom = GetLcmSemanticDomain(id);
+ return Task.FromResult(semDom is null ? null : FromLcmSemanticDomain(semDom));
+ }
+
+ public async Task CreateSemanticDomain(SemanticDomain semanticDomain)
{
if (semanticDomain.Id == Guid.Empty) semanticDomain.Id = Guid.NewGuid();
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Semantic Domain",
@@ -273,8 +285,35 @@ public Task CreateSemanticDomain(SemanticDomain semanticDomain)
.Create(semanticDomain.Id, Cache.LangProject.SemanticDomainListOA);
lcmSemanticDomain.OcmCodes = semanticDomain.Code;
UpdateLcmMultiString(lcmSemanticDomain.Name, semanticDomain.Name);
+ // TODO: Find out if semantic domains are guaranteed to have an "en" writing system, or if we should use lcmCache.DefautlAnalWs instead
UpdateLcmMultiString(lcmSemanticDomain.Abbreviation, new MultiString(){{"en", semanticDomain.Code}});
});
+ return await GetSemanticDomain(semanticDomain.Id) ?? throw new InvalidOperationException("Semantic domain was not created");
+ }
+
+ public Task UpdateSemanticDomain(Guid id, UpdateObjectInput update)
+ {
+ var lcmSemanticDomain = SemanticDomainRepository.GetObject(id);
+ UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Update Semantic Domain",
+ "Revert Semantic Domain",
+ Cache.ServiceLocator.ActionHandler,
+ () =>
+ {
+ var updateProxy = new UpdateSemanticDomainProxy(lcmSemanticDomain, this);
+ update.Apply(updateProxy);
+ });
+ return Task.FromResult(FromLcmSemanticDomain(lcmSemanticDomain));
+ }
+
+ public Task DeleteSemanticDomain(Guid id)
+ {
+ UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Delete Semantic Domain",
+ "Revert delete",
+ Cache.ServiceLocator.ActionHandler,
+ () =>
+ {
+ SemanticDomainRepository.GetObject(id).Delete();
+ });
return Task.CompletedTask;
}
@@ -428,12 +467,7 @@ private Sense FromLexSense(ILexSense sense)
Definition = FromLcmMultiString(sense.Definition),
PartOfSpeech = sense.MorphoSyntaxAnalysisRA?.GetPartOfSpeech()?.Name.get_String(enWs).Text ?? "",
PartOfSpeechId = sense.MorphoSyntaxAnalysisRA?.GetPartOfSpeech()?.Guid,
- SemanticDomains = sense.SemanticDomainsRC.Select(s => new SemanticDomain
- {
- Id = s.Guid,
- Name = FromLcmMultiString(s.Name),
- Code = s.OcmCodes
- }).ToList(),
+ SemanticDomains = sense.SemanticDomainsRC.Select(FromLcmSemanticDomain).ToList(),
ExampleSentences = sense.ExamplesOS.Select(sentence => FromLexExampleSentence(sense.Guid, sentence)).ToList()
};
return s;
diff --git a/backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSemanticDomainProxy.cs b/backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSemanticDomainProxy.cs
new file mode 100644
index 000000000..a154c2951
--- /dev/null
+++ b/backend/FwLite/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSemanticDomainProxy.cs
@@ -0,0 +1,30 @@
+using System.Diagnostics.CodeAnalysis;
+using MiniLcm.Models;
+using SIL.LCModel;
+
+namespace FwDataMiniLcmBridge.Api.UpdateProxy;
+
+public class UpdateSemanticDomainProxy : SemanticDomain
+{
+ private readonly ICmSemanticDomain _lcmSemanticDomain;
+ private readonly FwDataMiniLcmApi _lexboxLcmApi;
+
+ public UpdateSemanticDomainProxy(ICmSemanticDomain lcmSemanticDomain, FwDataMiniLcmApi lexboxLcmApi)
+ {
+ _lcmSemanticDomain = lcmSemanticDomain;
+ Id = lcmSemanticDomain.Guid;
+ _lexboxLcmApi = lexboxLcmApi;
+ }
+
+ public override MultiString Name
+ {
+ get => new UpdateMultiStringProxy(_lcmSemanticDomain.Name, _lexboxLcmApi);
+ set => throw new NotImplementedException();
+ }
+
+ public override string Code
+ {
+ get => _lcmSemanticDomain.Abbreviation.BestAnalysisVernacularAlternative.Text;
+ set => throw new NotImplementedException();
+ }
+}
diff --git a/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs b/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
index 7c150891b..138390e28 100644
--- a/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
+++ b/backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
@@ -213,6 +213,85 @@ await crdtApi.CreateEntry(new Entry()
.For(e => e.ComplexForms).Exclude(c => c.Id));
}
+ [Fact]
+ public async Task SemanticDomainsSyncBothWays()
+ {
+ var crdtApi = _fixture.CrdtApi;
+ var fwdataApi = _fixture.FwDataApi;
+ await _syncService.Sync(crdtApi, fwdataApi);
+
+ var semdom3 = new SemanticDomain()
+ {
+ Id = new Guid("f4491f9b-3c5e-42ab-afc0-f22e19d0fff5"),
+ Name = new MultiString() { { "en", "Language and thought" } },
+ Code = "3",
+ Predefined = true,
+ };
+ await fwdataApi.CreateSemanticDomain(semdom3);
+
+ var semdom4 = new SemanticDomain()
+ {
+ Id = new Guid("62b4ae33-f3c2-447a-9ef7-7e41805b6a02"),
+ Name = new MultiString() { { "en", "Social behavior" } },
+ Code = "4",
+ Predefined = true,
+ };
+ await crdtApi.CreateSemanticDomain(semdom4);
+
+ await _syncService.Sync(crdtApi, fwdataApi);
+
+ var crdtSemanticDomains = await crdtApi.GetSemanticDomains().ToArrayAsync();
+ var fwdataSemanticDomains = await fwdataApi.GetSemanticDomains().ToArrayAsync();
+ crdtSemanticDomains.Should().ContainEquivalentOf(semdom3);
+ crdtSemanticDomains.Should().ContainEquivalentOf(semdom4);
+ fwdataSemanticDomains.Should().ContainEquivalentOf(semdom3);
+ fwdataSemanticDomains.Should().ContainEquivalentOf(semdom4);
+
+ crdtSemanticDomains.Should().BeEquivalentTo(fwdataSemanticDomains);
+ }
+
+ [Fact]
+ public async Task SemanticDomainsSyncInEntries()
+ {
+ var crdtApi = _fixture.CrdtApi;
+ var fwdataApi = _fixture.FwDataApi;
+ await _syncService.Sync(crdtApi, fwdataApi);
+
+ var semdom3 = new SemanticDomain()
+ {
+ Id = new Guid("f4491f9b-3c5e-42ab-afc0-f22e19d0fff5"),
+ Name = new MultiString() { { "en", "Language and thought" } },
+ Code = "3",
+ Predefined = true,
+ };
+ await fwdataApi.CreateSemanticDomain(semdom3);
+ // Note we do *not* call crdtApi.CreateSemanticDomain(semdom3);
+
+ await fwdataApi.CreateEntry(new Entry()
+ {
+ LexemeForm = { { "en", "Pear" } },
+ Senses =
+ [
+ new Sense() { Gloss = { { "en", "Pear" } }, SemanticDomains = [ semdom3 ] }
+ ]
+ });
+ await crdtApi.CreateEntry(new Entry()
+ {
+ LexemeForm = { { "en", "Banana" } },
+ Senses =
+ [
+ new Sense() { Gloss = { { "en", "Banana" } }, SemanticDomains = [ semdom3 ] }
+ ]
+ });
+ await _syncService.Sync(crdtApi, fwdataApi);
+
+ var crdtEntries = await crdtApi.GetEntries().ToArrayAsync();
+ var fwdataEntries = await fwdataApi.GetEntries().ToArrayAsync();
+ crdtEntries.Should().BeEquivalentTo(fwdataEntries,
+ options => options.For(e => e.Components).Exclude(c => c.Id)
+ .For(e => e.ComplexForms).Exclude(c => c.Id));
+ }
+
[Fact]
public async Task UpdatingAnEntryInEachProjectSyncsAcrossBoth()
{
diff --git a/backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs b/backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs
index 167c58fe7..12258dcee 100644
--- a/backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs
+++ b/backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs
@@ -28,7 +28,10 @@ public async Task Sync(IMiniLcmApi crdtApi, FwDataMiniLcmApi fwdataA
if (!dryRun)
{
await SaveProjectSnapshot(fwdataApi.Project.Name, fwdataApi.Project.ProjectsPath,
- new ProjectSnapshot(await fwdataApi.GetEntries().ToArrayAsync(), await fwdataApi.GetPartsOfSpeech().ToArrayAsync()));
+ new ProjectSnapshot(
+ await fwdataApi.GetEntries().ToArrayAsync(),
+ await fwdataApi.GetPartsOfSpeech().ToArrayAsync(),
+ await fwdataApi.GetSemanticDomains().ToArrayAsync()));
}
return result;
}
@@ -48,12 +51,16 @@ private async Task Sync(IMiniLcmApi crdtApi, IMiniLcmApi fwdataApi,
return new SyncResult(entryCount, 0);
}
- //todo sync complex form types, parts of speech, semantic domains, writing systems
+ //todo sync complex form types, writing systems
var currentFwDataPartsOfSpeech = await fwdataApi.GetPartsOfSpeech().ToArrayAsync();
var crdtChanges = await PartOfSpeechSync.Sync(currentFwDataPartsOfSpeech, projectSnapshot.PartsOfSpeech, crdtApi);
var fwdataChanges = await PartOfSpeechSync.Sync(await crdtApi.GetPartsOfSpeech().ToArrayAsync(), currentFwDataPartsOfSpeech, fwdataApi);
+ var currentFwDataSemanticDomains = await fwdataApi.GetSemanticDomains().ToArrayAsync();
+ crdtChanges += await SemanticDomainSync.Sync(currentFwDataSemanticDomains, projectSnapshot.SemanticDomains, crdtApi);
+ fwdataChanges += await SemanticDomainSync.Sync(await crdtApi.GetSemanticDomains().ToArrayAsync(), currentFwDataSemanticDomains, fwdataApi);
+
var currentFwDataEntries = await fwdataApi.GetEntries().ToArrayAsync();
crdtChanges += await EntrySync.Sync(currentFwDataEntries, projectSnapshot.Entries, crdtApi);
LogDryRun(crdtApi, "crdt");
@@ -77,7 +84,7 @@ private void LogDryRun(IMiniLcmApi api, string type)
logger.LogInformation($"Dry run {type} changes: {dryRunApi.DryRunRecords.Count}");
}
- public record ProjectSnapshot(Entry[] Entries, PartOfSpeech[] PartsOfSpeech);
+ public record ProjectSnapshot(Entry[] Entries, PartOfSpeech[] PartsOfSpeech, SemanticDomain[] SemanticDomains);
private async Task GetProjectSnapshot(string projectName, string? projectPath)
{
diff --git a/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs b/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
index d3b18caf8..959a22113 100644
--- a/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
+++ b/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
@@ -1,4 +1,4 @@
-using MiniLcm;
+using MiniLcm;
using MiniLcm.Models;
namespace FwLiteProjectSync;
@@ -49,6 +49,7 @@ public Task CreatePartOfSpeech(PartOfSpeech partOfSpeech)
DryRunRecords.Add(new DryRunRecord(nameof(CreatePartOfSpeech), $"Create part of speech {partOfSpeech.Name}"));
return Task.FromResult(partOfSpeech); // Since this is a dry run, api.GetPartOfSpeech would return null
}
+
public Task UpdatePartOfSpeech(Guid id, UpdateObjectInput update)
{
DryRunRecords.Add(new DryRunRecord(nameof(UpdatePartOfSpeech), $"Update part of speech {id}"));
@@ -66,10 +67,27 @@ public IAsyncEnumerable GetSemanticDomains()
return api.GetSemanticDomains();
}
- public Task CreateSemanticDomain(SemanticDomain semanticDomain)
+ public Task GetSemanticDomain(Guid id)
+ {
+ return api.GetSemanticDomain(id);
+ }
+
+ public Task CreateSemanticDomain(SemanticDomain semanticDomain)
{
DryRunRecords.Add(new DryRunRecord(nameof(CreateSemanticDomain),
$"Create semantic domain {semanticDomain.Name}"));
+ return Task.FromResult(semanticDomain);
+ }
+
+ public Task UpdateSemanticDomain(Guid id, UpdateObjectInput update)
+ {
+ DryRunRecords.Add(new DryRunRecord(nameof(UpdateSemanticDomain), $"Update part of speech {id}"));
+ return GetSemanticDomain(id)!;
+ }
+
+ public Task DeleteSemanticDomain(Guid id)
+ {
+ DryRunRecords.Add(new DryRunRecord(nameof(DeleteSemanticDomain), $"Delete part of speech {id}"));
return Task.CompletedTask;
}
diff --git a/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs b/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
index 22ebcd6c8..83261e1d7 100644
--- a/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
+++ b/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs
@@ -1,4 +1,4 @@
-using System.Linq.Expressions;
+using System.Linq.Expressions;
using SIL.Harmony;
using SIL.Harmony.Changes;
using LcmCrdt.Changes;
@@ -107,9 +107,29 @@ public async Task DeletePartOfSpeech(Guid id)
return SemanticDomains.AsAsyncEnumerable();
}
- public async Task CreateSemanticDomain(MiniLcm.Models.SemanticDomain semanticDomain)
+ public Task GetSemanticDomain(Guid id)
{
- await dataModel.AddChange(ClientId, new CreateSemanticDomainChange(semanticDomain.Id, semanticDomain.Name, semanticDomain.Code));
+ return SemanticDomains.FirstOrDefaultAsync(semdom => semdom.Id == id);
+ }
+
+ public async Task CreateSemanticDomain(MiniLcm.Models.SemanticDomain semanticDomain)
+ {
+ await dataModel.AddChange(ClientId, new CreateSemanticDomainChange(semanticDomain.Id, semanticDomain.Name, semanticDomain.Code, semanticDomain.Predefined));
+ return await GetSemanticDomain(semanticDomain.Id) ?? throw new NullReferenceException();
+ }
+
+ public async Task UpdateSemanticDomain(Guid id, UpdateObjectInput update)
+ {
+ var semDom = await GetSemanticDomain(id);
+ if (semDom is null) throw new NullReferenceException($"unable to find semantic domain with id {id}");
+
+ await dataModel.AddChanges(ClientId, [..semDom.ToChanges(update.Patch)]);
+ return await GetSemanticDomain(id) ?? throw new NullReferenceException();
+ }
+
+ public async Task DeleteSemanticDomain(Guid id)
+ {
+ await dataModel.AddChange(ClientId, new DeleteChange(id));
}
public async Task BulkImportSemanticDomains(IEnumerable semanticDomains)
diff --git a/backend/FwLite/LcmCrdt/Objects/JsonPatchChangeExtractor.cs b/backend/FwLite/LcmCrdt/Objects/JsonPatchChangeExtractor.cs
index 08757b4b6..9a9c4c409 100644
--- a/backend/FwLite/LcmCrdt/Objects/JsonPatchChangeExtractor.cs
+++ b/backend/FwLite/LcmCrdt/Objects/JsonPatchChangeExtractor.cs
@@ -1,4 +1,4 @@
-using LcmCrdt.Changes;
+using LcmCrdt.Changes;
using LcmCrdt.Changes.Entries;
using LcmCrdt.Utils;
using SIL.Harmony.Changes;
@@ -154,4 +154,10 @@ public static IEnumerable ToChanges(this PartOfSpeech pos, JsonPatchDoc
if (patch.Operations.Count > 0)
yield return new JsonPatchChange(pos.Id, patch);
}
+
+ public static IEnumerable ToChanges(this SemanticDomain semDom, JsonPatchDocument patch)
+ {
+ if (patch.Operations.Count > 0)
+ yield return new JsonPatchChange(semDom.Id, patch);
+ }
}
diff --git a/backend/FwLite/MiniLcm/IMiniLcmReadApi.cs b/backend/FwLite/MiniLcm/IMiniLcmReadApi.cs
index 9769f3705..de0bc543e 100644
--- a/backend/FwLite/MiniLcm/IMiniLcmReadApi.cs
+++ b/backend/FwLite/MiniLcm/IMiniLcmReadApi.cs
@@ -1,4 +1,4 @@
-using System.Text.Json.Serialization;
+using System.Text.Json.Serialization;
using MiniLcm.Models;
namespace MiniLcm;
@@ -13,6 +13,7 @@ public interface IMiniLcmReadApi
IAsyncEnumerable SearchEntries(string query, QueryOptions? options = null);
Task GetEntry(Guid id);
Task GetPartOfSpeech(Guid id);
+ Task GetSemanticDomain(Guid id);
}
public record QueryOptions(
diff --git a/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs b/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs
index db45680da..e925f8cd0 100644
--- a/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs
+++ b/backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs
@@ -1,4 +1,4 @@
-using System.Linq.Expressions;
+using System.Linq.Expressions;
using MiniLcm.Models;
using SystemTextJsonPatch;
@@ -13,10 +13,18 @@ Task UpdateWritingSystem(WritingSystemId id,
UpdateObjectInput update);
+ #region PartOfSpeech
Task CreatePartOfSpeech(PartOfSpeech partOfSpeech);
Task UpdatePartOfSpeech(Guid id, UpdateObjectInput update);
Task DeletePartOfSpeech(Guid id);
- Task CreateSemanticDomain(SemanticDomain semanticDomain);
+ #endregion
+
+ #region SemanticDomain
+ Task CreateSemanticDomain(SemanticDomain semanticDomain);
+ Task UpdateSemanticDomain(Guid id, UpdateObjectInput update);
+ Task DeleteSemanticDomain(Guid id);
+ #endregion
+
Task CreateComplexFormType(ComplexFormType complexFormType);
#region Entry
diff --git a/backend/FwLite/MiniLcm/Models/SemanticDomain.cs b/backend/FwLite/MiniLcm/Models/SemanticDomain.cs
index 616af0adb..7d144831b 100644
--- a/backend/FwLite/MiniLcm/Models/SemanticDomain.cs
+++ b/backend/FwLite/MiniLcm/Models/SemanticDomain.cs
@@ -1,10 +1,10 @@
-namespace MiniLcm.Models;
+namespace MiniLcm.Models;
public class SemanticDomain : IObjectWithId
{
- public virtual required Guid Id { get; set; }
- public virtual required MultiString Name { get; set; }
- public virtual required string Code { get; set; }
+ public virtual Guid Id { get; set; }
+ public virtual MultiString Name { get; set; } = new();
+ public virtual string Code { get; set; } = string.Empty;
public DateTimeOffset? DeletedAt { get; set; }
public bool Predefined { get; set; }
diff --git a/backend/FwLite/MiniLcm/SyncHelpers/SemanticDomainSync.cs b/backend/FwLite/MiniLcm/SyncHelpers/SemanticDomainSync.cs
new file mode 100644
index 000000000..1219db9aa
--- /dev/null
+++ b/backend/FwLite/MiniLcm/SyncHelpers/SemanticDomainSync.cs
@@ -0,0 +1,47 @@
+using MiniLcm;
+using MiniLcm.Models;
+using MiniLcm.SyncHelpers;
+using SystemTextJsonPatch;
+
+public static class SemanticDomainSync
+{
+ public static async Task Sync(SemanticDomain[] currentSemanticDomains,
+ SemanticDomain[] previousSemanticDomains,
+ IMiniLcmApi api)
+ {
+ return await DiffCollection.Diff(api,
+ previousSemanticDomains,
+ currentSemanticDomains,
+ pos => pos.Id,
+ async (api, currentPos) =>
+ {
+ await api.CreateSemanticDomain(currentPos);
+ return 1;
+ },
+ async (api, previousPos) =>
+ {
+ await api.DeleteSemanticDomain(previousPos.Id);
+ return 1;
+ },
+ async (api, previousPos, currentPos) =>
+ {
+ var updateObjectInput = SemanticDomainDiffToUpdate(previousPos, currentPos);
+ if (updateObjectInput is not null) await api.UpdateSemanticDomain(currentPos.Id, updateObjectInput);
+ return updateObjectInput is null ? 0 : 1;
+ });
+ }
+
+ public static UpdateObjectInput? SemanticDomainDiffToUpdate(SemanticDomain previousSemanticDomain, SemanticDomain currentSemanticDomain)
+ {
+ JsonPatchDocument patchDocument = new();
+ patchDocument.Operations.AddRange(MultiStringDiff.GetMultiStringDiff(nameof(SemanticDomain.Name),
+ previousSemanticDomain.Name,
+ currentSemanticDomain.Name));
+ // TODO: Once we add abbreviations to MiniLcm's SemanticDomain objects, then:
+ // patchDocument.Operations.AddRange(GetMultiStringDiff(nameof(SemanticDomain.Abbreviation),
+ // previousSemanticDomain.Abbreviation,
+ // currentSemanticDomain.Abbreviation));
+ if (patchDocument.Operations.Count == 0) return null;
+ return new UpdateObjectInput(patchDocument);
+ }
+}
diff --git a/backend/LfClassicData/LfClassicMiniLcmApi.cs b/backend/LfClassicData/LfClassicMiniLcmApi.cs
index 2bc264260..e762a5440 100644
--- a/backend/LfClassicData/LfClassicMiniLcmApi.cs
+++ b/backend/LfClassicData/LfClassicMiniLcmApi.cs
@@ -78,15 +78,7 @@ public async IAsyncEnumerable GetPartsOfSpeech()
foreach (var item in optionListItems)
{
- yield return new PartOfSpeech
- {
- Id = item.Guid ?? Guid.Empty,
- Name = new MultiString
- {
- { "en", item.Value ?? item.Abbreviation ?? string.Empty },
- { "__key", item.Key ?? string.Empty } // The key is all that senses have on them, so we need it client-side to find the display name
- }
- };
+ yield return ToPartOfSpeech(item);
}
}
@@ -95,9 +87,19 @@ public async IAsyncEnumerable GetPartsOfSpeech()
return await GetPartsOfSpeech().FirstOrDefaultAsync(pos => pos.Id == id);
}
- public IAsyncEnumerable GetSemanticDomains()
+ public async IAsyncEnumerable GetSemanticDomains()
{
- return AsyncEnumerable.Empty();
+ var optionListItems = await dbContext.GetOptionListItems(projectCode, "semantic-domain-ddp4");
+
+ foreach (var item in optionListItems)
+ {
+ yield return ToSemanticDomain(item);
+ }
+ }
+
+ public async Task GetSemanticDomain(Guid id)
+ {
+ return await GetSemanticDomains().FirstOrDefaultAsync(semdom => semdom.Id == id);
}
public IAsyncEnumerable GetEntries(QueryOptions? options = null)
@@ -276,6 +278,36 @@ private static MultiString ToMultiString(Dictionary? multiText
return ms;
}
+ private static PartOfSpeech ToPartOfSpeech(Entities.OptionListItem item)
+ {
+ return new PartOfSpeech
+ {
+ Id = item.Guid ?? Guid.Empty,
+ Name = new MultiString
+ {
+ { "en", item.Value ?? item.Abbreviation ?? string.Empty },
+ { "__key", item.Key ?? string.Empty } // The key is all that senses have on them, so we need it client-side to find the display name
+ },
+ // TODO: Abbreviation
+ Predefined = false,
+ };
+ }
+
+ private static SemanticDomain ToSemanticDomain(Entities.OptionListItem item)
+ {
+ // TODO: Needs testing against actual LF testlangproj data
+ return new SemanticDomain
+ {
+ Id = item.Guid ?? Guid.Empty,
+ Name = new MultiString
+ {
+ { "en", item.Value ?? item.Abbreviation ?? string.Empty },
+ { "__key", item.Key ?? string.Empty } // The key is all that senses have on them, so we need it client-side to find the display name
+ },
+ Predefined = false,
+ };
+ }
+
public async Task GetEntry(Guid id)
{
var entry = await Entries.Find(e => e.Guid == id).FirstOrDefaultAsync();