diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj
index 93fe14cc..27cb9759 100644
--- a/src/Client/Client.fsproj
+++ b/src/Client/Client.fsproj
@@ -35,6 +35,7 @@
+
@@ -90,7 +91,7 @@
-
+
diff --git a/src/Client/GenericComponents.fs b/src/Client/GenericComponents.fs
index 179d3539..eb6d41dd 100644
--- a/src/Client/GenericComponents.fs
+++ b/src/Client/GenericComponents.fs
@@ -10,6 +10,7 @@ module DaisyUiExtensions =
static member active = prop.className "modal-open"
type Components =
+
static member DeleteButton(?children, ?props) =
Daisy.button.button [
button.square
diff --git a/src/Client/MainComponents/Widgets.fs b/src/Client/MainComponents/Widgets.fs
index b31dd277..acce3812 100644
--- a/src/Client/MainComponents/Widgets.fs
+++ b/src/Client/MainComponents/Widgets.fs
@@ -4,6 +4,9 @@ open Feliz
open Feliz.DaisyUI
open Browser.Types
open LocalStorage.Widgets
+open Modals
+open Types.TableImport
+open Types.JsonImport
module private InitExtensions =
@@ -212,6 +215,12 @@ type Widget =
static member Templates (model: Model, dispatch, rmv: MouseEvent -> unit) =
let templates, setTemplates = React.useState(model.ProtocolState.Templates)
let config, setConfig = React.useState(TemplateFilterConfig.init)
+ let selectedColumnsLength =
+ if model.ProtocolState.TemplateSelected.IsSome then
+ model.ProtocolState.TemplateSelected.Value.Table.Columns.Length
+ else 0
+ let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init selectedColumnsLength)
+ let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init)
let filteredTemplates = Protocol.Search.filterTemplates (templates, config)
React.useEffectOnce(fun _ -> Messages.Protocol.GetAllProtocolsRequest |> Messages.ProtocolMsg |> dispatch)
React.useEffect((fun _ -> setTemplates model.ProtocolState.Templates), [|box model.ProtocolState.Templates|])
@@ -222,13 +231,38 @@ type Widget =
]
let insertContent() =
[
- Html.div [
- Protocol.TemplateFromDB.addFromDBToTableButton model dispatch
- ]
Html.div [
prop.style [style.maxHeight (length.px 350); style.overflow.auto]
prop.children [
- Protocol.TemplateFromDB.displaySelectedProtocolEle model dispatch
+ SidebarComponents.SidebarLayout.LogicContainer [
+ Html.div [
+ SelectiveTemplateFromDBModal.ToProtocolSearchElement model dispatch
+ ]
+ if model.ProtocolState.TemplateSelected.IsSome then
+ Html.div [
+ SelectiveImportModal.RadioPluginsBox(
+ "Import Type",
+ "fa-solid fa-cog",
+ importTypeState.ImportType,
+ "importType ",
+ [|
+ ARCtrl.TableJoinOptions.Headers, " Column Headers";
+ ARCtrl.TableJoinOptions.WithUnit, " ..With Units";
+ ARCtrl.TableJoinOptions.WithValues, " ..With Values";
+ |],
+ fun importType -> {importTypeState with ImportType = importType} |> setImportTypeState
+ )
+ ]
+ Html.div [
+ ModalElements.Box(
+ model.ProtocolState.TemplateSelected.Value.Name,
+ "",
+ SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false))
+ ]
+ Html.div [
+ SelectiveTemplateFromDBModal.AddFromDBToTableButton model selectedColumns importTypeState dispatch
+ ]
+ ]
]
]
]
diff --git a/src/Client/Modals/ModalElements.fs b/src/Client/Modals/ModalElements.fs
new file mode 100644
index 00000000..544634ba
--- /dev/null
+++ b/src/Client/Modals/ModalElements.fs
@@ -0,0 +1,108 @@
+namespace Modals
+
+open Feliz
+open Feliz.DaisyUI
+open Model
+open Messages
+open Shared
+open Types.TableImport
+
+open ARCtrl
+open JsonImport
+open Components
+open Fable.React.Helpers
+
+type ModalElements =
+
+ static member Button(text: string, onClickAction, buttonInput, ?isDisabled: bool) =
+ let isDisabled = defaultArg isDisabled false
+ Daisy.button.a [
+ button.success
+ button.wide
+ if isDisabled then
+ button.error
+ prop.disabled isDisabled
+ prop.onClick (fun _ -> onClickAction buttonInput)
+
+ prop.text text
+ ]
+
+ static member RadioPlugin(radioGroup: string, txt: string, isChecked, onChange: bool -> unit, ?isDisabled: bool) =
+ let isDisabled = defaultArg isDisabled false
+ Daisy.formControl [
+ Daisy.label [
+ prop.className [
+ "cursor-pointer transition-colors"
+ if isDisabled then
+ "!cursor-not-allowed"
+ else
+ "hover:bg-base-300"
+ ]
+ prop.children [
+ Daisy.radio [
+ prop.disabled isDisabled
+ radio.xs
+ prop.name radioGroup
+ prop.isChecked isChecked
+ prop.onChange onChange
+ ]
+ Html.span [
+ prop.className "text-sm"
+ prop.text txt
+ ]
+ ]
+ ]
+ ]
+
+ static member Box(title: string, icon: string, content: ReactElement, ?className: string list) =
+ Html.div [
+ prop.className [
+ "rounded shadow p-2 flex flex-col gap-2 border"
+ if className.IsSome then
+ className.Value |> String.concat " "
+ ]
+ prop.children [
+ Html.h3 [
+ prop.className "font-semibold gap-2 flex flex-row items-center"
+ prop.children [
+ Html.i [prop.className icon]
+ Html.span title
+ ]
+ ]
+ content
+ ]
+ ]
+
+ static member BoxWithChildren(children: ReactElement list, ?title: string, ?icon: string, ?className: string list) =
+ Html.div [
+ prop.className [
+ "rounded shadow p-2 flex flex-col gap-2 border"
+ if className.IsSome then
+ className.Value |> String.concat " "
+ ]
+ prop.children [
+ Html.h3 [
+ prop.className "font-semibold gap-2 flex flex-row items-center"
+ if icon.IsSome || title.IsSome then
+ prop.children [
+ if icon.IsSome then
+ Html.i [prop.className icon.Value]
+ if title.IsSome then
+ Html.span title.Value
+ ]
+ prop.children children
+ ]
+ ]
+ ]
+
+ static member SelectorButton<'a when 'a : equality> (targetselector: 'a, selector: 'a, setSelector: 'a -> unit, ?isDisabled) =
+ Daisy.button.button [
+ join.item
+ if isDisabled.IsSome then
+ prop.disabled isDisabled.Value
+ prop.style [style.flexGrow 1]
+ if (targetselector = selector) then
+ button.primary
+ prop.onClick (fun _ -> setSelector targetselector)
+ prop.text (string targetselector)
+ ]
diff --git a/src/Client/Modals/SelectiveImportModal.fs b/src/Client/Modals/SelectiveImportModal.fs
index 5e333e8b..9dd84c41 100644
--- a/src/Client/Modals/SelectiveImportModal.fs
+++ b/src/Client/Modals/SelectiveImportModal.fs
@@ -5,6 +5,7 @@ open Feliz.DaisyUI
open Model
open Messages
open Shared
+open Types.TableImport
open ARCtrl
open JsonImport
@@ -12,66 +13,83 @@ open Components
type SelectiveImportModal =
- static member private Radio(radioGroup: string, txt:string, isChecked, onChange: bool -> unit, ?isDisabled: bool) =
- let isDisabled = defaultArg isDisabled false
- Daisy.formControl [
- Daisy.label [
- prop.className [
- "cursor-pointer transition-colors"
- if isDisabled then
- "!cursor-not-allowed"
- else
- "hover:bg-base-300"
- ]
- prop.children [
- Daisy.radio [
- prop.disabled isDisabled
- radio.xs
- prop.name radioGroup
- prop.isChecked isChecked
- prop.onChange onChange
- ]
- Html.span [
- prop.className "text-sm"
- prop.text txt
- ]
- ]
+ static member RadioPluginsBox(boxName, icon, importType: TableJoinOptions, radioGroupName, radioData: (TableJoinOptions * string)[], setImportType: TableJoinOptions -> unit) =
+ let myradio(target: TableJoinOptions, txt: string) =
+ let isChecked = importType = target
+ ModalElements.RadioPlugin(radioGroupName, txt, isChecked, fun (b: bool) -> if b then setImportType target)
+ ModalElements.Box (boxName, icon, React.fragment [
+ Html.div [
+ for i in 0..radioData.Length-1 do
+ myradio(radioData.[i])
]
- ]
- static member private Box (title: string, icon: string, content: ReactElement, ?className: string list) =
+ ])
+
+ static member CheckBoxForTableColumnSelection(columns: CompositeColumn [], index, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit) =
Html.div [
- prop.className [
- "rounded shadow p-2 flex flex-col gap-2 border"
- if className.IsSome then
- className.Value |> String.concat " "
- ]
+ prop.style [style.display.flex; style.justifyContent.center]
prop.children [
- Html.h3 [
- prop.className "font-semibold gap-2 flex flex-row items-center"
- prop.children [
- Html.i [prop.className icon]
- Html.span title
+ Daisy.checkbox [
+ prop.type'.checkbox
+ prop.style [
+ style.height(length.perc 100)
]
+ prop.isChecked
+ (if selectionInformation.Columns.Length > 0 then
+ selectionInformation.Columns.[index]
+ else true)
+ prop.onChange (fun (b: bool) ->
+ if columns.Length > 0 then
+ let selectedData = selectionInformation.Columns
+ selectedData.[index] <- b
+ {selectionInformation with Columns = selectedData} |> setSelectedColumns)
]
- content
]
]
- static member private ImportTypeRadio(importType: TableJoinOptions, setImportType: TableJoinOptions -> unit) =
- let myradio(target: TableJoinOptions, txt: string) =
- let isChecked = importType = target
- SelectiveImportModal.Radio("importType", txt, isChecked, fun (b:bool) -> if b then setImportType target)
- SelectiveImportModal.Box ("Import Type", "fa-solid fa-cog", React.fragment [
- Html.div [
- myradio(ARCtrl.TableJoinOptions.Headers, " Column Headers")
- myradio(ARCtrl.TableJoinOptions.WithUnit, " ..With Units")
- myradio(ARCtrl.TableJoinOptions.WithValues, " ..With Values")
+ static member TableWithImportColumnCheckboxes(table: ArcTable, ?selectionInformation: SelectedColumns, ?setSelectedColumns: SelectedColumns -> unit) =
+ let columns = table.Columns
+ let displayCheckBox =
+ //Determine whether to display checkboxes or not
+ selectionInformation.IsSome && setSelectedColumns.IsSome
+ Daisy.table [
+ prop.children [
+ Html.thead [
+ Html.tr [
+ for i in 0..columns.Length-1 do
+ Html.th [
+ Html.label [
+ prop.className "join flex flex-row centered gap-2"
+ prop.children [
+ if displayCheckBox then
+ SelectiveImportModal.CheckBoxForTableColumnSelection(columns, i, selectionInformation.Value, setSelectedColumns.Value)
+ Html.text (columns.[i].Header.ToString())
+ Html.div [
+ prop.onClick (fun e ->
+ if columns.Length > 0 && selectionInformation.IsSome then
+ let selectedData = selectionInformation.Value.Columns
+ selectedData.[i] <- not selectedData.[i]
+ {selectionInformation.Value with Columns = selectedData} |> setSelectedColumns.Value)
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+
+ Html.tbody [
+ for ri in 0 .. (table.RowCount-1) do
+ let row = table.GetRow(ri, true)
+ Html.tr [
+ for c in row do
+ Html.td (c.ToString())
+ ]
+ ]
]
- ])
+ ]
static member private MetadataImport(isActive: bool, setActive: bool -> unit, disArcFile: ArcFilesDiscriminate) =
let name = string disArcFile
- SelectiveImportModal.Box (sprintf "%s Metadata" name, "fa-solid fa-lightbulb", React.fragment [
+ ModalElements.Box (sprintf "%s Metadata" name, "fa-solid fa-lightbulb", React.fragment [
Daisy.formControl [
Daisy.label [
prop.className "cursor-pointer"
@@ -99,25 +117,25 @@ type SelectiveImportModal =
)
[]
- static member private TableImport(index: int, table0: ArcTable, state: SelectiveImportModalState, addTableImport: int -> bool -> unit, rmvTableImport: int -> unit) =
+ static member private TableImport(index: int, table0: ArcTable, state: SelectiveImportModalState, addTableImport: int -> bool -> unit, rmvTableImport: int -> unit, selectedColumns, setSelectedColumns) =
let name = table0.Name
let radioGroup = "radioGroup_" + name
let import = state.ImportTables |> List.tryFind (fun it -> it.Index = index)
let isActive = import.IsSome
let isDisabled = state.ImportMetadata
- SelectiveImportModal.Box (name, "fa-solid fa-table", React.fragment [
+ ModalElements.Box (name, "fa-solid fa-table", React.fragment [
Html.div [
- SelectiveImportModal.Radio (radioGroup, "Import",
+ ModalElements.RadioPlugin (radioGroup, "Import",
isActive && import.Value.FullImport,
(fun (b:bool) -> addTableImport index true),
isDisabled
)
- SelectiveImportModal.Radio (radioGroup, "Append to active table",
+ ModalElements.RadioPlugin (radioGroup, "Append to active table",
isActive && not import.Value.FullImport,
(fun (b:bool) -> addTableImport index false),
isDisabled
)
- SelectiveImportModal.Radio (radioGroup, "No Import",
+ ModalElements.RadioPlugin (radioGroup, "No Import",
not isActive,
(fun (b:bool) -> rmvTableImport index),
isDisabled
@@ -126,39 +144,24 @@ type SelectiveImportModal =
Daisy.collapse [
Html.input [prop.type'.checkbox; prop.className "min-h-0 h-5"]
Daisy.collapseTitle [
- prop.className "p-1 min-h-0 h-5 text-sm"
- prop.text "Preview Table"
+ prop.className "p-1 min-h-0 h-5 text-sm"
+ prop.text (if isActive then "Select Columns" else "Preview Table")
]
Daisy.collapseContent [
prop.className "overflow-x-auto"
prop.children [
- Daisy.table [
- table.xs
- prop.children [
- Html.thead [
- Html.tr [
- for c in table0.Headers do
- Html.th (c.ToString())
- ]
- ]
- Html.tbody [
- for ri in 0 .. (table0.RowCount-1) do
- let row = table0.GetRow(ri, true)
- Html.tr [
- for c in row do
- Html.td (c.ToString())
- ]
- ]
- ]
- ]
+ if isActive then
+ SelectiveImportModal.TableWithImportColumnCheckboxes(table0, selectedColumns, setSelectedColumns)
+ else
+ SelectiveImportModal.TableWithImportColumnCheckboxes(table0)
]
]
- ]],
- className = [if isActive then "!bg-primary !text-primary-content"]
- )
+ ]
+ ],
+ className = [if isActive then "!bg-primary !text-primary-content"])
[]
- static member Main(import: ArcFiles, dispatch, rmv) =
+ static member Main (import: ArcFiles, dispatch, rmv) =
let state, setState = React.useState(SelectiveImportModalState.init)
let tables, disArcfile =
match import with
@@ -170,17 +173,21 @@ type SelectiveImportModal =
if b then
{
state with
- ImportMetadata = true;
- ImportTables = [ for ti in 0 .. tables.Count-1 do {ImportTable.Index = ti; ImportTable.FullImport = true}]
+ ImportMetadata = true;
+ ImportTables = [for ti in 0 .. tables.Count-1 do {ImportTable.Index = ti; ImportTable.FullImport = true}]
} |> setState
else
SelectiveImportModalState.init() |> setState
- let addTableImport = fun (i:int) (fullImport: bool) ->
+ let addTableImport = fun (i: int) (fullImport: bool) ->
let newImportTable: ImportTable = {Index = i; FullImport = fullImport}
let newImportTables = newImportTable::state.ImportTables |> List.distinct
{state with ImportTables = newImportTables} |> setState
let rmvTableImport = fun i ->
{state with ImportTables = state.ImportTables |> List.filter (fun it -> it.Index <> i)} |> setState
+ let selectedColumns =
+ tables
+ |> Array.ofSeq
+ |> Array.map (fun t -> React.useState(SelectedColumns.init t.Columns.Length))
Daisy.modal.div [
modal.active
prop.children [
@@ -195,18 +202,29 @@ type SelectiveImportModal =
Components.DeleteButton(props=[prop.onClick rmv])
]
]
- SelectiveImportModal.ImportTypeRadio(state.ImportType, fun it -> {state with ImportType = it} |> setState)
+ SelectiveImportModal.RadioPluginsBox(
+ "Import Type",
+ "fa-solid fa-cog",
+ state.ImportType,
+ "importType",
+ [|
+ ARCtrl.TableJoinOptions.Headers, " Column Headers";
+ ARCtrl.TableJoinOptions.WithUnit, " ..With Units";
+ ARCtrl.TableJoinOptions.WithValues, " ..With Values";
+ |],
+ fun importType -> {state with ImportType = importType} |> setState)
SelectiveImportModal.MetadataImport(state.ImportMetadata, setMetadataImport, disArcfile)
for ti in 0 .. (tables.Count-1) do
let t = tables.[ti]
- SelectiveImportModal.TableImport(ti, t, state, addTableImport, rmvTableImport)
+ let selectedColumns, setSelectedColumns = fst selectedColumns.[ti], snd selectedColumns.[ti]
+ SelectiveImportModal.TableImport(ti, t, state, addTableImport, rmvTableImport, selectedColumns, setSelectedColumns)
Daisy.cardActions [
Daisy.button.button [
button.info
prop.style [style.marginLeft length.auto]
prop.text "Submit"
prop.onClick(fun e ->
- {| importState = state; importedFile = import|} |> SpreadsheetInterface.ImportJson |> InterfaceMsg |> dispatch
+ {| importState = state; importedFile = import; selectedColumns = selectedColumns |} |> SpreadsheetInterface.ImportJson |> InterfaceMsg |> dispatch
rmv e
)
]
diff --git a/src/Client/OfficeInterop/OfficeInterop.fs b/src/Client/OfficeInterop/OfficeInterop.fs
index a42fca99..edcf7282 100644
--- a/src/Client/OfficeInterop/OfficeInterop.fs
+++ b/src/Client/OfficeInterop/OfficeInterop.fs
@@ -687,6 +687,16 @@ module AnnotationTable =
|> Array.ofSeq
|> Array.map (fun header -> header.ToString())
+ ///
+ /// Get headers from range
+ ///
+ ///
+ let getBody (range: ResizeArray>) =
+ range
+ |> Array.ofSeq
+ |> (fun items -> items.[1..])
+ |> ResizeArray
+
///
/// Try find annotation table in active worksheet and parse to ArcTable
///
@@ -920,11 +930,44 @@ let addCompositeColumn (excelTable: Table) (arcTable: ArcTable) (newColumn: Comp
loggingList
+let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) (removeColumns: int list): ArcTable =
+ // Remove existing columns
+ let mutable columnsToRemove = removeColumns
+ // find duplicate columns
+ let tablecopy = toJoinTable.Copy()
+ for header in activeTable.Headers do
+ let containsAtIndex = tablecopy.Headers |> Seq.tryFindIndex (fun h -> h = header)
+ if containsAtIndex.IsSome then
+ columnsToRemove <- containsAtIndex.Value::columnsToRemove
+
+ //Remove duplicates because unselected and already existing columns can overlap
+ let columnsToRemove = columnsToRemove |> Set.ofList |> Set.toList
+
+ tablecopy.RemoveColumns (Array.ofList columnsToRemove)
+ tablecopy.IteriColumns(fun i c0 ->
+ let c1 = {c0 with Cells = tablecopy.Columns.[i].Cells}
+ let c2 =
+ if c1.Header.isInput then
+ match activeTable.TryGetInputColumn() with
+ | Some ic ->
+ {c1 with Cells = ic.Cells}
+ | _ -> c1
+ elif c1.Header.isOutput then
+ match activeTable.TryGetOutputColumn() with
+ | Some oc ->
+ {c1 with Cells = oc.Cells}
+ | _ -> c1
+ else
+ c1
+ tablecopy.UpdateColumn(i, c2.Header, c2.Cells)
+ )
+ tablecopy
+
///
/// Prepare the given table to be joined with the currently active annotation table
///
///
-let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (context: RequestContext) =
+let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (selectedColumns:bool []) (context: RequestContext) =
promise {
let! originTableRes = ArcTable.fromExcelTable(table, context)
@@ -932,7 +975,13 @@ let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (context: Requ
| Result.Error _ ->
return failwith $"Failed to create arc table for table {table.name}"
| Result.Ok originTable ->
- let finalTable = Table.selectiveTablePrepare originTable tableToAdd
+ let selectedColumnIndices =
+ selectedColumns
+ |> Array.mapi (fun i item -> if item = false then Some i else None)
+ |> Array.choose (fun x -> x)
+ |> List.ofArray
+
+ let finalTable = Table.selectiveTablePrepare originTable tableToAdd selectedColumnIndices
let selectedRange = context.workbook.getSelectedRange()
@@ -959,17 +1008,16 @@ let prepareTemplateInMemory (table: Table) (tableToAdd: ArcTable) (context: Requ
///
///
///
-let joinTable (tableToAdd: ArcTable, options: TableJoinOptions option) =
+let joinTable (tableToAdd: ArcTable, selectedColumns: bool [], options: TableJoinOptions option) =
Excel.run(fun context ->
promise {
-
//When a name is available get the annotation and arctable for easy access of indices and value adaption
//Annotation table enables a easy way to adapt the table, updating existing and adding new columns
let! result = AnnotationTable.tryGetActive context
match result with
| Some excelTable ->
- let! (tableToAdd: ArcTable, index: int option) = prepareTemplateInMemory excelTable tableToAdd context
+ let! (refinedTableToAdd: ArcTable, index: int option) = prepareTemplateInMemory excelTable tableToAdd selectedColumns context
//Arctable enables a fast check for the existence of input- and output-columns and their indices
let! arcTableRes = ArcTable.fromExcelTable(excelTable, context)
@@ -977,11 +1025,11 @@ let joinTable (tableToAdd: ArcTable, options: TableJoinOptions option) =
//When both tables could be accessed succesfully then check what kind of column shall be added an whether it is already there or not
match arcTableRes with
| Result.Ok arcTable ->
- arcTable.Join(tableToAdd, ?index=index, ?joinOptions=options, forceReplace=true)
+ arcTable.Join(refinedTableToAdd, ?index=index, ?joinOptions=options, forceReplace=true)
let newTableRange = excelTable.getRange()
- let _ = newTableRange.load(propertyNames = U2.Case2 (ResizeArray["rowCount";]))
+ let _ = newTableRange.load(propertyNames = U2.Case2 (ResizeArray["rowCount"]))
do! context.sync().``then``(fun _ ->
excelTable.delete()
@@ -989,14 +1037,16 @@ let joinTable (tableToAdd: ArcTable, options: TableJoinOptions option) =
let! (newTable, _) = AnnotationTable.createAtRange(false, false, newTableRange, context)
- let _ = newTable.load(propertyNames = U2.Case2 (ResizeArray["name"; "values"; "columns";]))
+ let _ = newTable.load(propertyNames = U2.Case2 (ResizeArray["name"; "values"; "columns"]))
+
+ let tableSeqs = arcTable.ToStringSeqs()
do! context.sync().``then``(fun _ ->
newTable.name <- excelTable.name
let headerNames =
- let names = AnnotationTable.getHeaders (arcTable.ToStringSeqs())
+ let names = AnnotationTable.getHeaders tableSeqs
names
|> Array.map (fun name -> extendName names name)
@@ -1025,9 +1075,12 @@ let joinTable (tableToAdd: ArcTable, options: TableJoinOptions option) =
do! context.sync()
+ let bodyValues = AnnotationTable.getBody tableSeqs
+ newBodyRange.values <- bodyValues
+
do! AnnotationTable.format(newTable, context, true)
- return [InteropLogging.Msg.create InteropLogging.Warning $"Joined template {tableToAdd.Name} to table {excelTable.name}!"]
+ return [InteropLogging.Msg.create InteropLogging.Info $"Joined template {refinedTableToAdd.Name} to table {excelTable.name}!"]
| Result.Error _ ->
return [InteropLogging.Msg.create InteropLogging.Error "No arc table could be created! This should not happen at this stage! Please report this as a bug to the developers.!"]
| None -> return [InteropLogging.NoActiveTableMsg]
@@ -1469,7 +1522,7 @@ let convertBuildingBlock () =
if String.IsNullOrEmpty(msgText) then $"Converted building block of {snd selectedBuildingBlock.[0]} to unit"
else msgText
- return [InteropLogging.Msg.create InteropLogging.Warning msg]
+ return [InteropLogging.Msg.create InteropLogging.Info msg]
| Result.Error ex -> return [InteropLogging.Msg.create InteropLogging.Error ex.Message]
| None -> return [InteropLogging.NoActiveTableMsg]
}
@@ -1792,7 +1845,7 @@ let deleteTopLevelMetadata () =
worksheet.delete()
)
- return [InteropLogging.Msg.create InteropLogging.Warning $"The top level metadata work sheet has been deleted"]
+ return [InteropLogging.Msg.create InteropLogging.Info $"The top level metadata work sheet has been deleted"]
}
)
@@ -2380,7 +2433,7 @@ type Main =
do! updateSelectedBuildingBlocks excelTable arcTable propertyColumns indexedTerms
do! AnnotationTable.format(excelTable, context, true)
- return [InteropLogging.Msg.create InteropLogging.Warning $"The annotation table {excelTable.name} is valid"]
+ return [InteropLogging.Msg.create InteropLogging.Info $"The annotation table {excelTable.name} is valid"]
| Result.Error ex -> return [InteropLogging.Msg.create InteropLogging.Error ex.Message]
}
diff --git a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs
index 8b4fb71e..8ad27ee5 100644
--- a/src/Client/Pages/ProtocolTemplates/ProtocolView.fs
+++ b/src/Client/Pages/ProtocolTemplates/ProtocolView.fs
@@ -27,7 +27,6 @@ type Templates =
SidebarComponents.SidebarLayout.Container [
SidebarComponents.SidebarLayout.Header "Templates"
-
SidebarComponents.SidebarLayout.Description (Html.p [
Html.b "Search the database for templates."
Html.text " The building blocks from these templates can be inserted into the Swate table. "
@@ -39,7 +38,7 @@ type Templates =
// Box 1
SidebarComponents.SidebarLayout.Description "Add template from database."
- TemplateFromDB.Main(model, dispatch)
+ Modals.SelectiveTemplateFromDBModal.Main(model, dispatch)
// Box 2
SidebarComponents.SidebarLayout.Description (Html.p [
diff --git a/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs
new file mode 100644
index 00000000..fac0583b
--- /dev/null
+++ b/src/Client/Pages/ProtocolTemplates/SelectiveTemplateFromDB.fs
@@ -0,0 +1,95 @@
+namespace Modals
+
+open Feliz
+open Feliz.DaisyUI
+open Model
+open Messages
+open Shared
+open Types.TableImport
+
+open ARCtrl
+open JsonImport
+
+type SelectiveTemplateFromDBModal =
+
+ static member ToProtocolSearchElement (model: Model) dispatch =
+ Daisy.button.button [
+ prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch)
+ button.primary
+ button.block
+ prop.text "Browse database"
+ ]
+
+ []
+ static member displaySelectedProtocolElements (model: Model, selectionInformation: SelectedColumns, setSelectedColumns: SelectedColumns -> unit, dispatch, ?hasIcon: bool) =
+ let hasIcon = defaultArg hasIcon true
+ Html.div [
+ prop.style [style.overflowX.auto; style.marginBottom (length.rem 1)]
+ prop.children [
+ if model.ProtocolState.TemplateSelected.IsSome then
+ if hasIcon then
+ Html.i [prop.className "fa-solid fa-cog"]
+ Html.span $"Template: {model.ProtocolState.TemplateSelected.Value.Name}"
+ if model.ProtocolState.TemplateSelected.IsSome then
+ SelectiveImportModal.TableWithImportColumnCheckboxes(model.ProtocolState.TemplateSelected.Value.Table, selectionInformation, setSelectedColumns)
+ ]
+ ]
+
+ static member AddFromDBToTableButton (model: Model) selectionInformation importType dispatch =
+ let addTemplate (templatePot: Template option, selectedColumns) =
+ if model.ProtocolState.TemplateSelected.IsNone then
+ failwith "No template selected!"
+ if templatePot.IsSome then
+ let table = templatePot.Value.Table
+ SpreadsheetInterface.AddTemplate(table, selectedColumns, importType) |> InterfaceMsg |> dispatch
+ Html.div [
+ prop.className "join flex flex-row justify-center gap-2"
+ prop.children [
+ ModalElements.Button("Add template", addTemplate, (model.ProtocolState.TemplateSelected, selectionInformation.Columns), model.ProtocolState.TemplateSelected.IsNone)
+ if model.ProtocolState.TemplateSelected.IsSome then
+ Daisy.button.a [
+ button.outline
+ prop.onClick (fun _ -> Protocol.RemoveSelectedProtocol |> ProtocolMsg |> dispatch)
+ button.error
+ Html.i [prop.className "fa-solid fa-times"] |> prop.children
+ ]
+ ]
+ ]
+
+ []
+ static member Main (model: Model, dispatch) =
+ let length =
+ if model.ProtocolState.TemplateSelected.IsSome then
+ model.ProtocolState.TemplateSelected.Value.Table.Columns.Length
+ else 0
+ let selectedColumns, setSelectedColumns = React.useState(SelectedColumns.init length)
+ let importTypeState, setImportTypeState = React.useState(SelectiveImportModalState.init)
+ SidebarComponents.SidebarLayout.LogicContainer [
+ Html.div [
+ SelectiveTemplateFromDBModal.ToProtocolSearchElement model dispatch
+ ]
+ if model.ProtocolState.TemplateSelected.IsSome then
+ Html.div [
+ SelectiveImportModal.RadioPluginsBox(
+ "Import Type",
+ "fa-solid fa-cog",
+ importTypeState.ImportType,
+ "importType",
+ [|
+ ARCtrl.TableJoinOptions.Headers, " Column Headers";
+ ARCtrl.TableJoinOptions.WithUnit, " ..With Units";
+ ARCtrl.TableJoinOptions.WithValues, " ..With Values";
+ |],
+ fun importType -> {importTypeState with ImportType = importType} |> setImportTypeState
+ )
+ ]
+ Html.div [
+ ModalElements.Box(
+ model.ProtocolState.TemplateSelected.Value.Name,
+ "",
+ SelectiveTemplateFromDBModal.displaySelectedProtocolElements(model, selectedColumns, setSelectedColumns, dispatch, false))
+ ]
+ Html.div [
+ SelectiveTemplateFromDBModal.AddFromDBToTableButton model selectedColumns importTypeState dispatch
+ ]
+ ]
diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs
deleted file mode 100644
index f6659a05..00000000
--- a/src/Client/Pages/ProtocolTemplates/TemplateFromDB.fs
+++ /dev/null
@@ -1,93 +0,0 @@
-namespace Protocol
-
-open Feliz
-open Feliz.DaisyUI
-open Messages
-open Model
-open Shared
-
-type TemplateFromDB =
-
- static member toProtocolSearchElement (model:Model) dispatch =
- Daisy.button.button [
- prop.onClick(fun _ -> UpdateModel {model with Model.PageState.SidebarPage = Routing.SidebarPage.ProtocolSearch} |> dispatch)
- button.primary
- button.block
- prop.text "Browse database"
- ]
-
- static member addFromDBToTableButton (model:Model) dispatch =
- Html.div [
- prop.className "join flex flex-row justify-center gap-2"
- prop.children [
- Daisy.button.a [
- button.success
- button.wide
- if model.ProtocolState.TemplateSelected.IsNone then
- button.error
- prop.disabled true
- prop.onClick (fun _ ->
- if model.ProtocolState.TemplateSelected.IsNone then
- failwith "No template selected!"
-
- SpreadsheetInterface.AddTemplate(model.ProtocolState.TemplateSelected.Value.Table) |> InterfaceMsg |> dispatch
- )
- prop.text "Add template"
- ]
- if model.ProtocolState.TemplateSelected.IsSome then
- Daisy.button.a [
- button.outline
- prop.onClick (fun e -> Protocol.RemoveSelectedProtocol |> ProtocolMsg |> dispatch)
- button.error
- Html.i [prop.className "fa-solid fa-times"] |> prop.children
- ]
- ]
- ]
-
- static member displaySelectedProtocolEle (model:Model) dispatch =
- Html.div [
- prop.style [style.overflowX.auto; style.marginBottom (length.rem 1)]
- prop.children [
- Daisy.table [
- prop.children [
- Html.thead [
- Html.tr [
- Html.th "Column"
- Html.th "Column TAN"
- //Html.th "Unit"
- //Html.th "Unit TAN"
- ]
- ]
- Html.tbody [
- for column in model.ProtocolState.TemplateSelected.Value.Table.Columns do
- //let unitOption = column.TryGetColumnUnits()
- yield
- Html.tr [
- Html.td (column.Header.ToString())
- Html.td (if column.Header.IsTermColumn then column.Header.ToTerm().TermAccessionShort else "-")
- //td [] [str (if unitOption.IsSome then insertBB.UnitTerm.Value.Name else "-")]
- //td [] [str (if insertBB.HasUnit then insertBB.UnitTerm.Value.TermAccession else "-")]
- ]
- ]
- ]
- ]
- ]
- ]
-
- static member Main(model:Model, dispatch) =
- SidebarComponents.SidebarLayout.LogicContainer [
- Html.div [
- TemplateFromDB.toProtocolSearchElement model dispatch
- ]
-
- Html.div [
- TemplateFromDB.addFromDBToTableButton model dispatch
- ]
- if model.ProtocolState.TemplateSelected.IsSome then
- Html.div [
- TemplateFromDB.displaySelectedProtocolEle model dispatch
- ]
- Html.div [
- TemplateFromDB.addFromDBToTableButton model dispatch
- ]
- ]
diff --git a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs
index cf287e9b..4052aa35 100644
--- a/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs
+++ b/src/Client/Pages/ProtocolTemplates/TemplateFromFile.fs
@@ -19,6 +19,8 @@ open Shared
open ARCtrl
open Fable.Core.JsInterop
+open Modals
+
type private TemplateFromFileState = {
/// User select type to upload
FileType: ArcFilesDiscriminate
@@ -80,18 +82,6 @@ type TemplateFromFile =
)
]
- static member private SelectorButton<'a when 'a : equality> (targetselector: 'a, selector: 'a, setSelector: 'a -> unit, ?isDisabled) =
- Daisy.button.button [
- join.item
- if isDisabled.IsSome then
- prop.disabled isDisabled.Value
- prop.style [style.flexGrow 1]
- if (targetselector = selector) then
- button.primary
- prop.onClick (fun _ -> setSelector targetselector)
- prop.text (string targetselector)
- ]
-
[]
static member Main(model: Model, dispatch) =
let state, setState = React.useState(TemplateFromFileState.init)
@@ -119,10 +109,10 @@ type TemplateFromFile =
Daisy.join [
prop.className "w-full"
prop.children [
- JsonExportFormat.ROCrate |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
- JsonExportFormat.ISA |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
- JsonExportFormat.ARCtrl |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
- JsonExportFormat.ARCtrlCompressed |> fun jef -> TemplateFromFile.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
+ JsonExportFormat.ROCrate |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
+ JsonExportFormat.ISA |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
+ JsonExportFormat.ARCtrl |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
+ JsonExportFormat.ARCtrlCompressed |> fun jef -> ModalElements.SelectorButton (jef, state.JsonFormat, setJsonFormat, jsonFormatDisabled jef)
]
]
]
@@ -131,10 +121,10 @@ type TemplateFromFile =
Daisy.join [
prop.className "w-full"
prop.children [
- ArcFilesDiscriminate.Assay |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
- ArcFilesDiscriminate.Study |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
- ArcFilesDiscriminate.Investigation |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
- ArcFilesDiscriminate.Template |> fun ft -> TemplateFromFile.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
+ ArcFilesDiscriminate.Assay |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
+ ArcFilesDiscriminate.Study |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
+ ArcFilesDiscriminate.Investigation |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
+ ArcFilesDiscriminate.Template |> fun ft -> ModalElements.SelectorButton (ft, state.FileType, setFileType, fileTypeDisabled ft)
]
]
]
diff --git a/src/Client/Pages/TermSearch/TermSearchView.fs b/src/Client/Pages/TermSearch/TermSearchView.fs
index d9cff126..8302d11c 100644
--- a/src/Client/Pages/TermSearch/TermSearchView.fs
+++ b/src/Client/Pages/TermSearch/TermSearchView.fs
@@ -8,7 +8,6 @@ open Elmish
open TermSearch
open Model
-
let update (termSearchMsg: TermSearch.Msg) (currentState:TermSearch.Model) : TermSearch.Model * Cmd =
match termSearchMsg with
// Toggle the search by parent ontology option on/off by clicking on a checkbox
diff --git a/src/Client/SidebarComponents/LayoutHelper.fs b/src/Client/SidebarComponents/LayoutHelper.fs
index d1578854..5358322b 100644
--- a/src/Client/SidebarComponents/LayoutHelper.fs
+++ b/src/Client/SidebarComponents/LayoutHelper.fs
@@ -20,11 +20,6 @@ module private LayoutHelperAux =
if v > 5 then false else true
type SidebarLayout =
- static member Container (children: ReactElement list) =
- Html.div [
- prop.className "flex flex-col gap-2 py-4"
- prop.children children
- ]
static member LogicContainer (children: ReactElement list) =
Html.div [
@@ -39,6 +34,13 @@ type SidebarLayout =
prop.className "relative flex p-4 animated-border shadow-md gap-4 flex-col" //experimental
prop.children children
]
+
+ static member Container (children: ReactElement list) =
+ Html.div [
+ prop.className "flex flex-col gap-2 py-4"
+ prop.children children
+ ]
+
static member Header(txt: string) =
Html.h3 [
prop.className "text-lg font-semibold"
diff --git a/src/Client/States/ModalState.fs b/src/Client/States/ModalState.fs
index 1b784b4e..982376da 100644
--- a/src/Client/States/ModalState.fs
+++ b/src/Client/States/ModalState.fs
@@ -5,6 +5,8 @@ open ARCtrl
open Feliz
+open Model
+
module ModalState =
type TableModals =
diff --git a/src/Client/States/OfficeInteropState.fs b/src/Client/States/OfficeInteropState.fs
index 009a3785..e509900a 100644
--- a/src/Client/States/OfficeInteropState.fs
+++ b/src/Client/States/OfficeInteropState.fs
@@ -31,7 +31,7 @@ type Msg =
| ValidateBuildingBlock
| AddAnnotationBlock of CompositeColumn
| AddAnnotationBlocks of CompositeColumn [] //* OfficeInterop.Types.Xml.ValidationTypes.TableValidation option
- | AddTemplate of ArcTable
+ | AddTemplate of ArcTable * bool[] * SelectiveImportModalState
| JoinTable of ArcTable * options: TableJoinOptions option
| RemoveBuildingBlock
| UpdateUnitForCells
diff --git a/src/Client/States/Spreadsheet.fs b/src/Client/States/Spreadsheet.fs
index 3b747069..bc8ce57b 100644
--- a/src/Client/States/Spreadsheet.fs
+++ b/src/Client/States/Spreadsheet.fs
@@ -3,6 +3,7 @@ namespace Spreadsheet
open Shared
open ARCtrl
open Fable.Core
+open JsonImport
type ColumnType =
| Main
@@ -201,7 +202,7 @@ type Msg =
| AddAnnotationBlock of CompositeColumn
| AddAnnotationBlocks of CompositeColumn []
| AddDataAnnotation of {| fragmentSelectors: string []; fileName: string; fileType: string; targetColumn: DataAnnotator.TargetColumn |}
-| AddTemplate of ArcTable
+| AddTemplate of ArcTable * bool[] * SelectiveImportModalState
| JoinTable of ArcTable * index: int option * options: TableJoinOptions option
| UpdateArcFile of ArcFiles
| InitFromArcFile of ArcFiles
diff --git a/src/Client/States/SpreadsheetInterface.fs b/src/Client/States/SpreadsheetInterface.fs
index e9e385e7..ed69e50e 100644
--- a/src/Client/States/SpreadsheetInterface.fs
+++ b/src/Client/States/SpreadsheetInterface.fs
@@ -1,6 +1,7 @@
namespace SpreadsheetInterface
open Shared
+open Types.TableImport
open ARCtrl
open JsonImport
@@ -17,14 +18,14 @@ type Msg =
| AddAnnotationBlocks of CompositeColumn []
| AddDataAnnotation of {| fragmentSelectors: string []; fileName: string; fileType: string; targetColumn: DataAnnotator.TargetColumn |}
/// This function will do preprocessing on the table to join
-| AddTemplate of ArcTable
+| AddTemplate of ArcTable * bool[] * SelectiveImportModalState
| JoinTable of ArcTable * columnIndex: int option * options: TableJoinOptions option
| UpdateArcFile of ArcFiles
/// Inserts TermMinimal to selected fields of one column
| InsertOntologyAnnotation of OntologyAnnotation
| InsertFileNames of string list
| ImportXlsx of byte []
-| ImportJson of {|importState: SelectiveImportModalState; importedFile: ArcFiles|}
+| ImportJson of {|importState: SelectiveImportModalState; importedFile: ArcFiles; selectedColumns: (SelectedColumns * (SelectedColumns -> unit))[]|}
/// Starts chain to export active table to isa json
| ExportJson of ArcFiles * JsonExportFormat
| UpdateUnitForCells
diff --git a/src/Client/Types.fs b/src/Client/Types.fs
index 5477c772..36771ca7 100644
--- a/src/Client/Types.fs
+++ b/src/Client/Types.fs
@@ -55,4 +55,15 @@ type Style =
| Some subClasses -> subClasses.TryFind name
| None -> None
|> Option.map _.StyleString
- |> Option.defaultValue ""
\ No newline at end of file
+ |> Option.defaultValue ""
+
+module TableImport =
+
+ type SelectedColumns = {
+ Columns: bool []
+ }
+ with
+ static member init(length) =
+ {
+ Columns = Array.init length (fun _ -> true)
+ }
diff --git a/src/Client/Update/InterfaceUpdate.fs b/src/Client/Update/InterfaceUpdate.fs
index 81e2b304..dba7b580 100644
--- a/src/Client/Update/InterfaceUpdate.fs
+++ b/src/Client/Update/InterfaceUpdate.fs
@@ -164,13 +164,13 @@ module Interface =
model, cmd
| _ -> failwith "not implemented"
- | AddTemplate table ->
+ | AddTemplate (table, selectedColumns, importType) ->
match host with
| Some Swatehost.Excel ->
- let cmd = OfficeInterop.AddTemplate table |> OfficeInteropMsg |> Cmd.ofMsg
+ let cmd = OfficeInterop.AddTemplate (table, selectedColumns, importType) |> OfficeInteropMsg |> Cmd.ofMsg
model, cmd
| Some Swatehost.Browser | Some Swatehost.ARCitect ->
- let cmd = Spreadsheet.AddTemplate table |> SpreadsheetMsg |> Cmd.ofMsg
+ let cmd = Spreadsheet.AddTemplate (table, selectedColumns, importType) |> SpreadsheetMsg |> Cmd.ofMsg
model, cmd
| _ -> failwith "not implemented"
| JoinTable (table, index, options) ->
@@ -201,6 +201,9 @@ module Interface =
model, cmd
| _ -> failwith "not implemented"
| ImportJson data ->
+ let selectedColumns =
+ data.selectedColumns
+ |> Array.map (fun (sc, _) -> sc)
match host with
| Some Swatehost.Excel ->
/// In Excel we must get the current information from worksheets and update them with the imported information
@@ -208,7 +211,7 @@ module Interface =
promise {
match data.importState.ImportMetadata with
| true -> // full import, does not require additional information
- return UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState
+ return UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState selectedColumns
| false -> // partial import, requires additional information
let! arcfile = OfficeInterop.Core.Main.tryParseToArcFile()
let arcfileOpt = arcfile |> Result.toOption
@@ -217,9 +220,9 @@ module Interface =
)
let activeTableIndex =
match arcfileOpt, activeTable with
- | Some arcfile, Ok activeTable -> arcfile.Tables() |> Seq.tryFindIndex (fun x -> x = activeTable)
+ | Some arcfile, Ok activeTable -> arcfile.Tables() |> Seq.tryFindIndex (fun table -> table = activeTable)
| _ -> None
- return UpdateUtil.JsonImportHelper.updateTables data.importedFile data.importState activeTableIndex arcfileOpt
+ return UpdateUtil.JsonImportHelper.updateTables data.importedFile data.importState (*selectedColumns*) activeTableIndex arcfileOpt
}
let updateArcFile (arcFile: ArcFiles) = SpreadsheetInterface.UpdateArcFile arcFile |> InterfaceMsg
let cmd =
@@ -232,7 +235,7 @@ module Interface =
| Some Swatehost.Browser | Some Swatehost.ARCitect ->
let cmd =
match data.importState.ImportMetadata with
- | true -> UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState
+ | true -> UpdateUtil.JsonImportHelper.updateWithMetadata data.importedFile data.importState selectedColumns
| false -> UpdateUtil.JsonImportHelper.updateTables data.importedFile data.importState model.SpreadsheetModel.ActiveView.TryTableIndex model.SpreadsheetModel.ArcFile
|> SpreadsheetInterface.UpdateArcFile |> InterfaceMsg |> Cmd.ofMsg
model, cmd
diff --git a/src/Client/Update/OfficeInteropUpdate.fs b/src/Client/Update/OfficeInteropUpdate.fs
index fb82cb49..71be2cd5 100644
--- a/src/Client/Update/OfficeInteropUpdate.fs
+++ b/src/Client/Update/OfficeInteropUpdate.fs
@@ -76,11 +76,11 @@ module OfficeInterop =
UpdateUtil.downloadFromString (jsonExport)
state, model, Cmd.none
- | AddTemplate table ->
+ | AddTemplate (table, selectedColumns, importType) ->
let cmd =
Cmd.OfPromise.either
OfficeInterop.Core.joinTable
- (table, Some ARCtrl.TableJoinOptions.WithValues)
+ (table, selectedColumns, Some importType.ImportType)
(curry GenericInteropLogs Cmd.none >> DevMsg)
(curry GenericError Cmd.none >> DevMsg)
state, model, cmd
@@ -89,7 +89,7 @@ module OfficeInterop =
let cmd =
Cmd.OfPromise.either
OfficeInterop.Core.joinTable
- (table, options)
+ (table, [||], options)
(curry GenericInteropLogs Cmd.none >> DevMsg)
(curry GenericError Cmd.none >> DevMsg)
state, model, cmd
diff --git a/src/Client/Update/SpreadsheetUpdate.fs b/src/Client/Update/SpreadsheetUpdate.fs
index 300a3cfa..1e2ce94c 100644
--- a/src/Client/Update/SpreadsheetUpdate.fs
+++ b/src/Client/Update/SpreadsheetUpdate.fs
@@ -103,13 +103,18 @@ module Spreadsheet =
| IsTable -> Controller.BuildingBlocks.addDataAnnotation data state
| IsMetadata -> failwith "Unable to add data annotation in metadata view"
nextState, model, Cmd.none
- | AddTemplate table ->
+ | AddTemplate (table, selectedColumns, importType) ->
let index = Some (Spreadsheet.Controller.BuildingBlocks.SidebarControllerAux.getNextColumnIndex model.SpreadsheetModel)
/// Filter out existing building blocks and keep input/output values.
- let options = Some ARCtrl.TableJoinOptions.WithValues // If changed to anything else we need different logic to keep input/output values
- let msg = fun t -> JoinTable(t, index, options) |> SpreadsheetMsg
+ let msg = fun table -> JoinTable(table, index, Some importType.ImportType) |> SpreadsheetMsg
+ let selectedColumnsIndices =
+ selectedColumns
+ |> Array.mapi (fun i item -> if item = false then Some i else None)
+ |> Array.choose (fun x -> x)
+ |> List.ofArray
+
let cmd =
- Table.selectiveTablePrepare state.ActiveTable table
+ Table.selectiveTablePrepare state.ActiveTable table selectedColumnsIndices
|> msg
|> Cmd.ofMsg
state, model, cmd
diff --git a/src/Client/Update/UpdateUtil.fs b/src/Client/Update/UpdateUtil.fs
index 6295fa63..3c7c3620 100644
--- a/src/Client/Update/UpdateUtil.fs
+++ b/src/Client/Update/UpdateUtil.fs
@@ -2,6 +2,7 @@ module Update.UpdateUtil
open ARCtrl
open Shared
+open Types.TableImport
open Fable.Remoting.Client
let download(filename, bytes:byte []) = bytes.SaveFileAs(filename)
@@ -15,21 +16,34 @@ module JsonImportHelper =
open ARCtrl
open JsonImport
- let updateWithMetadata (uploadedFile: ArcFiles) (state: SelectiveImportModalState) =
+ let updateWithMetadata (uploadedFile: ArcFiles) (state: SelectiveImportModalState) (selectedColumns: SelectedColumns []) =
if not state.ImportMetadata then failwith "Metadata must be imported"
/// This updates the existing tables based on import config (joinOptions)
let createUpdatedTables (arcTables: ResizeArray) =
[
- for it in state.ImportTables do
- let sourceTable = arcTables.[it.Index]
+ for importTable in state.ImportTables do
+
+ let selectedColumn = selectedColumns.[importTable.Index]
+ let selectedColumnIndices =
+ selectedColumn.Columns
+ |> Array.mapi (fun i item -> if item = false then Some i else None)
+ |> Array.choose (fun x -> x)
+ |> List.ofArray
+
+ let sourceTable = arcTables.[importTable.Index]
let appliedTable = ArcTable.init(sourceTable.Name)
- appliedTable.Join(sourceTable, joinOptions=state.ImportType)
+
+ let finalTable = Table.selectiveTablePrepare appliedTable sourceTable selectedColumnIndices
+ let values =
+ sourceTable.Columns
+ |> Array.map (fun item -> item.Cells |> Array.map (fun itemi -> itemi.ToString()))
+ appliedTable.Join(finalTable, joinOptions=state.ImportType)
appliedTable
]
|> ResizeArray
let arcFile =
match uploadedFile with
- | Assay a as arcFile->
+ | Assay a as arcFile ->
let tables = createUpdatedTables a.Tables
a.Tables <- tables
arcFile
diff --git a/src/Shared/ARCtrl.Helper.fs b/src/Shared/ARCtrl.Helper.fs
index 67abd0a4..c04bddf4 100644
--- a/src/Shared/ARCtrl.Helper.fs
+++ b/src/Shared/ARCtrl.Helper.fs
@@ -93,18 +93,22 @@ module Table =
///
/// The active/current table
/// The new table, which will be added to the existing one.
- let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) : ArcTable =
+ let selectiveTablePrepare (activeTable: ArcTable) (toJoinTable: ArcTable) (removeColumns: int list): ArcTable =
// Remove existing columns
- let mutable columnsToRemove = []
+ let mutable columnsToRemove = removeColumns
// find duplicate columns
let tablecopy = toJoinTable.Copy()
for header in activeTable.Headers do
let containsAtIndex = tablecopy.Headers |> Seq.tryFindIndex (fun h -> h = header)
if containsAtIndex.IsSome then
columnsToRemove <- containsAtIndex.Value::columnsToRemove
+
+ //Remove duplicates because unselected and already existing columns can overlap
+ let columnsToRemove = columnsToRemove |> Set.ofList |> Set.toList
+
tablecopy.RemoveColumns (Array.ofList columnsToRemove)
tablecopy.IteriColumns(fun i c0 ->
- let c1 = {c0 with Cells = [||]}
+ let c1 = {c0 with Cells = tablecopy.Columns.[i].Cells}
let c2 =
if c1.Header.isInput then
match activeTable.TryGetInputColumn() with
diff --git a/src/Shared/Shared.fs b/src/Shared/Shared.fs
index 028c701a..3ee48e0a 100644
--- a/src/Shared/Shared.fs
+++ b/src/Shared/Shared.fs
@@ -234,14 +234,14 @@ type IOntologyAPIv1 = {
///
getTermSuggestions : (int*string) -> Async
/// (nOfReturnedResults*queryString*parentOntology). If parentOntology = "" then isNull -> Error.
- getTermSuggestionsByParentTerm : (int*string*SwateObsolete.TermMinimal) -> Async
- getAllTermsByParentTerm : SwateObsolete.TermMinimal -> Async
+ getTermSuggestionsByParentTerm : (int*string*SwateObsolete.TermMinimal) -> Async
+ getAllTermsByParentTerm : SwateObsolete.TermMinimal -> Async
/// (nOfReturnedResults*queryString*parentOntology). If parentOntology = "" then isNull -> Error.
- getTermSuggestionsByChildTerm : (int*string*SwateObsolete.TermMinimal) -> Async
- getAllTermsByChildTerm : SwateObsolete.TermMinimal -> Async
+ getTermSuggestionsByChildTerm : (int*string*SwateObsolete.TermMinimal) -> Async
+ getAllTermsByChildTerm : SwateObsolete.TermMinimal -> Async
getTermsForAdvancedSearch : (AdvancedSearchTypes.AdvancedSearchOptions) -> Async
getUnitTermSuggestions : (int*string) -> Async
- getTermsByNames : SwateObsolete.TermSearchable [] -> Async
+ getTermsByNames : SwateObsolete.TermSearchable [] -> Async
// Tree related requests
getTreeByAccession : string -> Async