diff --git a/.gitignore b/.gitignore index 0d7f3130..31a62efa 100644 --- a/.gitignore +++ b/.gitignore @@ -352,6 +352,7 @@ MigrationBackup/ pkg/ tmp/ +output/ .fsdocs/ /tests/FsSpreadsheet.JsNativeTests/fable/**/*.js /tests/FsSpreadsheet.ExcelIO.Tests/TestFiles/WRITE_*.xlsx diff --git a/FsSpreadsheet.sln b/FsSpreadsheet.sln index 556dfc60..385997eb 100644 --- a/FsSpreadsheet.sln +++ b/FsSpreadsheet.sln @@ -49,6 +49,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JS", "JS", "{ADCF7D08-F2EE- EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TestUtils", "tests\TestUtils\TestUtils.fsproj", "{60678E53-EDC4-4ADE-A9EE-B194BDC76B37}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Speedtest", "tests\Speedtest\Speedtest.fsproj", "{6DAD5C65-64CA-4ED4-B609-2D068F021024}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -191,6 +193,18 @@ Global {60678E53-EDC4-4ADE-A9EE-B194BDC76B37}.Release|x64.Build.0 = Release|Any CPU {60678E53-EDC4-4ADE-A9EE-B194BDC76B37}.Release|x86.ActiveCfg = Release|Any CPU {60678E53-EDC4-4ADE-A9EE-B194BDC76B37}.Release|x86.Build.0 = Release|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Debug|x64.ActiveCfg = Debug|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Debug|x64.Build.0 = Debug|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Debug|x86.ActiveCfg = Debug|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Debug|x86.Build.0 = Debug|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Release|Any CPU.Build.0 = Release|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Release|x64.ActiveCfg = Release|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Release|x64.Build.0 = Release|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Release|x86.ActiveCfg = Release|Any CPU + {6DAD5C65-64CA-4ED4-B609-2D068F021024}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -208,6 +222,7 @@ Global {96E12F19-B25A-415E-B965-F9DE8D713C67} = {F77AD108-C6B4-46BB-B7BC-13573F45F876} {ADCF7D08-F2EE-4DFD-A96A-7E0134A1546F} = {F77AD108-C6B4-46BB-B7BC-13573F45F876} {60678E53-EDC4-4ADE-A9EE-B194BDC76B37} = {F77AD108-C6B4-46BB-B7BC-13573F45F876} + {6DAD5C65-64CA-4ED4-B609-2D068F021024} = {F77AD108-C6B4-46BB-B7BC-13573F45F876} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0EDE6697-0F13-4DB1-AC56-12C15A72D395} diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1a52d90a..0aa79480 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,9 @@ +### 5.1.2+21fd6e8 (Released 2024-1-30) +* Additions: + * [[#21fd6e8](https://github.com/CSBiology/FsSpreadsheet/commit/21fd6e82f6d9eab48b4197a56a5c9b9f06b51b0e)] improve reader speed by adjusting sharedStringTable usage +* Bugfixes: + * [[#66b6713](https://github.com/CSBiology/FsSpreadsheet/commit/66b6713381be3f133c58db7ca78fd36bd63c0ad1)] fix npm publish target + ### 5.1.1+e7cc638 (Released 2024-1-26) * Additions: * [[#e7cc638](https://github.com/CSBiology/FsSpreadsheet/commit/e7cc638b170c570005b6cd8378d1e0ac31075be7)] improve rowWithRange SkipSearch performance diff --git a/build/ReleaseTasks.fs b/build/ReleaseTasks.fs index 10bb74ff..4f267dc2 100644 --- a/build/ReleaseTasks.fs +++ b/build/ReleaseTasks.fs @@ -15,7 +15,7 @@ open Fake.Tools open Fake.IO open Fake.IO.Globbing.Operators -let createTag = BuildTask.create "CreateTag" [clean; build; runTests; pack] { +let createTag = BuildTask.create "CreateTag" [clean; build; runTests] { if promptYesNo (sprintf "tagging branch with %s OK?" stableVersionTag ) then Git.Branches.tag "" stableVersionTag Git.Branches.pushTag "" projectRepo stableVersionTag @@ -23,7 +23,7 @@ let createTag = BuildTask.create "CreateTag" [clean; build; runTests; pack] { failwith "aborted" } -let createPrereleaseTag = BuildTask.create "CreatePrereleaseTag" [setPrereleaseTag; clean; build; runTests; packPrerelease] { +let createPrereleaseTag = BuildTask.create "CreatePrereleaseTag" [setPrereleaseTag; clean; build; runTests] { if promptYesNo (sprintf "tagging branch with %s OK?" prereleaseTag ) then Git.Branches.tag "" prereleaseTag Git.Branches.pushTag "" projectRepo prereleaseTag @@ -32,7 +32,7 @@ let createPrereleaseTag = BuildTask.create "CreatePrereleaseTag" [setPrereleaseT } -let publishNuget = BuildTask.create "PublishNuget" [clean; build; runTests; pack] { +let publishNuget = BuildTask.create "PublishNuget" [clean; build; runTests; packDotNet] { let targets = (!! (sprintf "%s/*.*pkg" pkgDir )) for target in targets do printfn "%A" target let msg = sprintf "[.NET] release package with version %s?" stableVersionTag @@ -45,7 +45,7 @@ let publishNuget = BuildTask.create "PublishNuget" [clean; build; runTests; pack else failwith "aborted" } -let publishNugetPrerelease = BuildTask.create "PublishNugetPrerelease" [clean; build; runTests; packPrerelease] { +let publishNugetPrerelease = BuildTask.create "PublishNugetPrerelease" [clean; build; runTests; packDotNetPrerelease] { let targets = (!! (sprintf "%s/*.*pkg" pkgDir )) for target in targets do printfn "%A" target let msg = sprintf "[.NET] release package with version %s?" prereleaseTag diff --git a/package.json b/package.json index 808e057e..2e65b64c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fslab/fsspreadsheet", - "version": "5.1.1", + "version": "5.1.2", "description": "Minimal spreadsheet creation and manipulation using exceljs io.", "type": "module", "main": "Xlsx.js", diff --git a/src/FsSpreadsheet.ExcelIO/Cell.fs b/src/FsSpreadsheet.ExcelIO/Cell.fs index 06870a6c..6ba80fde 100644 --- a/src/FsSpreadsheet.ExcelIO/Cell.fs +++ b/src/FsSpreadsheet.ExcelIO/Cell.fs @@ -242,7 +242,7 @@ module Cell = /// /// Maps a Cell to the value string using a shared string table. /// - let tryGetValue (sharedStringTable:SharedStringTable Option) (cell:Cell) = + let tryGetValue (sharedStringTable:SST Option) (cell:Cell) = match cell |> tryGetType with | Some (CellValues.SharedString) when sharedStringTable.IsSome-> let sharedStringTable = sharedStringTable.Value @@ -251,8 +251,7 @@ module Cell = |> Option.map ( CellValue.getValue >> int - >> fun i -> SharedStringTable.getText i sharedStringTable - >> SharedStringTable.SharedStringItem.getText + >> fun i -> sharedStringTable.[i] ) | _ -> @@ -263,9 +262,9 @@ module Cell = /// /// Maps a Cell to the value string using a sharedStringTable. /// - let getValue (sharedStringTable : SharedStringTable Option) (cell : Cell) = + let getValue (sharedStringTable : SST Option) (cell : Cell) = match cell |> tryGetType with - | Some (CellValues.SharedString) when sharedStringTable.IsSome-> + | Some (CellValues.SharedString) when sharedStringTable.IsSome -> let sharedStringTable = sharedStringTable.Value let sharedStringTableIndex = cell @@ -273,9 +272,7 @@ module Cell = |> CellValue.getValue |> int - sharedStringTable - |> SharedStringTable.getText sharedStringTableIndex - |> SharedStringTable.SharedStringItem.getText + sharedStringTable.[sharedStringTableIndex] | _ -> cell |> getCellValue @@ -291,15 +288,15 @@ module Cell = /// /// Includes a value from the sharedStringTable in Cell.CellValue.Text. /// - let includeSharedStringValue (sharedStringTable:SharedStringTable) (cell:Cell) = + let includeSharedStringValue (sharedStringTable:SST) (cell:Cell) = if not (isNull cell.DataType) then match cell |> tryGetType with | Some (CellValues.SharedString) -> let index = int cell.InnerText - match sharedStringTable |> Seq.tryItem index with + match sharedStringTable |> Array.tryItem index with | Some value -> cell.DataType <- EnumValue(CellValues.String) - cell.CellValue.Text <- value.InnerText + cell.CellValue.Text <- value | None -> cell.CellValue.Text <- cell.InnerText cell diff --git a/src/FsSpreadsheet.ExcelIO/FsExtensions.fs b/src/FsSpreadsheet.ExcelIO/FsExtensions.fs index d2ee0bcd..70d3eaca 100644 --- a/src/FsSpreadsheet.ExcelIO/FsExtensions.fs +++ b/src/FsSpreadsheet.ExcelIO/FsExtensions.fs @@ -58,8 +58,7 @@ module FsExtensions = /// /// Creates an FsCell on the basis of an XlsxCell. Uses a SharedStringTable if present to get the XlsxCell's value. /// - static member ofXlsxCell (doc : Packaging.SpreadsheetDocument) (xlsxCell : Cell) = - let sst = Spreadsheet.tryGetSharedStringTable doc + static member ofXlsxCell (doc : Packaging.SpreadsheetDocument) (sst : SST option) (xlsxCell : Cell) = let cellValueString = Cell.getValue sst xlsxCell let col, row = xlsxCell.CellReference.Value |> CellReference.toIndices let dt = @@ -90,6 +89,42 @@ module FsExtensions = //let dt, v = DataType.InferCellValue v FsCell.createWithDataType dt (int row) (int col) (cellValue) + /// + /// Creates an FsCell on the basis of an XlsxCell. Uses a SharedStringTable if present to get the XlsxCell's value. + /// + static member tryOfXlsxCell (doc : Packaging.SpreadsheetDocument) (sst : SST option) (xlsxCell : Cell) = + Cell.tryGetValue sst xlsxCell + |> Option.map (fun cellValueString -> + let col, row = xlsxCell.CellReference.Value |> CellReference.toIndices + let dt = + try DataType.ofXlsXCell doc xlsxCell + with _ -> DataType.Number // default is number + let mutable cellValue : obj = cellValueString + match dt with + | Date -> + try + // datetime is written as float counting days since 1900. + // We use the .NET helper because we really do not want to deal with datetime issues. + cellValue <- System.DateTime.FromOADate(float cellValueString) + with + | _ -> () + | Boolean -> + // boolean is written as int/float either 0 or null + match cellValueString.ToLower() with + | "1" | "true" -> cellValue <- true + | "0" | "false" -> cellValue <- false + | _ -> () + | Number -> + try + cellValue <- float cellValueString + with + | _ -> + () + | Empty | String -> () + //let dt, v = DataType.InferCellValue v + FsCell.createWithDataType dt (int row) (int col) (cellValue) + ) + static member toXlsxCell (doc : Packaging.SpreadsheetDocument) (cell : FsCell) = Cell.fromValueWithDataType doc (uint32 cell.ColumnNumber) (uint32 cell.RowNumber) (cell.ValueAsString()) cell.DataType @@ -201,7 +236,7 @@ module FsExtensions = /// Creates an FsWorkbook from a given SpreadsheetDocument. /// static member fromSpreadsheetDocument (doc : Packaging.SpreadsheetDocument) = - let sst = Spreadsheet.tryGetSharedStringTable doc + let sst = Spreadsheet.tryGetSharedStringTable doc |> Option.map SharedStringTable.toSST let xlsxWorkbookPart = Spreadsheet.getWorkbookPart doc let xlsxSheets = try @@ -227,8 +262,9 @@ module FsExtensions = fun xlsxSheet -> let sheetId = Sheet.getID xlsxSheet let xlsxCells = - Spreadsheet.getCellsBySheetID sheetId doc - |> Seq.map (FsCell.ofXlsxCell doc) + Spreadsheet.getCellsBySheetID sheetId doc true + |> Seq.toArray + |> Array.choose (FsCell.tryOfXlsxCell doc sst) let assocXlsxTables = xlsxTables |> Seq.tryPick (fun (sid,ts) -> if sid = sheetId then Some ts else None) diff --git a/src/FsSpreadsheet.ExcelIO/FsSpreadsheet.ExcelIO.fsproj b/src/FsSpreadsheet.ExcelIO/FsSpreadsheet.ExcelIO.fsproj index aaf316d2..d4f73109 100644 --- a/src/FsSpreadsheet.ExcelIO/FsSpreadsheet.ExcelIO.fsproj +++ b/src/FsSpreadsheet.ExcelIO/FsSpreadsheet.ExcelIO.fsproj @@ -33,7 +33,7 @@ - + diff --git a/src/FsSpreadsheet.ExcelIO/Row.fs b/src/FsSpreadsheet.ExcelIO/Row.fs index 4f9d38f4..9af080dc 100644 --- a/src/FsSpreadsheet.ExcelIO/Row.fs +++ b/src/FsSpreadsheet.ExcelIO/Row.fs @@ -192,7 +192,7 @@ module Row = row) /// If the row contains a value at the given index, returns it. Returns none if not. - let tryGetValueAt (sst : SharedStringTable Option) index (row : Row) = + let tryGetValueAt (sst : SST Option) index (row : Row) = row |> tryGetCellAt index |> Option.bind (Cell.tryGetValue sst) @@ -257,7 +257,7 @@ module Row = |> extendSpanRight offset /// Maps the cells of the given row to tuples of 1-based column indices and the value strings using a sharedStringTable. - let getIndexedValues (sst : SharedStringTable Option) (row : Row) = + let getIndexedValues (sst : SST Option) (row : Row) = row |> toCellSeq |> Seq.choose (fun cell -> @@ -273,18 +273,18 @@ module Row = /// Maps the cells of the given row to the value strings. - let getRowValues (sst : SharedStringTable Option) (row : Row) = + let getRowValues (sst : SST Option) (row : Row) = row |> toCellSeq |> Seq.choose (Cell.tryGetValue sst) /// Maps each cell of the given row to each respective value strings if it exists, else returns None. - let tryGetRowValues (sst : SharedStringTable option) (row : Row) = + let tryGetRowValues (sst : SST option) (row : Row) = toCellSeq row |> Seq.map (Cell.tryGetValue sst) /// Maps the cells of the given row to the value strings for all existing cells. - let getPresentRowValues (sst : SharedStringTable option) (row : Row) = + let getPresentRowValues (sst : SST option) (row : Row) = toCellSeq row |> Seq.choose (Cell.tryGetValue sst) @@ -369,6 +369,6 @@ module Row = |> appendCell cell /// Includes a value from a sharedStringTable in the cells of the row. - let includeSharedStringValue (sst : SharedStringTable) (row : Row) = + let includeSharedStringValue (sst : SST) (row : Row) = row |> mapCells (Cell.includeSharedStringValue sst) diff --git a/src/FsSpreadsheet.ExcelIO/SharedStringTable.fs b/src/FsSpreadsheet.ExcelIO/SharedStringTable.fs index 1359fe7a..4a1557c0 100644 --- a/src/FsSpreadsheet.ExcelIO/SharedStringTable.fs +++ b/src/FsSpreadsheet.ExcelIO/SharedStringTable.fs @@ -5,6 +5,8 @@ open DocumentFormat.OpenXml.Packaging open DocumentFormat.OpenXml +type SST = string [] + /// Functions for working with SharedStringTables. module SharedStringTable = @@ -99,6 +101,9 @@ module SharedStringTable = try spreadsheetDocument.WorkbookPart.SharedStringTablePart.SharedStringTable |> Some with | _ -> None - + let toSST (sst : SharedStringTable) = + sst.Elements() + |> Seq.toArray + |> Array.map SharedStringItem.getText diff --git a/src/FsSpreadsheet.ExcelIO/SheetData.fs b/src/FsSpreadsheet.ExcelIO/SheetData.fs index 6be3f2e6..1e2939e9 100644 --- a/src/FsSpreadsheet.ExcelIO/SheetData.fs +++ b/src/FsSpreadsheet.ExcelIO/SheetData.fs @@ -120,19 +120,19 @@ module SheetData = /// Gets the string value of the cell at the given 1-based column and rowIndex, if it exists, else returns None. - let tryGetRowValuesAt (sst : SharedStringTable Option) rowIndex (sheet : SheetData) = + let tryGetRowValuesAt (sst : SST Option) rowIndex (sheet : SheetData) = sheet |> tryGetRowAt rowIndex |> Option.map (Row.getRowValues sst) /// Gets the string values of the row at the given 1-based rowIndex. - let getRowValuesAt (sst : SharedStringTable Option) rowIndex (sheet : SheetData) = + let getRowValuesAt (sst : SST Option) rowIndex (sheet : SheetData) = sheet |> getRowAt rowIndex |> Row.getRowValues sst /// Maps the cells of the given row to tuples of 1-based column indices and the value strings using a sharedStringTable, if it exists, else returns None. - let tryGetIndexedRowValuesAt (sst : SharedStringTable Option) rowIndex (sheet : SheetData) = + let tryGetIndexedRowValuesAt (sst : SST Option) rowIndex (sheet : SheetData) = sheet |> tryGetRowAt rowIndex |> Option.map (Row.getIndexedValues sst) @@ -214,13 +214,13 @@ module SheetData = |> Row.getCellAt columnIndex /// Gets the string value of the cell at the given 1-based column- and rowIndex using a sharedStringTable. - let getCellValueAt (sst : SharedStringTable Option) (rowIndex : uint32) (columnIndex : uint32) (sheetData : SheetData) = + let getCellValueAt (sst : SST Option) (rowIndex : uint32) (columnIndex : uint32) (sheetData : SheetData) = sheetData |> getCellAt rowIndex columnIndex |> Cell.getValue sst /// Gets the string value of the cell at the given 1-based column- and rowIndex using a sharedStringTable if it exists. Else returns None. - let tryGetCellValueAt (sst : SharedStringTable Option) (rowIndex: uint32) (columnIndex : uint32) (sheetData:SheetData) = + let tryGetCellValueAt (sst : SST Option) (rowIndex: uint32) (columnIndex : uint32) (sheetData:SheetData) = sheetData |> tryGetCellAt rowIndex columnIndex |> Option.bind (Cell.tryGetValue sst) @@ -279,7 +279,7 @@ module SheetData = sheet /// Includes a value from sharedStringTable in the cells of the rows of the sheetData - let includeSharedStringValue (sharedStringTable : SharedStringTable) (sheetData : SheetData) = + let includeSharedStringValue (sharedStringTable : SST) (sheetData : SheetData) = sheetData |> mapRows (Row.includeSharedStringValue sharedStringTable) @@ -288,7 +288,7 @@ module SheetData = //---------------------------------------------------------------------------------------------------------------------- /// Reads the values of all cells from a sheetData and a sharedStringTable and converts them into a sparse matrix. Values are stored sparsely in a dictionary, with the key being a row index and column index tuple. - let toSparseValueMatrix (sst : SharedStringTable) sheetData = + let toSparseValueMatrix (sst : SST) sheetData = let rows = getRows sheetData let noOfRows = countRows sheetData let noOfCols = diff --git a/src/FsSpreadsheet.ExcelIO/Spreadsheet.fs b/src/FsSpreadsheet.ExcelIO/Spreadsheet.fs index a5d8750f..a0c0370f 100644 --- a/src/FsSpreadsheet.ExcelIO/Spreadsheet.fs +++ b/src/FsSpreadsheet.ExcelIO/Spreadsheet.fs @@ -174,6 +174,7 @@ module Spreadsheet = let workbookPart = spreadsheetDocument.WorkbookPart let worksheetPart = Worksheet.WorksheetPart.getByID sheet.Id.Value workbookPart let stringTablePart = getOrInitSharedStringTablePart spreadsheetDocument + let sst = SharedStringTable.toSST stringTablePart.SharedStringTable seq { use reader = OpenXmlReader.Create(worksheetPart) @@ -183,7 +184,7 @@ module Spreadsheet = row.Elements() |> Seq.iter (fun item -> let cell = item :?> Cell - Cell.includeSharedStringValue stringTablePart.SharedStringTable cell |> ignore + Cell.includeSharedStringValue sst cell |> ignore ) yield row } @@ -192,12 +193,17 @@ module Spreadsheet = /// /// Returns a 1D-sequence of Cells for the given Sheet of the given SpreadsheetDocument. /// - let getCellsBySheet (sheet : Sheet) (spreadsheetDocument : SpreadsheetDocument) = + let getCellsBySheet (sheet : Sheet) (spreadsheetDocument : SpreadsheetDocument) (skipSST : bool) = let workbookPart = spreadsheetDocument.WorkbookPart let worksheetPart = Worksheet.WorksheetPart.getByID sheet.Id.Value workbookPart + let includeSSV = + if skipSST then id + else match tryGetSharedStringTable spreadsheetDocument with - | Some sst -> Cell.includeSharedStringValue sst + | Some sst -> + let sstArray = sst |> SharedStringTable.toSST + Cell.includeSharedStringValue sstArray | None -> id seq { use reader = OpenXmlReader.Create(worksheetPart) @@ -213,18 +219,18 @@ module Spreadsheet = /// Returns a 1D-sequence of Cells for the given sheetIndex of the given SpreadsheetDocument. /// /// SheetIndices are 1-based. - let getCellsBySheetIndex (sheetIndex : uint) (spreadsheetDocument : SpreadsheetDocument) = + let getCellsBySheetIndex (sheetIndex : uint) (spreadsheetDocument : SpreadsheetDocument) (skipSST : bool) = match Sheet.tryItem sheetIndex spreadsheetDocument with - | Some sheet -> getCellsBySheet sheet spreadsheetDocument + | Some sheet -> getCellsBySheet sheet spreadsheetDocument skipSST | None -> seq {()} /// /// Returns a 1D-sequence of Cells for the given sheetIndex of the given SpreadsheetDocument. /// /// SheetIndices are 1-based. - let getCellsBySheetID (sheetID : string) (spreadsheetDocument : SpreadsheetDocument) = + let getCellsBySheetID (sheetID : string) (spreadsheetDocument : SpreadsheetDocument) (skipSST : bool) = match Sheet.tryGetById sheetID spreadsheetDocument with - | Some sheet -> getCellsBySheet sheet spreadsheetDocument + | Some sheet -> getCellsBySheet sheet spreadsheetDocument skipSST | None -> seq {()} //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/FsSpreadsheet.ExcelIO/Table.fs b/src/FsSpreadsheet.ExcelIO/Table.fs index 662c94ca..b332f370 100644 --- a/src/FsSpreadsheet.ExcelIO/Table.fs +++ b/src/FsSpreadsheet.ExcelIO/Table.fs @@ -263,7 +263,7 @@ module Table = worksheetPart /// Create a table object by an area. If the first row of this area contains values in the given sheet, these are chosen as headers for the table and a table is returned. - let tryCreateWithExistingHeaders (sst : SharedStringTable Option) sheetData name area = + let tryCreateWithExistingHeaders (sst : SST Option) sheetData name area = if Area.isCorrect area then try let columns = @@ -301,7 +301,7 @@ module Table = |> Seq.tryFind (TableColumn.getName >> (=) name) /// If a column with the given header exists in the table, returns its values. Else returns None. - let tryGetColumnValuesByColumnHeader (sst : SharedStringTable Option) sheetData columnHeader (table : Table) = + let tryGetColumnValuesByColumnHeader (sst : SST Option) sheetData columnHeader (table : Table) = let area = getArea table table.TableColumns |> TableColumns.getTableColumns @@ -313,7 +313,7 @@ module Table = ) /// If a column with the given header exists in the table, returns its indexed values. Else returns None. - let tryGetIndexedColumnValuesByColumnHeader (sst : SharedStringTable Option) sheetData columnHeader (table : Table) = + let tryGetIndexedColumnValuesByColumnHeader (sst : SST Option) sheetData columnHeader (table : Table) = let area = getArea table table.TableColumns |> TableColumns.getTableColumns @@ -330,7 +330,7 @@ module Table = ) /// If a key column and a value with the given header exist in the table, returns a tuple list of keys and values (else returns None). Missing values get replaced with the given default value. - let tryGetKeyValuesByColumnHeaders (sst : SharedStringTable Option) sheetData keyColHeader valColHeader defaultValue (table : Table) = + let tryGetKeyValuesByColumnHeaders (sst : SST Option) sheetData keyColHeader valColHeader defaultValue (table : Table) = let area = getArea table let tableCols = table.TableColumns @@ -349,7 +349,7 @@ module Table = | _ -> None /// Reads a complete table. Values are stored sparsely in a dictionary, with the key being a row index and column header tuple. - let toSparseValueMatrix (sst : SharedStringTable Option) sheetData (table : Table) = + let toSparseValueMatrix (sst : SST Option) sheetData (table : Table) = let area = getArea table let dictionary = System.Collections.Generic.Dictionary() [Area.leftBoundary area .. Area.rightBoundary area] diff --git a/src/FsSpreadsheet/FsAddress.fs b/src/FsSpreadsheet/FsAddress.fs index c0f0a204..26e2348e 100644 --- a/src/FsSpreadsheet/FsAddress.fs +++ b/src/FsSpreadsheet/FsAddress.fs @@ -4,6 +4,13 @@ /// Module containing functions to work with "A1" style excel cell references. module CellReference = + [] + let indexPattern = + "([A-Z]*)(\d*)" + + let indexRegex = + System.Text.RegularExpressions.Regex(indexPattern) + /// Transforms excel column string indices (e.g. A, B, Z, AA, CD) to index number (starting with A = 1). let colAdressToIndex (columnAdress : string) = let length = columnAdress.Length @@ -33,8 +40,7 @@ module CellReference = /// Maps a "A1" style excel cell reference to a column * row index tuple (1 Based indices). let toIndices (reference : string) = let inp = reference.ToUpper() - let pattern = "([A-Z]*)(\d*)" - let regex = System.Text.RegularExpressions.Regex.Match(inp,pattern) + let regex = indexRegex.Match(inp) if regex.Success then regex.Groups diff --git a/tests/FsSpreadsheet.ExcelIO.Tests/FsWorkbook.fs b/tests/FsSpreadsheet.ExcelIO.Tests/FsWorkbook.fs index c320968b..1a4fac25 100644 --- a/tests/FsSpreadsheet.ExcelIO.Tests/FsWorkbook.fs +++ b/tests/FsSpreadsheet.ExcelIO.Tests/FsWorkbook.fs @@ -73,7 +73,8 @@ let performance = sw.Start() let wb = FsWorkbook.fromXlsxFile(p) sw.Stop() - Expect.isLessThan sw.Elapsed.Milliseconds 2000 "Elapsed time should be less than 2000ms" + let elapsed = sw.Elapsed.Milliseconds + Expect.isLessThan elapsed 2000 $"Elapsed time should be less than 2000ms, but was {elapsed}ms" Expect.equal (wb.GetWorksheetAt(1).Rows.Count) 153991 "Row count should be 153991" ) diff --git a/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Cell.fs b/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Cell.fs index 22ae6f3c..3987747a 100644 --- a/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Cell.fs +++ b/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Cell.fs @@ -10,8 +10,8 @@ let testFilePath = System.IO.Path.Combine(__SOURCE_DIRECTORY__, "../data", "test let ssdFox = Packaging.SpreadsheetDocument.Open(testFilePath, false) let wbpFox = ssdFox.WorkbookPart let sstpFox = wbpFox.SharedStringTablePart -let sstFox = sstpFox.SharedStringTable -let sstFoxInnerText = sstFox.InnerText +let sstFox = SharedStringTable.toSST sstpFox.SharedStringTable +let sstFoxInnerText = sstpFox.SharedStringTable.InnerText let wsp1Fox = (wbpFox.WorksheetParts |> Array.ofSeq)[0] let cbsi1Fox = wsp1Fox.Worksheet.Descendants() |> Array.ofSeq let nullCell = Cell.create (Some Spreadsheet.CellValues.Error) "A1" (Cell.CellValue.create "") diff --git a/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/FsExtensions.fs b/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/FsExtensions.fs index 5ffc6d5c..01d77aa4 100644 --- a/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/FsExtensions.fs +++ b/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/FsExtensions.fs @@ -81,18 +81,16 @@ let fsExtensionTests = // ] //] testList "FsCell" [ - testList "ofXlsxCell" [ + testCase "ofXlsxCell" (fun () -> let stream = new MemoryStream() let doc = Spreadsheet.initEmptyOnStream stream - let testCell = FsCell.ofXlsxCell doc dummyXlsxCell - testCase "is equal in value" <| fun _ -> - Expect.equal (testCell.ValueAsString()) dummyXlsxCell.CellValue.Text "values are not equal" - testCase "is equal in address/reference" <| fun _ -> - Expect.equal testCell.Address.Address dummyXlsxCell.CellReference.Value "addresses/references are not equal" - testCase "is equal in DataType/CellValues" <| fun _ -> - let dtOfCvs = DataType.ofXlsXCell doc dummyXlsxCell - Expect.equal testCell.DataType dtOfCvs "addresses/references are not equal" - ] + let sst = SharedStringTable.tryGet doc |> Option.map SharedStringTable.toSST + let testCell = FsCell.ofXlsxCell doc sst dummyXlsxCell + Expect.equal (testCell.ValueAsString()) dummyXlsxCell.CellValue.Text "values are not equal" + Expect.equal testCell.Address.Address dummyXlsxCell.CellReference.Value "addresses/references are not equal" + let dtOfCvs = DataType.ofXlsXCell doc dummyXlsxCell + Expect.equal testCell.DataType dtOfCvs "addresses/references are not equal" + ) ] testList "FsTable" [ testList "GetWorksheetOfTable" [ diff --git a/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Spreadsheet.fs b/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Spreadsheet.fs index cebdccfb..1e9a3ea3 100644 --- a/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Spreadsheet.fs +++ b/tests/FsSpreadsheet.ExcelIO.Tests/OpenXml/Spreadsheet.fs @@ -63,7 +63,7 @@ let spreadsheetTests = Expect.equal wbp wbpFox "Differs" ] testList "getCellsBySheetIndex" [ - let cbsi1 = Spreadsheet.getCellsBySheetIndex 1u ssd |> Array.ofSeq + let cbsi1 = Spreadsheet.getCellsBySheetIndex 1u ssd false |> Array.ofSeq // not applicable since Cell arrays and Cells always differ from each other (even if you compare the same cell, e.g. `cell1 = cell1`) //testCase "is equal to cbsi1Fox" <| fun _ -> // Expect.equal cbsi1 cbsi1Fox "Differs" diff --git a/tests/Speedtest/Program.fs b/tests/Speedtest/Program.fs new file mode 100644 index 00000000..c01019f0 --- /dev/null +++ b/tests/Speedtest/Program.fs @@ -0,0 +1,39 @@ +open FsSpreadsheet +open FsSpreadsheet.ExcelIO + +open ClosedXML.Excel + +[] +let main argv = + + let assayPath = @"C:\Users\HLWei\Downloads\ipk-gabi-wheat\assays\alpha_lattice\isa.assay.xlsx" + let studyPath = @"C:\Users\HLWei\Downloads\ipk-gabi-wheat\studies\alpha_lattice_trial_data\isa.study.xlsx" + let investigationPath = @"C:\Users\HLWei\Downloads\ipk-gabi-wheat\isa.investigation.xlsx" + + let fsSpreadsheet() = + + + let readAssay() = FsWorkbook.fromXlsxFile assayPath + let readStudy() = FsWorkbook.fromXlsxFile studyPath + let readInvestigation() = FsWorkbook.fromXlsxFile investigationPath + + readAssay() |> ignore + readStudy() |> ignore + readInvestigation() |> ignore + + let closedXML() = + + // Read xlsx file using closedxml + let readAssay() = new ClosedXML.Excel.XLWorkbook(assayPath) + let readStudy() = new ClosedXML.Excel.XLWorkbook(studyPath) + let readInvestigation() = new ClosedXML.Excel.XLWorkbook(investigationPath) + + readAssay() |> ignore + readStudy() |> ignore + readInvestigation() |> ignore + + + fsSpreadsheet() + closedXML() + + 1 \ No newline at end of file diff --git a/tests/Speedtest/Speedtest.fsproj b/tests/Speedtest/Speedtest.fsproj new file mode 100644 index 00000000..bf38f3f6 --- /dev/null +++ b/tests/Speedtest/Speedtest.fsproj @@ -0,0 +1,24 @@ + + + + Exe + net6.0 + + + + + + + + + + + + + + + + + + +