From 3256ba020d8ebb6f24857044e27f8351f5463bed Mon Sep 17 00:00:00 2001 From: kMutagene Date: Wed, 27 May 2020 16:23:03 +0200 Subject: [PATCH] #15, #18 : Add Building block insert and unit cell formatting --- SAFEOfficeAddInn.sln | 7 +- build.fsx | 22 ++++++- src/Client/Client.fs | 3 + src/Client/Client.fsproj | 1 + .../AnnotationTableMissingWarning.fs | 28 ++++++++ .../CustomComponents/AutocompleteDropdown.fs | 8 +-- src/Client/Messages.fs | 5 +- src/Client/OfficeInterop.fs | 64 +++++++++++++------ src/Client/Routing.fs | 8 +++ src/Client/Update.fs | 49 +++++++++++--- src/Client/Views/ActivityLogView.fs | 12 ++-- src/Client/Views/AddBuildingBlockView.fs | 10 ++- src/Client/Views/BaseView.fs | 34 ++++++++++ src/Client/Views/FilePickerView.fs | 7 +- src/Server/Properties/launchSettings.json | 16 ++++- src/Server/Server.fsproj | 1 + src/Server/Server.fsproj.user | 5 +- src/Server/web.config | 16 +++++ tools/manifestGenerator.fsx | 19 ++++++ tools/testDbSetup.fsx | 3 + 20 files changed, 273 insertions(+), 45 deletions(-) create mode 100644 src/Client/CustomComponents/AnnotationTableMissingWarning.fs create mode 100644 src/Server/web.config create mode 100644 tools/manifestGenerator.fsx create mode 100644 tools/testDbSetup.fsx diff --git a/SAFEOfficeAddInn.sln b/SAFEOfficeAddInn.sln index 00971205..63ea6c9e 100644 --- a/SAFEOfficeAddInn.sln +++ b/SAFEOfficeAddInn.sln @@ -13,7 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .config\dotnet-tools.json = .config\dotnet-tools.json global.json = global.json manifest.xml = manifest.xml - manifestGeneration.fsx = manifestGeneration.fsx package.json = package.json paket.dependencies = paket.dependencies paket.lock = paket.lock @@ -55,6 +54,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "generators", "generators", docsrc\generators\staticfile.fsx = docsrc\generators\staticfile.fsx EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{494903F3-B40F-4C45-BAF6-C89D8DD002C6}" + ProjectSection(SolutionItems) = preProject + tools\manifestGenerator.fsx = tools\manifestGenerator.fsx + tools\testDbSetup.fsx = tools\testDbSetup.fsx + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/build.fsx b/build.fsx index 29eb1038..5db3059e 100644 --- a/build.fsx +++ b/build.fsx @@ -163,19 +163,39 @@ Target.create "CreateDevCerts" (fun _ -> ) +Target.create "Bundle" (fun _ -> + let serverDir = Path.combine deployDir "Server" + let clientDir = Path.combine deployDir "Client" + let publicDir = Path.combine clientDir "public" + let publishArgs = sprintf "publish -c Release -o \"%s\"" serverDir + runDotNet publishArgs serverPath + + Shell.copyDir publicDir clientDeployPath FileFilter.allFiles +) + +Target.create "Setup" ignore + open Fake.Core.TargetOperators "Clean" ==> "InstallClient" ==> "Build" - "Clean" ==> "InstallClient" ==> "Run" +"Clean" +==> "InstallClient" +==> "Build" +==> "Bundle" + "Clean" ==> "InstallClient" ==> "OfficeDebug" +"InstallOfficeAddinTooling" +==> "CreateDevCerts" +==> "Setup" + Target.runOrDefaultWithArguments "Build" diff --git a/src/Client/Client.fs b/src/Client/Client.fs index bf3ecc4e..5ac3c5cb 100644 --- a/src/Client/Client.fs +++ b/src/Client/Client.fs @@ -84,6 +84,9 @@ let view (model : Model) (dispatch : Msg -> unit) = str "Footer content" ] + | Routing.Page.Home -> + div [] [str "soos"] + | _ -> div [ Style [MinHeight "100vh"; BackgroundColor model.SiteStyleState.ColorMode.BodyBackground; Color model.SiteStyleState.ColorMode.Text;] ] [ diff --git a/src/Client/Client.fsproj b/src/Client/Client.fsproj index 82e1a24f..3995f853 100644 --- a/src/Client/Client.fsproj +++ b/src/Client/Client.fsproj @@ -20,6 +20,7 @@ + diff --git a/src/Client/CustomComponents/AnnotationTableMissingWarning.fs b/src/Client/CustomComponents/AnnotationTableMissingWarning.fs new file mode 100644 index 00000000..c8ea205e --- /dev/null +++ b/src/Client/CustomComponents/AnnotationTableMissingWarning.fs @@ -0,0 +1,28 @@ +module CustomComponents.AnnotationTableMissingWarning + +open Fable.React +open Fable.React.Props +open Fulma +open Fulma.Extensions.Wikiki +open ExcelColors +open Model +open Messages + +let annotationTableMissingWarningComponent (model:Model) (dispatch: Msg-> unit) = + Notification.notification [ + Notification.Color IsWarning + Notification.Props [ + + ] + ] [ + Notification.delete [] [] + Heading.h5 [] [str "Warning: No Annotation table found in worksheet"] + Text.p [] [ + str "Your worksheet seems to contain no annotation table. You can create one by pressing the button below" + ] + Button.buttonComponent + model.SiteStyleState.ColorMode + true + "create annoation table" + (fun _ -> model.SiteStyleState.IsDarkMode |> CreateAnnotationTable |> ExcelInterop |> dispatch) + ] diff --git a/src/Client/CustomComponents/AutocompleteDropdown.fs b/src/Client/CustomComponents/AutocompleteDropdown.fs index ee3c547b..a0dcb9ac 100644 --- a/src/Client/CustomComponents/AutocompleteDropdown.fs +++ b/src/Client/CustomComponents/AutocompleteDropdown.fs @@ -24,9 +24,9 @@ let autocompleteDropdownComponent (model:Model) (dispatch: Msg -> unit) (isVisib BorderColor model.SiteStyleState.ColorMode.ControlForeground ]] ] [ - Table.table [Table.IsFullWidth] ( + Table.table [Table.IsFullWidth] [ if isLoading then - [ + tbody [] [ tr [] [ td [Style [TextAlign TextAlignOptions.Center]] [ Loading.loadingComponent @@ -35,8 +35,8 @@ let autocompleteDropdownComponent (model:Model) (dispatch: Msg -> unit) (isVisib ] ] else - suggestions - ) + tbody [] suggestions + ] ] diff --git a/src/Client/Messages.fs b/src/Client/Messages.fs index 6ce0f301..9e21c5d4 100644 --- a/src/Client/Messages.fs +++ b/src/Client/Messages.fs @@ -13,8 +13,11 @@ type ExcelInteropMsg = | InSync of string | TryExcel | FillSelection of string - | AddColumn of string + | AddColumn of colname:string * formatString:string + | FormatColumn of colname:string * formatString:string | CreateAnnotationTable of bool + | AnnotationtableCreated of string + | AnnotationTableExists of bool type SimpleTermSearchMsg = | SearchTermTextChange of string diff --git a/src/Client/OfficeInterop.fs b/src/Client/OfficeInterop.fs index 347c7132..0fc7babf 100644 --- a/src/Client/OfficeInterop.fs +++ b/src/Client/OfficeInterop.fs @@ -37,6 +37,14 @@ let createEmptyMatrixForTables (colCount:int) (rowCount:int) value = |] :> IList> |] :> IList>> + +let createValueMatrix (colCount:int) (rowCount:int) value = + ResizeArray([ + for outer in 0 .. rowCount-1 do + let tmp = Array.zeroCreate colCount |> Seq.map (fun _ -> Some (value |> box)) + ResizeArray(tmp) + ]) + let createAnnotationTable (isDark:bool) = Excel.run(fun context -> let tableRange = context.workbook.getSelectedRange() @@ -62,31 +70,28 @@ let createAnnotationTable (isDark:bool) = annotationTable.style <- style - if tableRange.columnCount < 2. then // only one column there, so add data col to end. - - let dataCol = createEmptyMatrixForTables 1 (int tableRange.rowCount) "" - - (annotationTable.columns.getItemAt 0.).name <- "Sample Name" - annotationTable.columns.add(-1.,U4.Case1 dataCol, "Data File Name") |> ignore - - sheet.getUsedRange().format.autofitColumns() - sheet.getUsedRange().format.autofitRows() - - sprintf "Annotation Table created in [%s] with dimensions %.0f + 1 mandatory c x (%.0f + 1h)r" tableRange.address tableRange.columnCount (tableRange.rowCount - 1.) - else + (annotationTable.columns.getItemAt 0.).name <- "Source Name" - (annotationTable.columns.getItemAt 0.).name <- "Sample Name" - (annotationTable.columns.getItemAt (tableRange.columnCount - 1.)).name <- "Data File Name" + sheet.getUsedRange().format.autofitColumns() + sheet.getUsedRange().format.autofitRows() - sheet.getUsedRange().format.autofitColumns() - sheet.getUsedRange().format.autofitRows() + sprintf "Annotation Table created in [%s] with dimensions %.0f c x (%.0f + 1h)r" tableRange.address tableRange.columnCount (tableRange.rowCount - 1.) - sprintf "Annotation Table created in [%s] with dimensions %.0fc x (%.0f + 1h)r. Adapted style to %s" tableRange.address tableRange.columnCount (tableRange.rowCount - 1.) style - ) //.catch (fun e -> e |> unbox |> fun x -> x.Message) ) +let checkIfAnnotationTableIsPresent () = + Excel.run(fun context -> + let tableRange = context.workbook.getSelectedRange() + let sheet = context.workbook.worksheets.getActiveWorksheet() + //delete table with the same name if present because there can only be one chosen one <3 + let table = sheet.tables.getItemOrNullObject("annotationTable") + context.sync() + .``then``( fun _ -> + not table.isNullObject + ) + ) let addAnnotationColumn (colName:string) = @@ -100,17 +105,38 @@ let addAnnotationColumn (colName:string) = context.sync().``then``( fun _ -> let colCount = tableRange.columnCount let rowCount = tableRange.rowCount |> int + //create an empty column to insert let testCol = createEmptyMatrixForTables 1 rowCount "" let _ = annotationTable.columns.add( - colCount - 1., //last column should always be the predefined results column + colCount, values = U4.Case1 testCol, name=colName ) sprintf "%s column was added." colName ) ) +let changeTableColumnFormat (colName:string) (format:string) = + Excel.run(fun context -> + let sheet = context.workbook.worksheets.getActiveWorksheet() + let annotationTable = sheet.tables.getItem("annotationTable") + + let colRange = (annotationTable.columns.getItem (U2.Case2 colName)).getDataBodyRange() + colRange.load(U2.Case2 (ResizeArray(["columnCount";"rowCount"]))) |> ignore + + context.sync().``then``( fun _ -> + let rowCount = colRange.rowCount |> int + //create an empty column to insert + let formats = createValueMatrix 1 rowCount format + + colRange.numberFormat <- formats + + sprintf "format of %s was changed to %s" colName format + ) + ) + + let fillValue (v:string) = Excel.run(fun context -> let range = context.workbook.getSelectedRange() diff --git a/src/Client/Routing.fs b/src/Client/Routing.fs index b3ef61b7..c985571b 100644 --- a/src/Client/Routing.fs +++ b/src/Client/Routing.fs @@ -20,6 +20,14 @@ type Page = | Page.ActivityLog -> "/#ActivityLog" | Page.NotFound -> "/#NotFound" + static member toString = function + | Page.Home -> "" + | Page.TermSearch -> "TermSearch" + | Page.AddBuildingBlock -> "AddBuildingBlock" + | Page.FilePicker -> "FilePicker" + | Page.ActivityLog -> "ActivityLog" + | Page.NotFound -> "NotFound" + /// The URL is turned into a Result. let pageParser : Parser Page,_> = oneOf [ diff --git a/src/Client/Update.fs b/src/Client/Update.fs index 9fd9d1bc..61262a91 100644 --- a/src/Client/Update.fs +++ b/src/Client/Update.fs @@ -53,11 +53,26 @@ let handleExcelInteropMsg (excelInteropMsg: ExcelInteropMsg) (currentState:Excel | SyncContext passthroughMessage -> currentState, - Cmd.OfPromise.either - OfficeInterop.syncContext - passthroughMessage - (fun _ -> ExcelInterop (InSync passthroughMessage)) - (GenericError >> Dev) + Cmd.batch [ + Cmd.OfPromise.either + OfficeInterop.checkIfAnnotationTableIsPresent + () + (AnnotationTableExists >> ExcelInterop) + (GenericError >> Dev) + Cmd.OfPromise.either + OfficeInterop.syncContext + passthroughMessage + (fun _ -> ExcelInterop (InSync passthroughMessage)) + (GenericError >> Dev) + ] + + | AnnotationTableExists exists -> + let nextState = { + currentState with + HasAnnotationTable = exists + } + + nextState,Cmd.none | InSync passthroughMessage -> currentState, @@ -81,11 +96,20 @@ let handleExcelInteropMsg (excelInteropMsg: ExcelInteropMsg) (currentState:Excel (SyncContext >> ExcelInterop) (GenericError >> Dev) - | AddColumn columnValue -> + | AddColumn (colName,format) -> currentState, + Cmd.OfPromise.either OfficeInterop.addAnnotationColumn - columnValue + colName + (fun _ -> (colName,format) |> FormatColumn |> ExcelInterop) + (GenericError >> Dev) + + | FormatColumn (colName,format) -> + currentState, + Cmd.OfPromise.either + (OfficeInterop.changeTableColumnFormat colName) + format (SyncContext >> ExcelInterop) (GenericError >> Dev) @@ -94,9 +118,18 @@ let handleExcelInteropMsg (excelInteropMsg: ExcelInteropMsg) (currentState:Excel Cmd.OfPromise.either OfficeInterop.createAnnotationTable isDark - (SyncContext >> ExcelInterop) + (AnnotationtableCreated >> ExcelInterop) (GenericError >> Dev) + | AnnotationtableCreated range -> + let nextState = { + currentState with + HasAnnotationTable = true + } + + nextState,Cmd.ofMsg(range |> SyncContext |> ExcelInterop) + + let handleSimpleTermSearchMsg (simpleTermSearchMsg: SimpleTermSearchMsg) (currentState:SimpleTermSearchState) : SimpleTermSearchState * Cmd = match simpleTermSearchMsg with | SearchTermTextChange newTerm -> diff --git a/src/Client/Views/ActivityLogView.fs b/src/Client/Views/ActivityLogView.fs index 42a99e63..b0825311 100644 --- a/src/Client/Views/ActivityLogView.fs +++ b/src/Client/Views/ActivityLogView.fs @@ -1,6 +1,7 @@ module ActivityLogView open Fulma +open Fable.React open Model //TO-DO: Save log as tab seperated file @@ -9,7 +10,10 @@ let activityLogComponent (model:Model) = Table.table [ Table.IsFullWidth Table.Props [ExcelColors.colorBackground model.SiteStyleState.ColorMode] - ] ( - model.DevState.Log - |> List.map LogItem.toTableRow - ) \ No newline at end of file + ] [ + tbody [] ( + model.DevState.Log + |> List.map LogItem.toTableRow + ) + ] + \ No newline at end of file diff --git a/src/Client/Views/AddBuildingBlockView.fs b/src/Client/Views/AddBuildingBlockView.fs index 951fc49a..d5ade255 100644 --- a/src/Client/Views/AddBuildingBlockView.fs +++ b/src/Client/Views/AddBuildingBlockView.fs @@ -173,7 +173,15 @@ let addBuildingBlockComponent (model:Model) (dispatch:Msg -> unit) = Button.Props [Disabled true] Button.IsFullWidth //TODO: add fill support via Excel interop here - //Button.OnClick (fun _ -> model.TermSearchState.Simple.TermSearchText |> FillSelection |> ExcelInterop |> dispatch) + Button.OnClick ( + let format = + match model.AddBuildingBlockState.UnitTerm with + | Some unit -> + sprintf "0.00 \"%s\"" unit.Name + | _ -> "0.00" + let colName = model.AddBuildingBlockState.CurrentBuildingBlock |> AnnotationBuildingBlock.toAnnotationTableHeader + fun _ -> (colName,format) |> AddColumn |> ExcelInterop |> dispatch + ) ] [ str "Insert this annotation building block" diff --git a/src/Client/Views/BaseView.fs b/src/Client/Views/BaseView.fs index 23ed16b2..558c3654 100644 --- a/src/Client/Views/BaseView.fs +++ b/src/Client/Views/BaseView.fs @@ -9,6 +9,23 @@ open Messages open CustomComponents +let createNavigationTab (pageLink: Routing.Page) (model:Model) (dispatch:Msg-> unit) = + let isActive = (model.PageState.CurrentPage = pageLink) + Tabs.tab [Tabs.Tab.IsActive isActive] [ + a [ Href (Routing.Page.toPath pageLink) + Style [ + if isActive then + BorderColor model.SiteStyleState.ColorMode.Accent + BackgroundColor model.SiteStyleState.ColorMode.BodyBackground + Color model.SiteStyleState.ColorMode.Accent + else + BorderBottomColor model.SiteStyleState.ColorMode.Accent + ] + ] [ + Text.span [] [str (Routing.Page.toString pageLink)] + ] + ] + /// The base react component for all views in the app. contains the navbar and takes body and footer components to create the full view. let baseViewComponent (model: Model) (dispatch: Msg -> unit) (bodyChildren: ReactElement list) (footerChildren: ReactElement list) = div [ Style [MinHeight "100vh"; BackgroundColor model.SiteStyleState.ColorMode.BodyBackground; Color model.SiteStyleState.ColorMode.Text;] @@ -18,6 +35,23 @@ let baseViewComponent (model: Model) (dispatch: Msg -> unit) (bodyChildren: Reac Container.IsFluid ] [ br [] + Tabs.tabs[ + Tabs.IsCentered; Tabs.IsFullWidth; Tabs.IsBoxed + Tabs.Props [ + Style [ + BackgroundColor model.SiteStyleState.ColorMode.BodyBackground + ] + ] + ] [ + createNavigationTab Routing.Page.AddBuildingBlock model dispatch + createNavigationTab Routing.Page.TermSearch model dispatch + createNavigationTab Routing.Page.FilePicker model dispatch + createNavigationTab Routing.Page.ActivityLog model dispatch + ] + br [] + + if (not model.ExcelState.HasAnnotationTable) then + CustomComponents.AnnotationTableMissingWarning.annotationTableMissingWarningComponent model dispatch yield! bodyChildren diff --git a/src/Client/Views/FilePickerView.fs b/src/Client/Views/FilePickerView.fs index 2e8e4c6f..19f3175e 100644 --- a/src/Client/Views/FilePickerView.fs +++ b/src/Client/Views/FilePickerView.fs @@ -49,7 +49,7 @@ let filePickerComponent (model:Model) (dispatch:Msg -> unit) = File.input [ Props [ Multiple true - OnInput (fun ev -> + OnChange (fun ev -> let files : FileList = ev.target?files let fileNames = @@ -72,7 +72,8 @@ let filePickerComponent (model:Model) (dispatch:Msg -> unit) = ] ] ] - Table.table [Table.IsFullWidth] ( - createFileList model dispatch) + Table.table [Table.IsFullWidth] [ + tbody [] (createFileList model dispatch) + ] ] \ No newline at end of file diff --git a/src/Server/Properties/launchSettings.json b/src/Server/Properties/launchSettings.json index 8b2f5ed2..6f7f9bca 100644 --- a/src/Server/Properties/launchSettings.json +++ b/src/Server/Properties/launchSettings.json @@ -2,9 +2,13 @@ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, + "iis": { + "applicationUrl": "https://localhost/Server", + "sslPort": 0 + }, "iisExpress": { - "applicationUrl": "http://localhost:52317/", - "sslPort": 44338 + "applicationUrl": "https://localhost/Swate/", + "sslPort": 0 } }, "profiles": { @@ -15,6 +19,14 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, + "IIS": { + "commandName": "IIS", + "launchBrowser": true, + "launchUrl": "https://localhost/Swate", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, "Server": { "commandName": "Project", "launchBrowser": true, diff --git a/src/Server/Server.fsproj b/src/Server/Server.fsproj index 7c7ec2e8..0436aab0 100644 --- a/src/Server/Server.fsproj +++ b/src/Server/Server.fsproj @@ -3,6 +3,7 @@ Exe netcoreapp3.0 + 6de80bdf-2a05-4cf7-a1a8-d08581dfa887 diff --git a/src/Server/Server.fsproj.user b/src/Server/Server.fsproj.user index cff74a90..a08889bf 100644 --- a/src/Server/Server.fsproj.user +++ b/src/Server/Server.fsproj.user @@ -1,6 +1,9 @@  - IIS Express + IIS + + + ProjectDebugger \ No newline at end of file diff --git a/src/Server/web.config b/src/Server/web.config new file mode 100644 index 00000000..0b027cc3 --- /dev/null +++ b/src/Server/web.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/manifestGenerator.fsx b/tools/manifestGenerator.fsx new file mode 100644 index 00000000..d25031a7 --- /dev/null +++ b/tools/manifestGenerator.fsx @@ -0,0 +1,19 @@ +open System.Xml. + +type ManifestIcon = { + Size : int + Id :string + Path :string +} +with + static member toXmlRessource (icon:ManifestIcon) = + () + +type ManifestControl = { + ControlType : string + Id : string + Label : string + ToolTip : string + Icons : ManifestIcon [] + Action : string +} \ No newline at end of file diff --git a/tools/testDbSetup.fsx b/tools/testDbSetup.fsx new file mode 100644 index 00000000..5ee9eaf2 --- /dev/null +++ b/tools/testDbSetup.fsx @@ -0,0 +1,3 @@ +#load "../src/Shared/Shared.fs" +#load "../src/Server/OntologyDb.fs" +