Skip to content

Commit

Permalink
fix json io datetime issues
Browse files Browse the repository at this point in the history
  • Loading branch information
HLWeil committed Mar 14, 2024
1 parent 514c204 commit 1586c5a
Show file tree
Hide file tree
Showing 20 changed files with 147 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ output/
/tests/FsSpreadsheet.JsNativeTests/fable/**/*.js
/tests/FsSpreadsheet.Net.Tests/TestFiles/WRITE_*.xlsx
/tests/JS/TestFiles/WRITE_*.xlsx
/tests/TestUtils/TestFiles/TestWorkbook_FsSpreadsheet_WRITE.*.xlsx
/tests/TestUtils/TestFiles/TestWorkbook_FsSpreadsheet_WRITE.**
/js
**/py/**
/.venv
Expand Down
11 changes: 8 additions & 3 deletions src/FsSpreadsheet/Json/Cell.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@
open FsSpreadsheet
open Thoth.Json.Core

[<Literal>]
let column = "column"

[<Literal>]
let value = "value"

let encode (cell:FsCell) =
Encode.object [
column, Encode.int cell.ColumnNumber
value, Value.encode cell.Value
]

let decode : Decoder<FsCell> =
let decode rowNumber : Decoder<FsCell> =
Decode.object (fun builder ->
let v = builder.Required.Field value (Value.decode)
new FsCell(v)
let v,dt = builder.Required.Field value (Value.decode)
let c = builder.Required.Field column Decode.int
new FsCell(v,dt,FsAddress(rowNumber,c))
)
10 changes: 8 additions & 2 deletions src/FsSpreadsheet/Json/Row.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ open Thoth.Json.Core
[<Literal>]
let cells = "cells"

[<Literal>]
let number = "number"

let encode (row:FsRow) =
Encode.object [
number, Encode.int row.Index
cells, Encode.seq (row.Cells |> Seq.map Cell.encode)
]

let decode : Decoder<FsCell seq> =
let decode : Decoder<int*FsCell seq> =
Decode.object (fun builder ->
builder.Required.Field cells (Decode.seq Cell.decode)
let n = builder.Required.Field number Decode.int
let cs = builder.Required.Field cells (Decode.seq (Cell.decode n))
n,cs
)
65 changes: 57 additions & 8 deletions src/FsSpreadsheet/Json/Value.fs
Original file line number Diff line number Diff line change
@@ -1,21 +1,70 @@
module FsSpreadsheet.Json.Value

open Thoth.Json.Core
open FsSpreadsheet

#if FABLE_COMPILER_PYTHON
module PyTime =

open Fable.Core
open Fable.Core.PyInterop

// Currently in Fable, a created datetime object will contain a timezone. This is not allowed in python xlsx, so we need to remove it.
// Unfortunately, the timezone object in python is read-only, so we need to create a new datetime object without timezone.
// For this, we use the fromtimestamp method of the datetime module and convert the timestamp to a new datetime object without timezone.

type DateTimeStatic =
[<Emit("$0.fromtimestamp(timestamp=$1)")>]
abstract member fromTimeStamp: timestamp:float -> System.DateTime

[<Import("datetime", "datetime")>]
let DateTime : DateTimeStatic = nativeOnly

let toUniversalTimePy (dt:System.DateTime) =

dt.ToUniversalTime()?timestamp()
|> DateTime.fromTimeStamp
#endif





module Decode =

let datetime: Decoder<System.DateTime> =
#if FABLE_COMPILER_PYTHON
Decode.datetimeLocal |> Decode.map PyTime.toUniversalTimePy
#else
{ new Decoder<System.DateTime> with
member _.Decode(helpers, value) =
if helpers.isString value then
match System.DateTime.TryParse(helpers.asString value) with
| true, datetime -> datetime |> Ok
| _ -> ("", BadPrimitive("a datetime", value)) |> Error
else
("", BadPrimitive("a datetime", value)) |> Error
}
#endif

let encode (value : obj) =
match value with
| :? string as s -> Encode.string s
| :? int as i -> Encode.int i
| :? float as f -> Encode.float f
| :? int as i -> Encode.int i
| :? bool as b -> Encode.bool b
| :? System.DateTime as d -> Encode.datetime d
| :? System.DateTime as d ->
d.ToString("O", System.Globalization.CultureInfo.InvariantCulture).Split('+').[0]
|> Encode.string
| _ -> Encode.nil

let decode : Decoder<obj> =


let decode =
Decode.oneOf [
Decode.string |> Decode.map (fun s -> s :> obj)
Decode.int |> Decode.map (fun i -> i :> obj)
Decode.float |> Decode.map (fun f -> f :> obj)
Decode.bool |> Decode.map (fun b -> b :> obj)
Decode.datetimeLocal |> Decode.map (fun d -> d :> obj)
Decode.bool |> Decode.map (fun b -> b :> obj, DataType.Boolean)
Decode.int |> Decode.map (fun i -> i :> obj, DataType.Number)
Decode.float |> Decode.map (fun f -> f :> obj, DataType.Number)
Decode.datetime |> Decode.map (fun d -> d :> obj, DataType.Date)
Decode.string |> Decode.map (fun s -> s :> obj, DataType.String)
]
11 changes: 7 additions & 4 deletions src/FsSpreadsheet/Json/Worksheet.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ let rows = "rows"
let tables = "tables"

let encode (sheet:FsWorksheet) =
sheet.RescanRows()
Encode.object [
name, Encode.string sheet.Name
if Seq.isEmpty sheet.Tables |> not then
Expand All @@ -28,11 +29,13 @@ let decode : Decoder<FsWorksheet> =
let rs = builder.Required.Field rows (Decode.seq Row.decode)
let sheet = new FsWorksheet(n)
rs
|> Seq.iteri (fun rowI cells ->
let r = sheet.Row(rowI + 1)
|> Seq.iter (fun (rowI,cells) ->
let r = sheet.Row(rowI)
cells
|> Seq.iteri (fun coli cell ->
r[coli + 1].Value <- cell.Value
|> Seq.iter (fun cell ->
let c = r[cell.ColumnNumber]
c.Value <- cell.Value
c.DataType <- cell.DataType
)
)
match ts with
Expand Down
3 changes: 0 additions & 3 deletions tests/FsSpreadsheet.Js.Tests/Json.Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ let defaultTestObject =
let s = dto.ToJsonString()
let dto2 = FsWorkbook.fromJsonString(s)
TestingUtils.Expect.isDefaultTestObject dto2

testCase "Should Fail" <| fun _ ->
Expect.isTrue false "is not the expected DataType.Boolean"
]

let main = testList "Json" [
Expand Down
2 changes: 0 additions & 2 deletions tests/FsSpreadsheet.Net.Tests/DefaultIO.Tests.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module DefaultIO

open Expecto
open TestingUtils
open FsSpreadsheet
open FsSpreadsheet.Net
Expand Down Expand Up @@ -45,7 +44,6 @@ let private tests_Write = testList "Write" [

]

[<Tests>]
let main = testList "DefaultIO" [
tests_Read
tests_Write
Expand Down
14 changes: 5 additions & 9 deletions tests/FsSpreadsheet.Net.Tests/FsWorkbook.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
module FsWorkbookTests

open Expecto
module FsWorkbook.Tests
open FsSpreadsheet
open FsSpreadsheet.Net

Expand Down Expand Up @@ -72,19 +70,17 @@ let performance =
testList "Performace" [
testCase "ReadBigFile" (fun () ->
let sw = Stopwatch()
let p = "./TestFiles/BigFile.xlsx"
sw.Start()
let wb = FsWorkbook.fromXlsxFile(p)
sw.Start()
let wb = FsWorkbook.fromXlsxFile(DefaultTestObject.BigFile.asRelativePath)
sw.Stop()
let elapsed = sw.Elapsed.Milliseconds
Expect.isLessThan elapsed 2000 $"Elapsed time should be less than 2000ms, but was {elapsed}ms"
Expect.isTrue (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"

)
]

[<Tests>]
let tests =
let main =
testList "FsWorkbook" [
writeAndReadBytes
performance
Expand Down
5 changes: 2 additions & 3 deletions tests/FsSpreadsheet.Net.Tests/Json.Tests.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Json.Tests

open TestingUtils
open FsSpreadsheet
open FsSpreadsheet.Net
open Fable.Pyxpecto
Expand All @@ -10,11 +11,9 @@ let defaultTestObject =
testCase "Read-Write DefaultTestObject" <| fun _ ->
let dto = DefaultTestObject.defaultTestObject()
let s = dto.ToJsonString()
System.IO.File.WriteAllText(DefaultTestObject.FsSpreadsheetJSON.asRelativePath,s)
let dto2 = FsWorkbook.fromJsonString(s)
TestingUtils.Expect.isDefaultTestObject dto2

testCase "Should Fail" <| fun _ ->
Expect.isTrue false "is not the expected DataType.Boolean"
]

let main = testList "Json" [
Expand Down
22 changes: 20 additions & 2 deletions tests/FsSpreadsheet.Net.Tests/Main.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
module FsSpreadsheet.Net.Tests
open Expecto

open Fable.Pyxpecto
open TestingUtils

let all =
testList "All"
[
ZipArchiveReader.main
Stylesheet.main
DefaultIO.main
FsExtension.Tests.main
Cell.Tests.main
Sheet.Tests.main
Workbook.Tests.main
Spreadsheet.Tests.main
Table.Tests.main
FsWorkbook.Tests.main
Json.Tests.main
]

[<EntryPoint>]
let main argv =
Tests.runTestsInAssemblyWithCLIArgs [] argv
Pyxpecto.runTests [||] all
9 changes: 5 additions & 4 deletions tests/FsSpreadsheet.Net.Tests/OpenXml/Cell.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Cell
module Cell.Tests

open Expecto
open TestingUtils
open FsSpreadsheet.Net
open DocumentFormat.OpenXml

Expand All @@ -17,7 +17,6 @@ let cbsi1Fox = wsp1Fox.Worksheet.Descendants<Spreadsheet.Cell>() |> Array.ofSeq
let nullCell = Cell.create (Some Spreadsheet.CellValues.Error) "A1" (Cell.CellValue.create "")
nullCell.CellValue.Text <- null

[<Tests>]
let cellTests =
testList "Cell" [
testList "includeSharedStringValue" [
Expand All @@ -28,4 +27,6 @@ let cellTests =
testCase "nullCell with included SharedStringValue has no CellValueText" <| fun _ ->
Expect.notEqual cissv1_0.CellValue.Text cissvNull.CellValue.Text "Does not differ"
]
]
]

let main = cellTests
11 changes: 6 additions & 5 deletions tests/FsSpreadsheet.Net.Tests/OpenXml/FsExtensions.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module FsExtension
module FsExtension.Tests

open Expecto
open TestingUtils
open FsSpreadsheet
open FsSpreadsheet.Net
open DocumentFormat.OpenXml
Expand Down Expand Up @@ -50,7 +50,6 @@ dummyFsWorkbook.AddWorksheet(dummyFsWorksheet4) |> ignore
let testFile2Path = Path.Combine(__SOURCE_DIRECTORY__, "../data", "2EXT02_Protein.xlsx")


[<Tests>]
let fsExtensionTests =
testList "FsExtensions" [
//testList "DataType" [
Expand Down Expand Up @@ -163,7 +162,9 @@ let fsExtensionTests =
Expect.equal v "Id" "value is not equal"
testCase "Worksheet SwateTemplateMetadata from 2EXT02_Protein has FsRows" <| fun _ ->
let rows = tf2Worksheet.Value.Rows
Expect.isGreaterThan rows.Count 0 "Worksheet SwateTemplateMetadata from 2EXT02_Protein has no FsRows"
Expect.notEqual rows.Count 0 "Worksheet SwateTemplateMetadata from 2EXT02_Protein has no FsRows"
]
]
]
]

let main = fsExtensionTests
9 changes: 5 additions & 4 deletions tests/FsSpreadsheet.Net.Tests/OpenXml/Sheet.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Sheet
module Sheet.Tests

open Expecto
open TestingUtils
open FsSpreadsheet.Net
open DocumentFormat.OpenXml

Expand All @@ -13,7 +13,6 @@ let shtsFox = wbFox.Sheets
let shtssFox = shtsFox.Descendants<Spreadsheet.Sheet>() |> Array.ofSeq // array is needed since seqs cannot be compared


[<Tests>]
let sheetsTests =
testList "Sheets" [
testList "get" [
Expand All @@ -26,4 +25,6 @@ let sheetsTests =
let shtss = Sheet.Sheets.getSheets shtsFox |> Array.ofSeq // array is needed since seqs cannot be compared
Expect.equal shtss shtssFox "Differs"
]
]
]

let main = sheetsTests
10 changes: 3 additions & 7 deletions tests/FsSpreadsheet.Net.Tests/OpenXml/Spreadsheet.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Spreadsheet
module Spreadsheet.Tests

open Expecto
open TestingUtils
open FsSpreadsheet.Net
open DocumentFormat.OpenXml

Expand Down Expand Up @@ -36,9 +36,6 @@ let cbsi1Fox = // get the Cells, but with their real values (inferred from
//testSsdFox = testDoc
//testSsdFox = testSsdFox2



[<Tests>]
let spreadsheetTests =
testList "Spreadsheet" [
let ssd = Spreadsheet.fromFile testFilePath false
Expand Down Expand Up @@ -90,5 +87,4 @@ let spreadsheetTests =




//testSsdFox.Close()
let main = spreadsheetTests
Loading

0 comments on commit 1586c5a

Please sign in to comment.