Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion backend/FwLite/LcmCrdt.Tests/MiniLcmApiFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ await CrdtProjectsService.InitProjectDb(_crdtDbContext,
projectData);
await currentProjectService.RefreshProjectData();
// CreateProject would also seed morph types — so we need to do it manually here
await PreDefinedData.AddPredefinedMorphTypes(_services.ServiceProvider.GetRequiredService<DataModel>(), projectData.ClientId);
await PreDefinedData.AddPredefinedMorphTypes(_services.ServiceProvider.GetRequiredService<DataModel>(), projectData);
if (_seedWs)
{
await Api.CreateWritingSystem(new WritingSystem()
Expand Down
14 changes: 7 additions & 7 deletions backend/FwLite/LcmCrdt/CrdtProjectsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,9 @@ public virtual async Task<CrdtProject> CreateProject(CreateProjectRequest reques
// Morph types are predefined system data that must always exist — seed them
// unconditionally so they're available before AfterCreate (e.g. import) runs.
var dataModel = serviceScope.ServiceProvider.GetRequiredService<DataModel>();
await PreDefinedData.AddPredefinedMorphTypes(dataModel, projectData.ClientId);
await PreDefinedData.AddPredefinedMorphTypes(dataModel, projectData);
if (request.SeedNewProjectData)
await SeedSystemData(dataModel, projectData.ClientId);
await SeedSystemData(dataModel, projectData);
await (request.AfterCreate?.Invoke(serviceScope.ServiceProvider, crdtProject) ?? Task.CompletedTask);
}
catch (Exception e)
Expand Down Expand Up @@ -243,13 +243,13 @@ internal static async Task InitProjectDb(LcmCrdtDbContext db, ProjectData data)
await db.SaveChangesAsync();
}

internal static async Task SeedSystemData(DataModel dataModel, Guid clientId)
internal static async Task SeedSystemData(DataModel dataModel, ProjectData projectData)
{
// Note: AddPredefinedMorphTypes is seeded unconditionally in CreateProject, not here.
await PreDefinedData.AddPredefinedComplexFormTypes(dataModel, clientId);
await PreDefinedData.AddPredefinedPartsOfSpeech(dataModel, clientId);
await PreDefinedData.AddPredefinedSemanticDomains(dataModel, clientId);
await PreDefinedData.AddPredefinedCustomViews(dataModel, clientId);
await PreDefinedData.AddPredefinedComplexFormTypes(dataModel, projectData);
await PreDefinedData.AddPredefinedPartsOfSpeech(dataModel, projectData);
await PreDefinedData.AddPredefinedSemanticDomains(dataModel, projectData);
await PreDefinedData.AddPredefinedCustomViews(dataModel, projectData);
}

[GeneratedRegex("^[a-zA-Z0-9][a-zA-Z0-9-_]+$")]
Expand Down
6 changes: 3 additions & 3 deletions backend/FwLite/LcmCrdt/CurrentProjectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ async Task Execute()
// Seed morph-types if missing (for existing projects created before morph-type support).
// Must happen BEFORE FTS regeneration so headwords include morph-type tokens.
// (querying Commits instead of MorphTypes, because the commit may not be projected yet)
if (!await dbContext.Set<Commit>().AsNoTracking().AnyAsync(c => c.Id == PreDefinedData.MorphTypesSeedCommitId))
var projectData = await dbContext.ProjectData.AsNoTracking().FirstAsync();
if (!await dbContext.Set<Commit>().AsNoTracking().AnyAsync(c => c.Id == PreDefinedData.MorphTypesSeedCommitId(projectData.Id)))
{
var dataModel = services.GetRequiredService<DataModel>();
var projectData = await dbContext.ProjectData.AsNoTracking().FirstAsync();
await PreDefinedData.AddPredefinedMorphTypes(dataModel, projectData.ClientId);
await PreDefinedData.AddPredefinedMorphTypes(dataModel, projectData);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (EntrySearchServiceFactory is not null)
Expand Down
1 change: 1 addition & 0 deletions backend/FwLite/LcmCrdt/LcmCrdt.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<PackageReference Include="Refit" />
<PackageReference Include="Refit.HttpClientFactory" />
<PackageReference Include="UUIDNext" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::IsOsPlatform('linux'))">
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" />
Expand Down
51 changes: 34 additions & 17 deletions backend/FwLite/LcmCrdt/Objects/PreDefinedData.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using LcmCrdt.Changes;
using SIL.Harmony;
using UUIDNext;

namespace LcmCrdt.Objects;

Expand All @@ -15,20 +16,38 @@ public static class PreDefinedData
public static readonly Guid AdjectivePartOfSpeechId = new("30d07580-5052-4d91-bc24-469b8b2d7df9");
public static readonly Guid AdverbPartOfSpeechId = new("46e4fe08-ffa0-4c8b-bf98-2c56f38904d9");

internal static async Task AddPredefinedComplexFormTypes(DataModel dataModel, Guid clientId)
// Seed commit-ids are derived per-project (UUIDv5 namespaced on projectId) so each project
// owns its own row in LexBox's CrdtCommits table — a shared constant id would collide on the
// primary key and the seed would get attributed to whichever project pushed first.
public static Guid ComplexFormTypesSeedCommitId(Guid projectId) =>
Uuid.NewNameBased(projectId, "complex-form-types-seed");

public static Guid SemanticDomainsSeedCommitId(Guid projectId) =>
Uuid.NewNameBased(projectId, "semantic-domains-seed");

public static Guid PartsOfSpeechSeedCommitId(Guid projectId) =>
Uuid.NewNameBased(projectId, "parts-of-speech-seed");

public static Guid CustomViewsSeedCommitId(Guid projectId) =>
Uuid.NewNameBased(projectId, "custom-views-seed");

public static Guid MorphTypesSeedCommitId(Guid projectId) =>
Uuid.NewNameBased(projectId, "morph-types-seed");

internal static async Task AddPredefinedComplexFormTypes(DataModel dataModel, ProjectData projectData)
{
await dataModel.AddChanges(clientId,
await dataModel.AddChanges(projectData.ClientId,
[
new CreateComplexFormType(CompoundComplexFormTypeId, new MultiString() { { "en", "Compound" } } ),
new CreateComplexFormType(UnspecifiedComplexFormTypeId, new MultiString() { { "en", "Unspecified" } })
],
new Guid("dc60d2a9-0cc2-48ed-803c-a238a14b6eae"));
ComplexFormTypesSeedCommitId(projectData.Id));
}

internal static async Task AddPredefinedSemanticDomains(DataModel dataModel, Guid clientId)
internal static async Task AddPredefinedSemanticDomains(DataModel dataModel, ProjectData projectData)
{
//todo load from xml instead of hardcoding and use real IDs
await dataModel.AddChanges(clientId,
await dataModel.AddChanges(projectData.ClientId,
[
new CreateSemanticDomainChange(new Guid("63403699-07c1-43f3-a47c-069d6e4316e5"), new MultiString() { { "en", "Universe, Creation" } }, "1", true),
new CreateSemanticDomainChange(new Guid("999581c4-1611-4acb-ae1b-5e6c1dfe6f0c"), new MultiString() { { "en", "Sky" } }, "1.1", true),
Expand All @@ -38,25 +57,25 @@ await dataModel.AddChanges(clientId,
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d5"), new MultiString() { { "en", "Head" } }, "2.1.1", false),
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d6"), new MultiString() { { "en", "Eye" } }, "2.1.1.1", false),
],
new Guid("023faebb-711b-4d2f-a14f-a15621fc66bc"));
SemanticDomainsSeedCommitId(projectData.Id));
}

public static async Task AddPredefinedPartsOfSpeech(DataModel dataModel, Guid clientId)
public static async Task AddPredefinedPartsOfSpeech(DataModel dataModel, ProjectData projectData)
{
//todo load from xml instead of hardcoding
await dataModel.AddChanges(clientId,
await dataModel.AddChanges(projectData.ClientId,
[
new CreatePartOfSpeechChange(NounPartOfSpeechId, new MultiString() { { "en", "Noun" } }, true),
new CreatePartOfSpeechChange(VerbPartOfSpeechId, new MultiString() { { "en", "Verb" } }, true),
new CreatePartOfSpeechChange(AdjectivePartOfSpeechId, new MultiString() { { "en", "Adjective" } }, true),
new CreatePartOfSpeechChange(AdverbPartOfSpeechId, new MultiString() { { "en", "Adverb" } }, true),
],
new Guid("023faebb-711b-4d2f-b34f-a15621fc66bb"));
PartsOfSpeechSeedCommitId(projectData.Id));
}

internal static async Task AddPredefinedCustomViews(DataModel dataModel, Guid clientId)
internal static async Task AddPredefinedCustomViews(DataModel dataModel, ProjectData projectData)
{
await dataModel.AddChanges(clientId,
await dataModel.AddChanges(projectData.ClientId,
[
new CreateCustomViewChange(
new Guid("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
Expand All @@ -82,15 +101,13 @@ await dataModel.AddChanges(clientId,
Analysis = [new ViewWritingSystem { WsId = "en" }]
})
],
new Guid("b2c3d4e5-f6a7-8901-bcde-f12345678901"));
CustomViewsSeedCommitId(projectData.Id));
}

public static readonly Guid MorphTypesSeedCommitId = new("a7b2c3d4-e5f6-4a8b-9c0d-1e2f3a4b5c6d");

internal static async Task AddPredefinedMorphTypes(DataModel dataModel, Guid clientId)
internal static async Task AddPredefinedMorphTypes(DataModel dataModel, ProjectData projectData)
{
await dataModel.AddChanges(clientId,
await dataModel.AddChanges(projectData.ClientId,
[.. CanonicalMorphTypes.All.Values.Select(mt => new CreateMorphTypeChange(mt))],
MorphTypesSeedCommitId);
MorphTypesSeedCommitId(projectData.Id));
}
}
Loading