From b88aa5878f4622147cbd511e5be562f2e35a5184 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 26 Sep 2024 16:35:53 +0700 Subject: [PATCH 1/8] DB migration for FlexModelVersion metadata column --- .../LexCore/Entities/FlexProjectMetadata.cs | 1 + ..._AddModelVersionToFlexMetadata.Designer.cs | 1402 +++++++++++++++++ ...926093530_AddModelVersionToFlexMetadata.cs | 28 + .../LexBoxDbContextModelSnapshot.cs | 3 + 4 files changed, 1434 insertions(+) create mode 100644 backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.Designer.cs create mode 100644 backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.cs diff --git a/backend/LexCore/Entities/FlexProjectMetadata.cs b/backend/LexCore/Entities/FlexProjectMetadata.cs index 9c5ba9bb3..4ef16a564 100644 --- a/backend/LexCore/Entities/FlexProjectMetadata.cs +++ b/backend/LexCore/Entities/FlexProjectMetadata.cs @@ -10,6 +10,7 @@ public class FlexProjectMetadata /// public Guid? LangProjectId { get; set; } public ProjectWritingSystems? WritingSystems { get; set; } + public int? FlexModelVersion { get; set; } } public class ProjectWritingSystems diff --git a/backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.Designer.cs b/backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.Designer.cs new file mode 100644 index 000000000..2201989b0 --- /dev/null +++ b/backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.Designer.cs @@ -0,0 +1,1402 @@ +// +using System; +using System.Collections.Generic; +using LexData; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LexData.Migrations +{ + [DbContext(typeof(LexBoxDbContext))] + [Migration("20240926093530_AddModelVersionToFlexMetadata")] + partial class AddModelVersionToFlexMetadata + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:case_insensitive", "und-u-ks-level2,und-u-ks-level2,icu,False") + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzBlobTrigger", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("TriggerName") + .HasColumnType("text") + .HasColumnName("trigger_name"); + + b.Property("TriggerGroup") + .HasColumnType("text") + .HasColumnName("trigger_group"); + + b.Property("BlobData") + .HasColumnType("bytea") + .HasColumnName("blob_data"); + + b.HasKey("SchedulerName", "TriggerName", "TriggerGroup"); + + b.ToTable("qrtz_blob_triggers", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzCalendar", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("CalendarName") + .HasColumnType("text") + .HasColumnName("calendar_name"); + + b.Property("Calendar") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("calendar"); + + b.HasKey("SchedulerName", "CalendarName"); + + b.ToTable("qrtz_calendars", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzCronTrigger", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("TriggerName") + .HasColumnType("text") + .HasColumnName("trigger_name"); + + b.Property("TriggerGroup") + .HasColumnType("text") + .HasColumnName("trigger_group"); + + b.Property("CronExpression") + .IsRequired() + .HasColumnType("text") + .HasColumnName("cron_expression"); + + b.Property("TimeZoneId") + .HasColumnType("text") + .HasColumnName("time_zone_id"); + + b.HasKey("SchedulerName", "TriggerName", "TriggerGroup"); + + b.ToTable("qrtz_cron_triggers", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzFiredTrigger", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("EntryId") + .HasColumnType("text") + .HasColumnName("entry_id"); + + b.Property("FiredTime") + .HasColumnType("bigint") + .HasColumnName("fired_time"); + + b.Property("InstanceName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("instance_name"); + + b.Property("IsNonConcurrent") + .HasColumnType("bool") + .HasColumnName("is_nonconcurrent"); + + b.Property("JobGroup") + .HasColumnType("text") + .HasColumnName("job_group"); + + b.Property("JobName") + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("RequestsRecovery") + .HasColumnType("bool") + .HasColumnName("requests_recovery"); + + b.Property("ScheduledTime") + .HasColumnType("bigint") + .HasColumnName("sched_time"); + + b.Property("State") + .IsRequired() + .HasColumnType("text") + .HasColumnName("state"); + + b.Property("TriggerGroup") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trigger_group"); + + b.Property("TriggerName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trigger_name"); + + b.HasKey("SchedulerName", "EntryId"); + + b.HasIndex("InstanceName") + .HasDatabaseName("idx_qrtz_ft_trig_inst_name"); + + b.HasIndex("JobGroup") + .HasDatabaseName("idx_qrtz_ft_job_group"); + + b.HasIndex("JobName") + .HasDatabaseName("idx_qrtz_ft_job_name"); + + b.HasIndex("RequestsRecovery") + .HasDatabaseName("idx_qrtz_ft_job_req_recovery"); + + b.HasIndex("TriggerGroup") + .HasDatabaseName("idx_qrtz_ft_trig_group"); + + b.HasIndex("TriggerName") + .HasDatabaseName("idx_qrtz_ft_trig_name"); + + b.HasIndex("SchedulerName", "TriggerName", "TriggerGroup") + .HasDatabaseName("idx_qrtz_ft_trig_nm_gp"); + + b.ToTable("qrtz_fired_triggers", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzJobDetail", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("JobName") + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("JobGroup") + .HasColumnType("text") + .HasColumnName("job_group"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("IsDurable") + .HasColumnType("bool") + .HasColumnName("is_durable"); + + b.Property("IsNonConcurrent") + .HasColumnType("bool") + .HasColumnName("is_nonconcurrent"); + + b.Property("IsUpdateData") + .HasColumnType("bool") + .HasColumnName("is_update_data"); + + b.Property("JobClassName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_class_name"); + + b.Property("JobData") + .HasColumnType("bytea") + .HasColumnName("job_data"); + + b.Property("RequestsRecovery") + .HasColumnType("bool") + .HasColumnName("requests_recovery"); + + b.HasKey("SchedulerName", "JobName", "JobGroup"); + + b.HasIndex("RequestsRecovery") + .HasDatabaseName("idx_qrtz_j_req_recovery"); + + b.ToTable("qrtz_job_details", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzLock", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("LockName") + .HasColumnType("text") + .HasColumnName("lock_name"); + + b.HasKey("SchedulerName", "LockName"); + + b.ToTable("qrtz_locks", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzPausedTriggerGroup", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("TriggerGroup") + .HasColumnType("text") + .HasColumnName("trigger_group"); + + b.HasKey("SchedulerName", "TriggerGroup"); + + b.ToTable("qrtz_paused_trigger_grps", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSchedulerState", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("InstanceName") + .HasColumnType("text") + .HasColumnName("instance_name"); + + b.Property("CheckInInterval") + .HasColumnType("bigint") + .HasColumnName("checkin_interval"); + + b.Property("LastCheckInTime") + .HasColumnType("bigint") + .HasColumnName("last_checkin_time"); + + b.HasKey("SchedulerName", "InstanceName"); + + b.ToTable("qrtz_scheduler_state", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimplePropertyTrigger", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("TriggerName") + .HasColumnType("text") + .HasColumnName("trigger_name"); + + b.Property("TriggerGroup") + .HasColumnType("text") + .HasColumnName("trigger_group"); + + b.Property("BooleanProperty1") + .HasColumnType("bool") + .HasColumnName("bool_prop_1"); + + b.Property("BooleanProperty2") + .HasColumnType("bool") + .HasColumnName("bool_prop_2"); + + b.Property("DecimalProperty1") + .HasColumnType("numeric") + .HasColumnName("dec_prop_1"); + + b.Property("DecimalProperty2") + .HasColumnType("numeric") + .HasColumnName("dec_prop_2"); + + b.Property("IntegerProperty1") + .HasColumnType("integer") + .HasColumnName("int_prop_1"); + + b.Property("IntegerProperty2") + .HasColumnType("integer") + .HasColumnName("int_prop_2"); + + b.Property("LongProperty1") + .HasColumnType("bigint") + .HasColumnName("long_prop_1"); + + b.Property("LongProperty2") + .HasColumnType("bigint") + .HasColumnName("long_prop_2"); + + b.Property("StringProperty1") + .HasColumnType("text") + .HasColumnName("str_prop_1"); + + b.Property("StringProperty2") + .HasColumnType("text") + .HasColumnName("str_prop_2"); + + b.Property("StringProperty3") + .HasColumnType("text") + .HasColumnName("str_prop_3"); + + b.Property("TimeZoneId") + .HasColumnType("text") + .HasColumnName("time_zone_id"); + + b.HasKey("SchedulerName", "TriggerName", "TriggerGroup"); + + b.ToTable("qrtz_simprop_triggers", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimpleTrigger", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("TriggerName") + .HasColumnType("text") + .HasColumnName("trigger_name"); + + b.Property("TriggerGroup") + .HasColumnType("text") + .HasColumnName("trigger_group"); + + b.Property("RepeatCount") + .HasColumnType("bigint") + .HasColumnName("repeat_count"); + + b.Property("RepeatInterval") + .HasColumnType("bigint") + .HasColumnName("repeat_interval"); + + b.Property("TimesTriggered") + .HasColumnType("bigint") + .HasColumnName("times_triggered"); + + b.HasKey("SchedulerName", "TriggerName", "TriggerGroup"); + + b.ToTable("qrtz_simple_triggers", "quartz"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", b => + { + b.Property("SchedulerName") + .HasColumnType("text") + .HasColumnName("sched_name"); + + b.Property("TriggerName") + .HasColumnType("text") + .HasColumnName("trigger_name"); + + b.Property("TriggerGroup") + .HasColumnType("text") + .HasColumnName("trigger_group"); + + b.Property("CalendarName") + .HasColumnType("text") + .HasColumnName("calendar_name"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("EndTime") + .HasColumnType("bigint") + .HasColumnName("end_time"); + + b.Property("JobData") + .HasColumnType("bytea") + .HasColumnName("job_data"); + + b.Property("JobGroup") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_group"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("MisfireInstruction") + .HasColumnType("smallint") + .HasColumnName("misfire_instr"); + + b.Property("NextFireTime") + .HasColumnType("bigint") + .HasColumnName("next_fire_time"); + + b.Property("PreviousFireTime") + .HasColumnType("bigint") + .HasColumnName("prev_fire_time"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("StartTime") + .HasColumnType("bigint") + .HasColumnName("start_time"); + + b.Property("TriggerState") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trigger_state"); + + b.Property("TriggerType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trigger_type"); + + b.HasKey("SchedulerName", "TriggerName", "TriggerGroup"); + + b.HasIndex("NextFireTime") + .HasDatabaseName("idx_qrtz_t_next_fire_time"); + + b.HasIndex("TriggerState") + .HasDatabaseName("idx_qrtz_t_state"); + + b.HasIndex("NextFireTime", "TriggerState") + .HasDatabaseName("idx_qrtz_t_nft_st"); + + b.HasIndex("SchedulerName", "JobName", "JobGroup"); + + b.ToTable("qrtz_triggers", "quartz"); + }); + + modelBuilder.Entity("LexCore.Entities.DraftProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsConfidential") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OrgId") + .HasColumnType("uuid"); + + b.Property("ProjectManagerId") + .HasColumnType("uuid"); + + b.Property("RetentionPolicy") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("ProjectManagerId"); + + b.ToTable("DraftProjects"); + }); + + modelBuilder.Entity("LexCore.Entities.FlexProjectMetadata", b => + { + b.Property("ProjectId") + .HasColumnType("uuid"); + + b.Property("FlexModelVersion") + .HasColumnType("integer"); + + b.Property("LangProjectId") + .HasColumnType("uuid"); + + b.Property("LexEntryCount") + .HasColumnType("integer"); + + b.HasKey("ProjectId"); + + b.ToTable("FlexProjectMetadata"); + }); + + modelBuilder.Entity("LexCore.Entities.OrgMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrgId") + .HasColumnType("uuid"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("UpdatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrgId"); + + b.HasIndex("UserId", "OrgId") + .IsUnique(); + + b.ToTable("OrgMembers", (string)null); + }); + + modelBuilder.Entity("LexCore.Entities.OrgProjects", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrgId") + .HasColumnType("uuid"); + + b.Property("ProjectId") + .HasColumnType("uuid"); + + b.Property("UpdatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("OrgId", "ProjectId") + .IsUnique(); + + b.ToTable("OrgProjects"); + }); + + modelBuilder.Entity("LexCore.Entities.Organization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Orgs", (string)null); + }); + + modelBuilder.Entity("LexCore.Entities.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsConfidential") + .HasColumnType("boolean"); + + b.Property("LastCommit") + .HasColumnType("timestamp with time zone"); + + b.Property("MigratedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("ProjectOrigin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("ResetStatus") + .HasColumnType("integer"); + + b.Property("RetentionPolicy") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("ParentId"); + + b.ToTable("Projects"); + }); + + modelBuilder.Entity("LexCore.Entities.ProjectUsers", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("ProjectId") + .HasColumnType("uuid"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("UpdatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId", "ProjectId") + .IsUnique(); + + b.ToTable("ProjectUsers"); + }); + + modelBuilder.Entity("LexCore.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CanCreateProjects") + .HasColumnType("boolean"); + + b.Property("CreatedById") + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Email") + .HasColumnType("text") + .UseCollation("case_insensitive"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("GoogleId") + .HasColumnType("text"); + + b.Property("IsAdmin") + .HasColumnType("boolean"); + + b.Property("LastActive") + .HasColumnType("timestamp with time zone"); + + b.Property("LocalizationCode") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("en"); + + b.Property("Locked") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("PasswordStrength") + .HasColumnType("integer"); + + b.Property("Salt") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Username") + .HasColumnType("text") + .UseCollation("case_insensitive"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("SIL.Harmony.Core.ServerCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectId") + .HasColumnType("uuid"); + + b.ComplexProperty>("HybridDateTime", "SIL.Harmony.Core.ServerCommit.HybridDateTime#HybridDateTime", b1 => + { + b1.IsRequired(); + + b1.Property("Counter") + .HasColumnType("bigint"); + + b1.Property("DateTime") + .HasColumnType("timestamp with time zone"); + }); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("CrdtCommits", (string)null); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzBlobTrigger", b => + { + b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger") + .WithMany("BlobTriggers") + .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Trigger"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzCronTrigger", b => + { + b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger") + .WithMany("CronTriggers") + .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Trigger"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimplePropertyTrigger", b => + { + b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger") + .WithMany("SimplePropertyTriggers") + .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Trigger"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzSimpleTrigger", b => + { + b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", "Trigger") + .WithMany("SimpleTriggers") + .HasForeignKey("SchedulerName", "TriggerName", "TriggerGroup") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Trigger"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", b => + { + b.HasOne("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzJobDetail", "JobDetail") + .WithMany("Triggers") + .HasForeignKey("SchedulerName", "JobName", "JobGroup") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("JobDetail"); + }); + + modelBuilder.Entity("LexCore.Entities.DraftProject", b => + { + b.HasOne("LexCore.Entities.User", "ProjectManager") + .WithMany() + .HasForeignKey("ProjectManagerId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ProjectManager"); + }); + + modelBuilder.Entity("LexCore.Entities.FlexProjectMetadata", b => + { + b.HasOne("LexCore.Entities.Project", null) + .WithOne("FlexProjectMetadata") + .HasForeignKey("LexCore.Entities.FlexProjectMetadata", "ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("LexCore.Entities.ProjectWritingSystems", "WritingSystems", b1 => + { + b1.Property("FlexProjectMetadataProjectId") + .HasColumnType("uuid"); + + b1.HasKey("FlexProjectMetadataProjectId"); + + b1.ToTable("FlexProjectMetadata"); + + b1.ToJson("WritingSystems"); + + b1.WithOwner() + .HasForeignKey("FlexProjectMetadataProjectId"); + + b1.OwnsMany("LexCore.Entities.FLExWsId", "AnalysisWss", b2 => + { + b2.Property("ProjectWritingSystemsFlexProjectMetadataProjectId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + b2.Property("IsActive") + .HasColumnType("boolean"); + + b2.Property("IsDefault") + .HasColumnType("boolean"); + + b2.Property("Tag") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("ProjectWritingSystemsFlexProjectMetadataProjectId", "Id"); + + b2.ToTable("FlexProjectMetadata"); + + b2.WithOwner() + .HasForeignKey("ProjectWritingSystemsFlexProjectMetadataProjectId"); + }); + + b1.OwnsMany("LexCore.Entities.FLExWsId", "VernacularWss", b2 => + { + b2.Property("ProjectWritingSystemsFlexProjectMetadataProjectId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + b2.Property("IsActive") + .HasColumnType("boolean"); + + b2.Property("IsDefault") + .HasColumnType("boolean"); + + b2.Property("Tag") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("ProjectWritingSystemsFlexProjectMetadataProjectId", "Id"); + + b2.ToTable("FlexProjectMetadata"); + + b2.WithOwner() + .HasForeignKey("ProjectWritingSystemsFlexProjectMetadataProjectId"); + }); + + b1.Navigation("AnalysisWss"); + + b1.Navigation("VernacularWss"); + }); + + b.Navigation("WritingSystems"); + }); + + modelBuilder.Entity("LexCore.Entities.OrgMember", b => + { + b.HasOne("LexCore.Entities.Organization", "Organization") + .WithMany("Members") + .HasForeignKey("OrgId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LexCore.Entities.User", "User") + .WithMany("Organizations") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LexCore.Entities.OrgProjects", b => + { + b.HasOne("LexCore.Entities.Organization", "Org") + .WithMany() + .HasForeignKey("OrgId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LexCore.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Org"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("LexCore.Entities.Project", b => + { + b.HasOne("LexCore.Entities.Project", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("LexCore.Entities.ProjectUsers", b => + { + b.HasOne("LexCore.Entities.Project", "Project") + .WithMany("Users") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LexCore.Entities.User", "User") + .WithMany("Projects") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LexCore.Entities.User", b => + { + b.HasOne("LexCore.Entities.User", "CreatedBy") + .WithMany("UsersICreated") + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CreatedBy"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("SIL.Harmony.Core.ServerCommit", b => + { + b.HasOne("LexCore.Entities.FlexProjectMetadata", null) + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsMany("SIL.Harmony.Core.ChangeEntity", "ChangeEntities", b1 => + { + b1.Property("ServerCommitId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + b1.Property("Change") + .HasColumnType("text"); + + b1.Property("CommitId") + .HasColumnType("uuid"); + + b1.Property("EntityId") + .HasColumnType("uuid"); + + b1.Property("Index") + .HasColumnType("integer"); + + b1.HasKey("ServerCommitId", "Id"); + + b1.ToTable("CrdtCommits"); + + b1.ToJson("ChangeEntities"); + + b1.WithOwner() + .HasForeignKey("ServerCommitId"); + }); + + b.Navigation("ChangeEntities"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzJobDetail", b => + { + b.Navigation("Triggers"); + }); + + modelBuilder.Entity("AppAny.Quartz.EntityFrameworkCore.Migrations.QuartzTrigger", b => + { + b.Navigation("BlobTriggers"); + + b.Navigation("CronTriggers"); + + b.Navigation("SimplePropertyTriggers"); + + b.Navigation("SimpleTriggers"); + }); + + modelBuilder.Entity("LexCore.Entities.Organization", b => + { + b.Navigation("Members"); + }); + + modelBuilder.Entity("LexCore.Entities.Project", b => + { + b.Navigation("FlexProjectMetadata"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("LexCore.Entities.User", b => + { + b.Navigation("Organizations"); + + b.Navigation("Projects"); + + b.Navigation("UsersICreated"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.cs b/backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.cs new file mode 100644 index 000000000..e498be055 --- /dev/null +++ b/backend/LexData/Migrations/20240926093530_AddModelVersionToFlexMetadata.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LexData.Migrations +{ + /// + public partial class AddModelVersionToFlexMetadata : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FlexModelVersion", + table: "FlexProjectMetadata", + type: "integer", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FlexModelVersion", + table: "FlexProjectMetadata"); + } + } +} diff --git a/backend/LexData/Migrations/LexBoxDbContextModelSnapshot.cs b/backend/LexData/Migrations/LexBoxDbContextModelSnapshot.cs index f1b32c53c..daef50c7a 100644 --- a/backend/LexData/Migrations/LexBoxDbContextModelSnapshot.cs +++ b/backend/LexData/Migrations/LexBoxDbContextModelSnapshot.cs @@ -525,6 +525,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ProjectId") .HasColumnType("uuid"); + b.Property("FlexModelVersion") + .HasColumnType("integer"); + b.Property("LangProjectId") .HasColumnType("uuid"); From 9c276b9000179ccb7d40cd87b93f567edd96f538 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 26 Sep 2024 16:38:39 +0700 Subject: [PATCH 2/8] Add API to get FLEx model version from repo --- backend/LexBoxApi/Services/HgService.cs | 16 ++++++++++++++++ backend/LexCore/ServiceInterfaces/IHgService.cs | 1 + hgweb/command-runner.sh | 6 +++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/LexBoxApi/Services/HgService.cs b/backend/LexBoxApi/Services/HgService.cs index b3d3f956c..7df47938d 100644 --- a/backend/LexBoxApi/Services/HgService.cs +++ b/backend/LexBoxApi/Services/HgService.cs @@ -267,6 +267,22 @@ private string[] GetWsList(System.Xml.XmlElement root, string tagName) return null; } + public async Task GetModelVersionOfFlexProject(ProjectCode code, CancellationToken token = default) + { + var result = await ExecuteHgCommandServerCommand(code, "flexmodelversion", token); + var text = await result.ReadAsStringAsync(token); + try + { + var json = JsonDocument.Parse(text); + return json.RootElement.GetProperty("modelversion").GetInt32(); + } + catch + { + if (int.TryParse(text, out var num)) return num; + } + return null; + } + public Task RevertRepo(ProjectCode code, string revHash) { throw new NotImplementedException(); diff --git a/backend/LexCore/ServiceInterfaces/IHgService.cs b/backend/LexCore/ServiceInterfaces/IHgService.cs index df2426bde..488e59fa9 100644 --- a/backend/LexCore/ServiceInterfaces/IHgService.cs +++ b/backend/LexCore/ServiceInterfaces/IHgService.cs @@ -13,6 +13,7 @@ public interface IHgService Task SoftDeleteRepo(ProjectCode code, string deletedRepoSuffix); Task GetProjectWritingSystems(ProjectCode code, CancellationToken token = default); Task GetProjectIdOfFlexProject(ProjectCode code, CancellationToken token = default); + Task GetModelVersionOfFlexProject(ProjectCode code, CancellationToken token = default); BackupExecutor? BackupRepo(ProjectCode code); Task ResetRepo(ProjectCode code); Task FinishReset(ProjectCode code, Stream zipFile); diff --git a/hgweb/command-runner.sh b/hgweb/command-runner.sh index ac0e35d3a..60e6f8abf 100644 --- a/hgweb/command-runner.sh +++ b/hgweb/command-runner.sh @@ -1,7 +1,7 @@ #!/bin/bash # Define the list of allowed commands -allowed_commands=("verify" "tip" "tipdate" "wesaylexentrycount" "lexentrycount" "flexprojectid" "flexwritingsystems" "recover" "healthz" "invalidatedircache") +allowed_commands=("verify" "tip" "tipdate" "wesaylexentrycount" "lexentrycount" "flexprojectid" "flexwritingsystems" "flexmodelversion" "recover" "healthz" "invalidatedircache") # Get the project code and command name from the URL IFS='/' read -ra PATH_SEGMENTS <<< "$PATH_INFO" @@ -74,6 +74,10 @@ case $command_name in chg cat -r tip General/LanguageProject.langproj | sed -n -e '//,/<\/AnalysisWss>/p' -e '//,/<\/VernWss>/p' -e '//,/<\/CurAnalysisWss>/p' -e '//,/<\/CurVernWss>/p' -e '//,/<\/CurPronunWss>/p' ;; + flexmodelversion) + chg cat -r tip FLExProject.ModelVersion + ;; + tip) chg tip --template '{node}' ;; From 3b5ee77c2b6dbfc19fc93469a17930b2f8dd17bd Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 26 Sep 2024 16:40:11 +0700 Subject: [PATCH 3/8] Add GraphQL mutation to update FLEx model version --- backend/LexBoxApi/GraphQL/ProjectMutations.cs | 19 +++++++++++++++++++ backend/LexBoxApi/Services/ProjectService.cs | 12 ++++++++++++ frontend/schema.graphql | 17 ++++++++++++++++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/backend/LexBoxApi/GraphQL/ProjectMutations.cs b/backend/LexBoxApi/GraphQL/ProjectMutations.cs index 7e7cd16af..4c39e029b 100644 --- a/backend/LexBoxApi/GraphQL/ProjectMutations.cs +++ b/backend/LexBoxApi/GraphQL/ProjectMutations.cs @@ -412,6 +412,25 @@ public async Task> UpdateLangProjectId(string code, return dbContext.Projects.Where(p => p.Id == projectId); } + [Error] + [Error] + [Error] + [UseMutationConvention] + [UseFirstOrDefault] + [UseProjection] + public async Task> UpdateFLExModelVersion(string code, + IPermissionService permissionService, + [Service] ProjectService projectService, + LexBoxDbContext dbContext) + { + var projectId = await projectService.LookupProjectId(code); + await permissionService.AssertCanManageProject(projectId); + var project = await dbContext.Projects.FindAsync(projectId); + NotFoundException.ThrowIfNull(project); + await projectService.UpdateFLExModelVersion(projectId); + return dbContext.Projects.Where(p => p.Id == projectId); + } + [Error] [Error] [UseMutationConvention] diff --git a/backend/LexBoxApi/Services/ProjectService.cs b/backend/LexBoxApi/Services/ProjectService.cs index 363db5641..6c89ea5a4 100644 --- a/backend/LexBoxApi/Services/ProjectService.cs +++ b/backend/LexBoxApi/Services/ProjectService.cs @@ -81,6 +81,18 @@ public async Task UpdateProjectLangProjectId(Guid projectId) await dbContext.SaveChangesAsync(); } + public async Task UpdateFLExModelVersion(Guid projectId) + { + var project = await dbContext.Projects.FindAsync(projectId); + if (project is null || project.Type != ProjectType.FLEx) return; + await dbContext.Entry(project).Reference(p => p.FlexProjectMetadata).LoadAsync(); + var modelVersion = await hgService.GetModelVersionOfFlexProject(project.Code); + if (modelVersion is null) return; + project.FlexProjectMetadata ??= new FlexProjectMetadata(); + project.FlexProjectMetadata.FlexModelVersion = modelVersion; + await dbContext.SaveChangesAsync(); + } + public async Task CreateDraftProject(CreateProjectInput input) { // No need for a transaction if we're just saving a single item diff --git a/frontend/schema.graphql b/frontend/schema.graphql index 652eff98a..d6e2b97cd 100644 --- a/frontend/schema.graphql +++ b/frontend/schema.graphql @@ -1,4 +1,4 @@ -schema { +schema { query: Query mutation: Mutation } @@ -179,6 +179,7 @@ type FlexProjectMetadata { lexEntryCount: Int langProjectId: UUID writingSystems: ProjectWritingSystems + flexModelVersion: Int } type InvalidEmailError implements Error { @@ -246,6 +247,7 @@ type Mutation { updateProjectLexEntryCount(input: UpdateProjectLexEntryCountInput!): UpdateProjectLexEntryCountPayload! updateProjectLanguageList(input: UpdateProjectLanguageListInput!): UpdateProjectLanguageListPayload! updateLangProjectId(input: UpdateLangProjectIdInput!): UpdateLangProjectIdPayload! + updateFLExModelVersion(input: UpdateFLExModelVersionInput!): UpdateFLExModelVersionPayload! leaveProject(input: LeaveProjectInput!): LeaveProjectPayload! removeProjectMember(input: RemoveProjectMemberInput!): RemoveProjectMemberPayload! deleteDraftProject(input: DeleteDraftProjectInput!): DeleteDraftProjectPayload! @authorize(policy: "AdminRequiredPolicy") @@ -478,6 +480,11 @@ type UniqueValueError implements Error { message: String! } +type UpdateFLExModelVersionPayload { + project: Project + errors: [UpdateFLExModelVersionError!] +} + type UpdateLangProjectIdPayload { project: Project errors: [UpdateLangProjectIdError!] @@ -588,6 +595,8 @@ union SetUserLockedError = NotFoundError union SoftDeleteProjectError = NotFoundError | DbError +union UpdateFLExModelVersionError = NotFoundError | DbError | UnauthorizedAccessError + union UpdateLangProjectIdError = NotFoundError | DbError | UnauthorizedAccessError union UpdateProjectLanguageListError = NotFoundError | DbError | UnauthorizedAccessError @@ -769,12 +778,14 @@ input FlexProjectMetadataFilterInput { lexEntryCount: IntOperationFilterInput langProjectId: UuidOperationFilterInput writingSystems: ProjectWritingSystemsFilterInput + flexModelVersion: IntOperationFilterInput } input FlexProjectMetadataSortInput { projectId: SortEnumType lexEntryCount: SortEnumType langProjectId: SortEnumType + flexModelVersion: SortEnumType } input IntOperationFilterInput { @@ -1041,6 +1052,10 @@ input StringOperationFilterInput { ieq: String } +input UpdateFLExModelVersionInput { + code: String! +} + input UpdateLangProjectIdInput { code: String! } From 30c2c2014e21478ab2602d67be368d86f71abc49 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 27 Sep 2024 12:04:20 +0700 Subject: [PATCH 4/8] Show model version (to managers only) on project page Regular users won't be shown the model version because it's a little too technical, but project managers (and org & site admins) can see it. --- frontend/src/lib/i18n/locales/en.json | 1 + .../project/[project_code]/+page.svelte | 25 ++++++++++++++++- .../project/[project_code]/+page.ts | 28 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/i18n/locales/en.json b/frontend/src/lib/i18n/locales/en.json index edb2ceb17..ce0918109 100644 --- a/frontend/src/lib/i18n/locales/en.json +++ b/frontend/src/lib/i18n/locales/en.json @@ -420,6 +420,7 @@ the [Linguistics Institute at Payap University](https://li.payap.ac.th/) in Chia "description": "Description", "created_at": "Created", "last_commit": "Last Commit", + "model_version": "Model Version", "vernacular_langs": "Vernacular writing systems", "analysis_langs": "Analysis writing systems", "organization": { diff --git a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte index d12a1b808..6d132e283 100644 --- a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte +++ b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte @@ -5,7 +5,7 @@ import FormatRetentionPolicy from '$lib/components/FormatRetentionPolicy.svelte'; import HgLogView from '$lib/components/HgLogView.svelte'; import DeleteModal from '$lib/components/modals/DeleteModal.svelte'; - import t, { date, number } from '$lib/i18n'; + import t, { date, NULL_LABEL, number } from '$lib/i18n'; import { z } from 'zod'; import type { PageData } from './$types'; import { @@ -14,6 +14,7 @@ _deleteProjectUser, _leaveProject, _removeProjectFromOrg, + _updateFLExModelVersion, _updateProjectLanguageList, _updateProjectLexEntryCount, type ProjectUser, @@ -70,6 +71,7 @@ }); $: lexEntryCount = project.flexProjectMetadata?.lexEntryCount; + $: flexModelVersion = project.flexProjectMetadata?.flexModelVersion; $: vernacularLangTags = project.flexProjectMetadata?.writingSystems?.vernacularWss; $: analysisLangTags = project.flexProjectMetadata?.writingSystems?.analysisWss; @@ -98,6 +100,13 @@ loadingEntryCount = false; } + let loadingModelVersion = false; + async function updateModelVersion(): Promise { + loadingModelVersion = true; + await _updateFLExModelVersion(project.code); + loadingModelVersion = false; + } + let loadingLanguageList = false; async function updateLanguageList(): Promise { loadingLanguageList = true; @@ -379,6 +388,20 @@ {/if} + + {#if project.type === ProjectType.FlEx && canManage} + + + + {/if} {#if project.type === ProjectType.FlEx} diff --git a/frontend/src/routes/(authenticated)/project/[project_code]/+page.ts b/frontend/src/routes/(authenticated)/project/[project_code]/+page.ts index 55b11a081..cde428c3c 100644 --- a/frontend/src/routes/(authenticated)/project/[project_code]/+page.ts +++ b/frontend/src/routes/(authenticated)/project/[project_code]/+page.ts @@ -91,6 +91,7 @@ export async function load(event: PageLoadEvent) { } flexProjectMetadata { lexEntryCount + flexModelVersion writingSystems { vernacularWss { tag @@ -304,6 +305,33 @@ export async function _updateProjectLexEntryCount(code: string): $OpResult { // $OpResult { + //language=GraphQL + const result = await getClient() + .mutation( + graphql(` + mutation UpdateFLExModelVersion($input: UpdateFLExModelVersionInput!) { + updateFLExModelVersion(input: $input) { + project { + id + flexProjectMetadata { + flexModelVersion + } + } + errors { + __typename + ... on Error { + message + } + } + } + } + `), + { input: { code } }, + ); + return result; +} + export async function _updateLangProjectId(code: string): $OpResult { //language=GraphQL const result = await getClient() From d9df3ea93bba2a3ea4882dae2aa13bd0e30b5e92 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Mon, 30 Sep 2024 10:16:06 +0700 Subject: [PATCH 5/8] Show FLEx version instead of model version And show it to everyone instead of just managers. --- .../Projects/FlexModelVersionText.svelte | 31 +++++++++++++++++++ .../project/[project_code]/+page.svelte | 25 ++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 frontend/src/lib/components/Projects/FlexModelVersionText.svelte diff --git a/frontend/src/lib/components/Projects/FlexModelVersionText.svelte b/frontend/src/lib/components/Projects/FlexModelVersionText.svelte new file mode 100644 index 000000000..62523f50a --- /dev/null +++ b/frontend/src/lib/components/Projects/FlexModelVersionText.svelte @@ -0,0 +1,31 @@ + + + + +{fwModel} diff --git a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte index 6d132e283..5d3e28589 100644 --- a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte +++ b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte @@ -53,6 +53,7 @@ import WritingSystemList from '$lib/components/Projects/WritingSystemList.svelte'; import { onMount } from 'svelte'; import { getSearchParamValues } from '$lib/util/query-params'; + import FlexModelVersionText from '$lib/components/Projects/FlexModelVersionText.svelte'; export let data: PageData; $: user = data.user; @@ -389,17 +390,19 @@ {/if} - {#if project.type === ProjectType.FlEx && canManage} - - + {#if project.type === ProjectType.FlEx} + + + + + {/if} {#if project.type === ProjectType.FlEx} From 088fb71bd95de25b751404372fc8d72c422a9abd Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Mon, 30 Sep 2024 10:57:58 +0700 Subject: [PATCH 6/8] Use single quotes in JS/Svelte files --- .../Projects/FlexModelVersionText.svelte | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/frontend/src/lib/components/Projects/FlexModelVersionText.svelte b/frontend/src/lib/components/Projects/FlexModelVersionText.svelte index 62523f50a..816e2a9b1 100644 --- a/frontend/src/lib/components/Projects/FlexModelVersionText.svelte +++ b/frontend/src/lib/components/Projects/FlexModelVersionText.svelte @@ -3,29 +3,29 @@ const versionLookupTable: Record = { 0: NULL_LABEL, - 7000058: "FieldWorks 7.2", - 7000059: "FieldWorks 7.2", - 7000060: "FieldWorks 7.2", - 7000061: "FieldWorks 7.2", - 7000062: "FieldWorks 7.3", - 7000063: "FieldWorks 7.3", - 7000064: "FieldWorks 7.3", - 7000065: "FieldWorks 7.3", - 7000066: "FieldWorks 8.0", - 7000067: "FieldWorks 8.0", - 7000068: "FieldWorks 8.0-8.2", - 7000069: "FieldWorks 8.2", - 7000070: "FieldWorks 8.3", - 7000071: "FieldWorks 8.3", - 7000072: "FieldWorks 9", + 7000058: 'FieldWorks 7.2', + 7000059: 'FieldWorks 7.2', + 7000060: 'FieldWorks 7.2', + 7000061: 'FieldWorks 7.2', + 7000062: 'FieldWorks 7.3', + 7000063: 'FieldWorks 7.3', + 7000064: 'FieldWorks 7.3', + 7000065: 'FieldWorks 7.3', + 7000066: 'FieldWorks 8.0', + 7000067: 'FieldWorks 8.0', + 7000068: 'FieldWorks 8.0-8.2', + 7000069: 'FieldWorks 8.2', + 7000070: 'FieldWorks 8.3', + 7000071: 'FieldWorks 8.3', + 7000072: 'FieldWorks 9', } {fwModel} From 21d089e28d7e219894408e3821af70636ed90256 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Mon, 30 Sep 2024 11:00:33 +0700 Subject: [PATCH 7/8] Remove unused import --- .../routes/(authenticated)/project/[project_code]/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte index 5d3e28589..be2335f77 100644 --- a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte +++ b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte @@ -5,7 +5,7 @@ import FormatRetentionPolicy from '$lib/components/FormatRetentionPolicy.svelte'; import HgLogView from '$lib/components/HgLogView.svelte'; import DeleteModal from '$lib/components/modals/DeleteModal.svelte'; - import t, { date, NULL_LABEL, number } from '$lib/i18n'; + import t, { date, number } from '$lib/i18n'; import { z } from 'zod'; import type { PageData } from './$types'; import { From 59733e0bbd5e9de013e29e67d305ba0e6e6a253f Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Tue, 15 Oct 2024 09:43:11 +0700 Subject: [PATCH 8/8] Address review comments --- backend/LexBoxApi/GraphQL/ProjectMutations.cs | 2 -- backend/LexBoxApi/Services/HgService.cs | 12 ++---------- .../components/Projects/FlexModelVersionText.svelte | 2 +- .../project/[project_code]/+page.svelte | 1 - 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/backend/LexBoxApi/GraphQL/ProjectMutations.cs b/backend/LexBoxApi/GraphQL/ProjectMutations.cs index 4c39e029b..3ceeeb163 100644 --- a/backend/LexBoxApi/GraphQL/ProjectMutations.cs +++ b/backend/LexBoxApi/GraphQL/ProjectMutations.cs @@ -425,8 +425,6 @@ public async Task> UpdateFLExModelVersion(string code, { var projectId = await projectService.LookupProjectId(code); await permissionService.AssertCanManageProject(projectId); - var project = await dbContext.Projects.FindAsync(projectId); - NotFoundException.ThrowIfNull(project); await projectService.UpdateFLExModelVersion(projectId); return dbContext.Projects.Where(p => p.Id == projectId); } diff --git a/backend/LexBoxApi/Services/HgService.cs b/backend/LexBoxApi/Services/HgService.cs index 7df47938d..6a48f3c16 100644 --- a/backend/LexBoxApi/Services/HgService.cs +++ b/backend/LexBoxApi/Services/HgService.cs @@ -271,16 +271,8 @@ private string[] GetWsList(System.Xml.XmlElement root, string tagName) { var result = await ExecuteHgCommandServerCommand(code, "flexmodelversion", token); var text = await result.ReadAsStringAsync(token); - try - { - var json = JsonDocument.Parse(text); - return json.RootElement.GetProperty("modelversion").GetInt32(); - } - catch - { - if (int.TryParse(text, out var num)) return num; - } - return null; + var json = JsonDocument.Parse(text); + return json.RootElement.GetProperty("modelversion").GetInt32(); } public Task RevertRepo(ProjectCode code, string revHash) diff --git a/frontend/src/lib/components/Projects/FlexModelVersionText.svelte b/frontend/src/lib/components/Projects/FlexModelVersionText.svelte index 816e2a9b1..5adde58e2 100644 --- a/frontend/src/lib/components/Projects/FlexModelVersionText.svelte +++ b/frontend/src/lib/components/Projects/FlexModelVersionText.svelte @@ -28,4 +28,4 @@ $: fwModel = versionLookupTable[modelVersion] ?? 'Unknown FieldWorks version'; -{fwModel} +{fwModel} diff --git a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte index be2335f77..a0531bbc1 100644 --- a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte +++ b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte @@ -389,7 +389,6 @@ {/if} - {#if project.type === ProjectType.FlEx}