فهرست منبع

Getting rid of QAForm

Kenric Nugteren 1 هفته پیش
والد
کامیت
90587b2c7e

+ 1 - 0
prs.shared/Database Update Scripts/DatabaseUpdateScripts.cs

@@ -64,5 +64,6 @@ public static class DatabaseUpdateScripts
         DataUpdater.RegisterUpdateScript<Update_8_49>();
         DataUpdater.RegisterUpdateScript<Update_8_49>();
         DataUpdater.RegisterUpdateScript<Update_8_55>();
         DataUpdater.RegisterUpdateScript<Update_8_55>();
         DataUpdater.RegisterUpdateScript<Update_8_57>();
         DataUpdater.RegisterUpdateScript<Update_8_57>();
+        DataUpdater.RegisterUpdateScript<Update_8_58>();
     }
     }
 }
 }

+ 0 - 1
prs.shared/Database Update Scripts/Update_7_34.cs

@@ -375,7 +375,6 @@ namespace PRS.Shared
             }
             }
 
 
             DigitalForm.SerializeFormData(instance, save);
             DigitalForm.SerializeFormData(instance, save);
-            
 
 
             return instance.IsChanged();
             return instance.IsChanged();
         }
         }

+ 239 - 1
prs.shared/Database Update Scripts/Update_8_58.cs

@@ -26,6 +26,11 @@ internal class Update_8_58 : DatabaseUpdateScript
                 .Add(x => x.Processed)
                 .Add(x => x.Processed)
                 .Add(x => x.Posted))
                 .Add(x => x.Posted))
             .ToArray<TimeSheet>();
             .ToArray<TimeSheet>();
+        if(timeSheets.Length == 0)
+        {
+            Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed -> TimeSheet.Posted: Done");
+            return;
+        }
         Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed -> TimeSheet.Posted: {timeSheets.Length} items");
         Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed -> TimeSheet.Posted: {timeSheets.Length} items");
         Utils.Utils.ProcessInChunks(
         Utils.Utils.ProcessInChunks(
             timeSheets,
             timeSheets,
@@ -38,7 +43,7 @@ internal class Update_8_58 : DatabaseUpdateScript
                 provider.Save(chunk);
                 provider.Save(chunk);
             },
             },
             200,
             200,
-            percentage => Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed: {percentage:F2}%");
+            percentage => Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed: {percentage:F2}%"));
     }
     }
 
 
     private static void ConvertLink<T, TFromLink, TToLink>(IProvider provider, Expression<Func<T, TFromLink>> fromLink, Expression<Func<T, TToLink>> toLink)
     private static void ConvertLink<T, TFromLink, TToLink>(IProvider provider, Expression<Func<T, TFromLink>> fromLink, Expression<Func<T, TToLink>> toLink)
@@ -59,6 +64,11 @@ internal class Update_8_58 : DatabaseUpdateScript
                 .Add(fromColumn)
                 .Add(fromColumn)
                 .Add(toColumn))
                 .Add(toColumn))
             .ToArray<T>();
             .ToArray<T>();
+        if(items.Length == 0)
+        {
+            Logger.Send(LogType.Information, "", $"Migrating {typeof(T).Name}.{fromColumn.Property} -> {typeof(T).Name}.{toColumn.Property}: Done");
+            return;
+        }
         Logger.Send(LogType.Information, "", $"Migrating {typeof(T).Name}.{fromColumn.Property} -> {typeof(T).Name}.{toColumn.Property}: {items.Length} items");
         Logger.Send(LogType.Information, "", $"Migrating {typeof(T).Name}.{fromColumn.Property} -> {typeof(T).Name}.{toColumn.Property}: {items.Length} items");
 
 
         Utils.Utils.ProcessInChunks(
         Utils.Utils.ProcessInChunks(
@@ -83,6 +93,232 @@ internal class Update_8_58 : DatabaseUpdateScript
         ConvertLink<T, TLink, TLink>(provider, fromLink, toLink);
         ConvertLink<T, TLink, TLink>(provider, fromLink, toLink);
     }
     }
 
 
+    private static string QuoteString(string str)
+    {
+        return $"\"{str.Replace("\\", "\\\\").Replace("\"", "\\\"")}\"";
+    }
+
+    private static void ConvertQAQuestions(IProvider provider)
+    {
+        var qaQuestions = provider.Query(
+            Filter<QAQuestion>.Where(x => x.Converted).IsEqualTo(false),
+            Columns.None<QAQuestion>()
+                .Add(x => x.ID)
+                .Add(x => x.Converted)
+                .Add(x => x.Code)
+                .Add(x => x.Question)
+                .Add(x => x.Section)
+                .Add(x => x.Description)
+                .Add(x => x.Answer)
+                .Add(x => x.Parameters)
+                .Add(x => x.Sequence)
+                .Add(x => x.Form.ID))
+            .ToObjects<QAQuestion>()
+            .GroupBy(x => x.Form.ID)
+            .Select(x => new
+            {
+                FormID = x.Key,
+                Questions = x.OrderBy(x => x.Sequence).ToList()
+            })
+            .ToList();
+        Logger.Send(LogType.Information, "", $"Converting {qaQuestions.Count} QA forms into DigitalForms");
+        var formSequences = provider.Query(
+            Filter<DigitalFormVariable>.Where(x => x.Form.ID)
+                .InList(qaQuestions.ToArray(x => x.FormID)),
+            Columns.None<DigitalFormVariable>()
+                .Add(x => x.Form.ID)
+                .Add(x => x.Sequence))
+            .ToObjects<DigitalFormVariable>()
+            .GroupBy(x => x.Form.ID)
+            .ToDictionary(x => x.Key, x => x.Max(x => x.Sequence));
+
+        var variableMappings = new Dictionary<Guid, Dictionary<Guid, string>>();
+
+        var variables = new List<DigitalFormVariable>();
+        var layouts = new List<DigitalFormLayout>();
+        foreach(var item in qaQuestions)
+        {
+            if(formSequences.TryGetValue(item.FormID, out var sequence))
+            {
+                sequence++;
+            }
+            else
+            {
+                sequence = 0;
+            }
+
+            var layout = new DFLayout();
+            layout.ColumnWidths.Add("Auto");
+            layout.ColumnWidths.Add("Auto");
+            layout.ColumnWidths.Add("*");
+
+            var codes = new HashSet<string>();
+            string GenerateCode(string code)
+            {
+                var originalCode = code;
+                var i = 1;
+                while (codes.Contains(code))
+                {
+                    code = $"{originalCode}{i}";
+                    ++i;
+                }
+                return code;
+            }
+
+            var mappings = variableMappings.GetValueOrAdd(item.FormID);
+
+            var i = 1;
+            foreach(var question in item.Questions)
+            {
+                layout.RowHeights.Add("Auto");
+                var row = layout.RowHeights.Count;
+
+                if(question.Answer == QAAnswer.Comment)
+                {
+                    var label = new DFLayoutLabel { Caption = question.Question, Row = row, Column = 1, ColumnSpan = 3 };
+                    label.Style.HorizontalTextAlignment = DFLayoutAlignment.Middle;
+                    layout.Elements.Add(label);
+                }
+                else
+                {
+                    var rowNum = new DFLayoutLabel { Caption = i.ToString(), Row = row, Column = 1 };
+                    var label = new DFLayoutLabel { Caption = question.Question, Row = row, Column = 2 };
+                    layout.Elements.Add(rowNum);
+                    layout.Elements.Add(label);
+
+                    var variable = new DigitalFormVariable();
+                    variable.Form.CopyFrom(question.Form);
+                    variable.Sequence = sequence++;
+
+                    DFLayoutFieldProperties properties;
+                    Type fieldType;
+
+                    var code = GenerateCode(question.Code.NotWhiteSpaceOr(question.Answer.ToString()));
+
+                    var parameters = question.ParseParameters();
+                    switch (question.Answer)
+                    {
+                        case QAAnswer.Choice:
+                            {
+                                // ColourExpression
+
+                                var buttons = parameters["Options"].Split(',').ToArray(x => x.Trim());
+                                var colors = parameters["Colors"].Split(',').ToArray(x => x.Trim());
+                                var defValue = parameters["Default"].Trim();
+
+                                fieldType = typeof(DFLayoutOptionField);
+                                var optionProperties = new DFLayoutOptionFieldProperties();
+                                properties = optionProperties;
+
+                                optionProperties.Default = defValue;
+                                optionProperties.OptionType = DFLayoutOptionType.Buttons;
+                                optionProperties.Options = DFLayoutOptionFieldProperties.WriteOptions(buttons);
+
+                                var colourExpression = "null";
+                                foreach(var (option, colour) in buttons.Zip(colors))
+                                {
+                                    colourExpression = $"If([{code}] == {QuoteString(option)}, {QuoteString(colour)}, {colourExpression})";
+                                }
+                                optionProperties.ColourExpression = colourExpression;
+                            }
+                            break;
+                        case QAAnswer.Number:
+                            {
+                                var defValue = parameters["Default"];
+
+                                fieldType = typeof(DFLayoutDoubleField);
+                                var doubleProperties = new DFLayoutDoubleFieldProperties();
+                                properties = doubleProperties;
+
+                                doubleProperties.Default = double.TryParse(defValue, out var d) ? d : default;
+                            }
+                            break;
+                        case QAAnswer.Text:
+                            {
+                                var defValue = parameters["Default"];
+
+                                fieldType = typeof(DFLayoutStringField);
+                                var stringProperties = new DFLayoutStringFieldProperties();
+                                properties = stringProperties;
+
+                                stringProperties.Default = defValue;
+                            }
+                            break;
+                        case QAAnswer.Combo:
+                            {
+                                var buttons = parameters["Options"].Split(',');
+                                var defValue = parameters["Default"];
+
+                                fieldType = typeof(DFLayoutOptionField);
+                                var optionProperties = new DFLayoutOptionFieldProperties();
+                                properties = optionProperties;
+
+                                optionProperties.Default = defValue;
+                                optionProperties.OptionType = DFLayoutOptionType.Combo;
+                                optionProperties.Options = DFLayoutOptionFieldProperties.WriteOptions(buttons);
+                            }
+                            break;
+                        default:
+                            throw new Exception("Impossible");
+                    }
+
+                    properties.Code = code;
+                    properties.Description = question.Description.NotWhiteSpaceOr(question.Question);
+                    properties.Required = !parameters.GetValueOrDefault("Default").IsNullOrWhiteSpace();
+
+                    variable.SaveProperties(fieldType, properties);
+
+                    mappings.Add(question.ID, variable.Code);
+                    codes.Add(variable.Code);
+
+                    var field = (Activator.CreateInstance(variable.FieldType()) as DFLayoutField)!;
+                    field.Name = variable.Code;
+                    field.Row = row;
+                    field.Column = 3;
+                    layout.Elements.Add(field);
+
+                    variables.Add(variable);
+                    ++i;
+                }
+
+                question.Converted = true;
+            }
+
+            var dfLayout = new DigitalFormLayout();
+            dfLayout.Form.ID = item.FormID;
+            dfLayout.Layout = layout.SaveLayout();
+            dfLayout.Description = "Generated from QA form";
+            dfLayout.Type = DFLayoutType.Desktop;
+            dfLayout.Active = true;
+            layouts.Add(dfLayout);
+        }
+
+        FormUpdater.UpdateAllForms(
+            (form, variables) => false,
+            (formType, instance, form, variables) =>
+            {
+                if (!variableMappings.TryGetValue(form.ID, out var mappings)) return false;
+
+                var values = DigitalForm.DeserializeFormSaveData(instance) ?? new();
+                var items = values.ToLoadStorage().Items().ToArray();
+                foreach(var (key, value) in items)
+                {
+                    if (!Guid.TryParse(key, out var id)) continue;
+                    if (!mappings.TryGetValue(id, out var code)) continue;
+
+                    values.AddValue(code, value);
+                }
+
+                DigitalForm.SerializeFormData(instance, values);
+                return true;
+            },
+            filter: Filter<DigitalForm>.Where(x => x.ID).InList(qaQuestions.ToArray(x => x.FormID)));
+
+        provider.Save(variables);
+        provider.Save(layouts);
+        provider.Save(qaQuestions.SelectMany(x => x.Questions));
+    }
+
     public override bool Update()
     public override bool Update()
     {
     {
         var provider = DbFactory.NewProvider(Logger.Main);
         var provider = DbFactory.NewProvider(Logger.Main);
@@ -100,6 +336,8 @@ internal class Update_8_58 : DatabaseUpdateScript
         ConvertLink<ManufacturingSection, QAFormLink, DigitalFormLink>(provider, x => x.QAForm, x => x.DigitalForm);
         ConvertLink<ManufacturingSection, QAFormLink, DigitalFormLink>(provider, x => x.QAForm, x => x.DigitalForm);
         ConvertLink<ManufacturingTemplateStage, QAFormLink, DigitalFormLink>(provider, x => x.QAForm, x => x.DigitalForm);
         ConvertLink<ManufacturingTemplateStage, QAFormLink, DigitalFormLink>(provider, x => x.QAForm, x => x.DigitalForm);
 
 
+        ConvertQAQuestions(provider);
+
         return true;
         return true;
     }
     }
 }
 }

+ 3 - 2
prs.shared/Database Update Scripts/Utils/FormUpdater.cs

@@ -11,10 +11,11 @@ namespace PRS.Shared
     {
     {
         public static void UpdateAllForms(
         public static void UpdateAllForms(
             Func<DigitalForm, IList<DigitalFormVariable>, bool> skip,
             Func<DigitalForm, IList<DigitalFormVariable>, bool> skip,
-            Func<Type, IDigitalFormInstance, DigitalForm, IList<DigitalFormVariable>, bool> convert, int step = 100)
+            Func<Type, IDigitalFormInstance, DigitalForm, IList<DigitalFormVariable>, bool> convert, int step = 100,
+            Filter<DigitalForm>? filter = null)
         {
         {
             var forms = DbFactory.NewProvider(Logger.Main).Query<DigitalForm>(
             var forms = DbFactory.NewProvider(Logger.Main).Query<DigitalForm>(
-                null,
+                filter,
                 Columns.None<DigitalForm>().Add(x => x.ID)
                 Columns.None<DigitalForm>().Add(x => x.ID)
                     .Add(x => x.Code)
                     .Add(x => x.Code)
                     .Add(x => x.Description)
                     .Add(x => x.Description)