diff --git a/Project.toml b/Project.toml
index 68ecaaa..9394511 100644
--- a/Project.toml
+++ b/Project.toml
@@ -18,7 +18,7 @@ XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0"
[compat]
DataValues = "0.4.11"
-ExcelReaders = "0.11"
+ExcelReaders = "0.11, 0.12"
FileIO = "1"
IterableTables = "0.8.3, 0.9, 0.10, 0.11, 1"
IteratorInterfaceExtensions = "0.1.1, 1"
@@ -32,6 +32,7 @@ julia = "1"
[extras]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
+TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"
[targets]
-test = ["Test", "DataFrames"]
+test = ["Test", "TestItemRunner", "DataFrames"]
diff --git a/src/ExcelFiles.jl b/src/ExcelFiles.jl
index 9b7eb6a..f1b1903 100644
--- a/src/ExcelFiles.jl
+++ b/src/ExcelFiles.jl
@@ -49,7 +49,7 @@ function gennames(n::Integer)
return res
end
-function _readxl(file::ExcelReaders.ExcelFile, sheetname::AbstractString, startrow::Integer, startcol::Integer, endrow::Integer, endcol::Integer; header::Bool=true, colnames::Vector{Symbol}=Symbol[])
+function _readxls(file::ExcelReaders.ExcelFile, sheetname::AbstractString, startrow::Integer, startcol::Integer, endrow::Integer, endcol::Integer; header::Bool=true, colnames::Vector{Symbol}=Symbol[])
data = ExcelReaders.readxl_internal(file, sheetname, startrow, startcol, endrow, endcol)
nrow, ncol = size(data)
@@ -103,24 +103,110 @@ function _readxl(file::ExcelReaders.ExcelFile, sheetname::AbstractString, startr
return columns, colnames
end
+function process_xlsx_data(data; header::Bool=true, colnames::Vector{Symbol}=Symbol[])
+ nrow, ncol = size(data)
+
+ if length(colnames) == 0
+ if header
+ headervec = data[1, :]
+ NAcol = map(i -> isa(i, DataValues.DataValue) && DataValues.isna(i), headervec)
+ headervec[NAcol] = gennames(count(!iszero, NAcol))
+
+ # This somewhat complicated conditional makes sure that column names
+ # that are integer numbers end up without an extra ".0" as their name
+ colnames = [isa(i, AbstractFloat) ? ( modf(i)[1] == 0.0 ? Symbol(Int(i)) : Symbol(string(i)) ) : Symbol(i) for i in vec(headervec)]
+ else
+ colnames = gennames(ncol)
+ end
+ elseif length(colnames) != ncol
+ error("Length of colnames must equal number of columns in selected range")
+ end
+
+ columns = Array{Any}(undef, ncol)
+
+ for i = 1:ncol
+ if header
+ vals = data[2:end,i]
+ else
+ vals = data[:,i]
+ end
+
+ # Check whether all non-NA values in this column
+ # are of the same type
+ type_of_el = length(vals) > 0 ? typeof(vals[1]) : Any
+ for val = vals
+ type_of_el = promote_type(type_of_el, typeof(val))
+ end
+
+ if type_of_el <: DataValue
+ columns[i] = convert(DataValueArray{eltype(type_of_el)}, vals)
+
+ # TODO Check wether this hack is correct
+ for (j, v) in enumerate(columns[i])
+ if v isa DataValue && !DataValues.isna(v) && v[] isa DataValue
+ columns[i][j] = v[]
+ end
+ end
+ else
+ columns[i] = convert(Array{type_of_el}, vals)
+ end
+ end
+
+ return columns, colnames
+end
+
+function _readxlsx(file, sheetname::AbstractString, startrow::Integer, startcol::Integer, endrow::Integer, endcol::Integer; header::Bool=true, colnames::Vector{Symbol}=Symbol[])
+ data = ExcelReaders.readxl_internal(file, sheetname, startrow, startcol, endrow, endcol)
+
+
+end
+
+function _readxlsx(file, range; header::Bool=true, colnames::Vector{Symbol}=Symbol[])
+ data = ExcelReaders.readxl_internal(file, sheetname, startrow, startcol, endrow, endcol)
+
+ process_xlsx_data(data, header=header, colnames=colnames)
+end
+
function IteratorInterfaceExtensions.getiterator(file::ExcelFile)
+ old_style_excel_file = endswith(file.filename, ".xls")
+
column_data, col_names = if occursin("!", file.range)
- excelfile = openxl(file.filename)
+ if old_style_excel_file
+ excelfile = openxl(file.filename)
- sheetname, startrow, startcol, endrow, endcol = ExcelReaders.convert_ref_to_sheet_row_col(file.range)
+ sheetname, startrow, startcol, endrow, endcol = ExcelReaders.convert_ref_to_sheet_row_col(file.range)
- _readxl(excelfile, sheetname, startrow, startcol, endrow, endcol; file.keywords...)
+ _readxls(excelfile, sheetname, startrow, startcol, endrow, endcol; file.keywords...)
+ else
+ excelfile = XLSX.readxlsx(file.filename)
+
+ _readxlsx(excelfile, file.range; file.keywords...)
+ end
else
- excelfile = openxl(file.filename)
- sheet = excelfile.workbook.sheet_by_name(file.range)
+ if old_style_excel_file
+ excelfile = openxl(file.filename)
+ sheet = excelfile.workbook.sheet_by_name(file.range)
+
+ keywords = filter(i -> !(i[1] in (:header, :colnames)), file.keywords)
+ startrow, startcol, endrow, endcol = ExcelReaders.convert_args_to_row_col(sheet; keywords...)
+
+ keywords2 = copy(file.keywords)
+ keywords2 = filter(i -> !(i[1] in (:skipstartrows, :skipstartcols, :nrows, :ncols)), file.keywords)
- keywords = filter(i -> !(i[1] in (:header, :colnames)), file.keywords)
- startrow, startcol, endrow, endcol = ExcelReaders.convert_args_to_row_col(sheet; keywords...)
+ _readxls(excelfile, file.range, startrow, startcol, endrow, endcol; keywords2...)
+ else
+ excelfile = openxl(file.filename)
+
+ sheet = excelfile[file.range]
- keywords2 = copy(file.keywords)
- keywords2 = filter(i -> !(i[1] in (:skipstartrows, :skipstartcols, :nrows, :ncols)), file.keywords)
+ keywords = filter(i -> !(i[1] in (:header, :colnames)), file.keywords)
+ startrow, startcol, endrow, endcol = ExcelReaders.convert_args_to_row_col(sheet; keywords...)
- _readxl(excelfile, file.range, startrow, startcol, endrow, endcol; keywords2...)
+ keywords2 = copy(file.keywords)
+ keywords2 = filter(i -> !(i[1] in (:skipstartrows, :skipstartcols, :nrows, :ncols)), file.keywords)
+
+ _readxls(excelfile, file.range, startrow, startcol, endrow, endcol; keywords2...)
+ end
end
return create_tableiterator(column_data, col_names)
diff --git a/test/TestData.xls b/test/TestData.xls
new file mode 100644
index 0000000..af2ebbe
Binary files /dev/null and b/test/TestData.xls differ
diff --git a/test/TestData.xlsx b/test/TestData.xlsx
new file mode 100644
index 0000000..817ae13
Binary files /dev/null and b/test/TestData.xlsx differ
diff --git a/test/runtests.jl b/test/runtests.jl
index d1d0372..b9e874d 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -1,149 +1,3 @@
-using ExcelFiles
-using ExcelReaders
-using IteratorInterfaceExtensions
-using TableTraits
-using TableTraitsUtils
-using Dates
-using DataValues
-using DataFrames
-using Test
+using TestItemRunner
-@testset "ExcelFiles" begin
-
- filename = normpath(dirname(pathof(ExcelReaders)), "..", "test", "TestData.xlsx")
-
- efile = load(filename, "Sheet1")
-
- @test sprint((stream, data) -> show(stream, "text/html", data), efile) == "
| Some Float64s | Some Strings | Some Bools | Mixed column | Mixed with NA | Float64 with NA | String with NA | Bool with NA | Some dates | Dates with NA | Some errors | Errors with NA | Column with NULL and then mixed |
|---|
| 1.0 | "A" | true | 2.0 | 9.0 | 3.0 | "FF" | #NA | 2015-03-03T00:00:00 | 1965-04-03T00:00:00 | #DIV/0! | #DIV/0! | #NA |
| 1.5 | "BB" | false | "EEEEE" | "III" | #NA | #NA | true | 2015-02-04T10:14:00 | 1950-08-09T18:40:00 | #N/A | #N/A | 3.4 |
| 2.0 | "CCC" | false | false | #NA | 3.5 | "GGG" | #NA | 1988-04-09T00:00:00 | 19:00:00 | #REF! | #NAME? | "HKEJW" |
| 2.5 | "DDDD" | true | 1.5 | true | 4.0 | "HHHH" | false | 15:02:00 | #NA | #NAME? | #NA | #NA |
"
-
- @test sprint((stream, data) -> show(stream, "application/vnd.dataresource+json", data), efile) == "{\"schema\":{\"fields\":[{\"name\":\"Some Float64s\",\"type\":\"number\"},{\"name\":\"Some Strings\",\"type\":\"string\"},{\"name\":\"Some Bools\",\"type\":\"boolean\"},{\"name\":\"Mixed column\",\"type\":\"string\"},{\"name\":\"Mixed with NA\",\"type\":\"string\"},{\"name\":\"Float64 with NA\",\"type\":\"number\"},{\"name\":\"String with NA\",\"type\":\"string\"},{\"name\":\"Bool with NA\",\"type\":\"boolean\"},{\"name\":\"Some dates\",\"type\":\"string\"},{\"name\":\"Dates with NA\",\"type\":\"string\"},{\"name\":\"Some errors\",\"type\":\"string\"},{\"name\":\"Errors with NA\",\"type\":\"string\"},{\"name\":\"Column with NULL and then mixed\",\"type\":\"string\"}]},\"data\":[{\"Some Float64s\":1.0,\"Some Strings\":\"A\",\"Some Bools\":true,\"Mixed column\":2.0,\"Mixed with NA\":9.0,\"Float64 with NA\":3.0,\"String with NA\":\"FF\",\"Bool with NA\":null,\"Some dates\":\"2015-03-03T00:00:00\",\"Dates with NA\":\"1965-04-03T00:00:00\",\"Some errors\":{\"errorcode\":7},\"Errors with NA\":{\"errorcode\":7},\"Column with NULL and then mixed\":null},{\"Some Float64s\":1.5,\"Some Strings\":\"BB\",\"Some Bools\":false,\"Mixed column\":\"EEEEE\",\"Mixed with NA\":\"III\",\"Float64 with NA\":null,\"String with NA\":null,\"Bool with NA\":true,\"Some dates\":\"2015-02-04T10:14:00\",\"Dates with NA\":\"1950-08-09T18:40:00\",\"Some errors\":{\"errorcode\":42},\"Errors with NA\":{\"errorcode\":42},\"Column with NULL and then mixed\":3.4},{\"Some Float64s\":2.0,\"Some Strings\":\"CCC\",\"Some Bools\":false,\"Mixed column\":false,\"Mixed with NA\":null,\"Float64 with NA\":3.5,\"String with NA\":\"GGG\",\"Bool with NA\":null,\"Some dates\":\"1988-04-09T00:00:00\",\"Dates with NA\":\"19:00:00\",\"Some errors\":{\"errorcode\":23},\"Errors with NA\":{\"errorcode\":29},\"Column with NULL and then mixed\":\"HKEJW\"},{\"Some Float64s\":2.5,\"Some Strings\":\"DDDD\",\"Some Bools\":true,\"Mixed column\":1.5,\"Mixed with NA\":true,\"Float64 with NA\":4.0,\"String with NA\":\"HHHH\",\"Bool with NA\":false,\"Some dates\":\"15:02:00\",\"Dates with NA\":null,\"Some errors\":{\"errorcode\":29},\"Errors with NA\":null,\"Column with NULL and then mixed\":null}]}"
-
- @test sprint(show, efile) == "4x13 Excel file\nSome Float64s │ Some Strings │ Some Bools │ Mixed column │ Mixed with NA\n──────────────┼──────────────┼────────────┼──────────────┼──────────────\n1.0 │ A │ true │ 2.0 │ 9.0 \n1.5 │ BB │ false │ \"EEEEE\" │ \"III\" \n2.0 │ CCC │ false │ false │ #NA \n2.5 │ DDDD │ true │ 1.5 │ true \n... with 8 more columns: Float64 with NA, String with NA, Bool with NA, Some dates, Dates with NA, Some errors, Errors with NA, Column with NULL and then mixed"
-
- @test TableTraits.isiterabletable(efile) == true
- @test IteratorInterfaceExtensions.isiterable(efile) == true
- @test showable("text/html", efile) == true
- @test showable("application/vnd.dataresource+json", efile) == true
-
- @test isiterable(efile) == true
-
- full_dfs = [create_columns_from_iterabletable(load(filename, "Sheet1!C3:O7")), create_columns_from_iterabletable(load(filename, "Sheet1"))]
- for (df, names) in full_dfs
- @test length(df) == 13
- @test length(df[1]) == 4
-
- @test df[1] == [1., 1.5, 2., 2.5]
- @test df[2] == ["A", "BB", "CCC", "DDDD"]
- @test df[3] == [true, false, false, true]
- @test df[4] == [2, "EEEEE", false, 1.5]
- @test df[5] == [9., "III", NA, true]
- @test df[6] == [3., NA, 3.5, 4]
- @test df[7] == ["FF", NA, "GGG", "HHHH"]
- @test df[8] == [NA, true, NA, false]
- @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), Date(1988, 4, 9), Dates.Time(15, 2, 0)]
- @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
- @test eltype(df[11]) == ExcelReaders.ExcelErrorCell
- @test df[12][1][] isa ExcelReaders.ExcelErrorCell
- @test df[12][2][] isa ExcelReaders.ExcelErrorCell
- @test df[12][3][] isa ExcelReaders.ExcelErrorCell
- @test df[12][4] == NA
- @test df[13] == [NA, 3.4, "HKEJW", NA]
- end
-
- df, names = create_columns_from_iterabletable(load(filename, "Sheet1!C4:O7", header=false))
- @test names == [:x1,:x2,:x3,:x4,:x5,:x6,:x7,:x8,:x9,:x10,:x11,:x12,:x13]
- @test length(df[1]) == 4
- @test length(df) == 13
- @test df[1] == [1., 1.5, 2., 2.5]
- @test df[2] == ["A", "BB", "CCC", "DDDD"]
- @test df[3] == [true, false, false, true]
- @test df[4] == [2, "EEEEE", false, 1.5]
- @test df[5] == [9., "III", NA, true]
- @test df[6] == [3, NA, 3.5, 4]
- @test df[7] == ["FF", NA, "GGG", "HHHH"]
- @test df[8] == [NA, true, NA, false]
- @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), DateTime(1988, 4, 9), Dates.Time(15, 2, 0)]
- @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
- @test isa(df[11][1], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][2], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][3], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][4], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][1][], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][2][], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][3][], ExcelReaders.ExcelErrorCell)
- @test DataValues.isna(df[12][4])
- @test df[13] == [NA, 3.4, "HKEJW", NA]
-
- good_colnames = [:c1, :c2, :c3, :c4, :c5, :c6, :c7, :c8, :c9, :c10, :c11, :c12, :c13]
-
- df, names = create_columns_from_iterabletable(load(filename, "Sheet1!C4:O7", header=false, colnames=good_colnames))
- @test names == good_colnames
- @test length(df[1]) == 4
- @test length(df) == 13
- @test df[1] == [1., 1.5, 2., 2.5]
- @test df[2] == ["A", "BB", "CCC", "DDDD"]
- @test df[3] == [true, false, false, true]
- @test df[4] == [2, "EEEEE", false, 1.5]
- @test df[5] == [9., "III", NA, true]
- @test df[6] == [3, NA, 3.5, 4]
- @test df[7] == ["FF", NA, "GGG", "HHHH"]
- @test df[8] == [NA, true, NA, false]
- @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), DateTime(1988, 4, 9), Dates.Time(15, 2, 0)]
- @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
- @test isa(df[11][1], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][2], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][3], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][4], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][1][], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][2][], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][3][], ExcelReaders.ExcelErrorCell)
- @test DataValues.isna(df[12][4])
- @test df[13] == [NA, 3.4, "HKEJW", NA]
-
-# Test for saving DataFrame to XLSX
- input = (Day = ["Nov. 27","Nov. 28","Nov. 29"], Highest = [78,79,75]) |> DataFrame
- file = save("file.xlsx", input)
- output = load("file.xlsx", "Sheet1") |> DataFrame
- @test input == output
- rm("file.xlsx")
-
-# Test for saving DataFrame to XLSX with sheetname keyword
- input = (Day = ["Nov. 27","Nov. 28","Nov. 29"], Highest = [78,79,75]) |> DataFrame
- file = save("file.xlsx", input, sheetname="SheetName")
- output = load("file.xlsx", "SheetName") |> DataFrame
- @test input == output
- rm("file.xlsx")
-
- df, names = create_columns_from_iterabletable(load(filename, "Sheet1", colnames=good_colnames))
- @test names == good_colnames
- @test length(df[1]) == 4
- @test length(df) == 13
- @test df[1] == [1., 1.5, 2., 2.5]
- @test df[2] == ["A", "BB", "CCC", "DDDD"]
- @test df[3] == [true, false, false, true]
- @test df[4] == [2, "EEEEE", false, 1.5]
- @test df[5] == [9., "III", NA, true]
- @test df[6] == [3, NA, 3.5, 4]
- @test df[7] == ["FF", NA, "GGG", "HHHH"]
- @test df[8] == [NA, true, NA, false]
- @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), DateTime(1988, 4, 9), Dates.Time(15, 2, 0)]
- @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
- @test isa(df[11][1], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][2], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][3], ExcelReaders.ExcelErrorCell)
- @test isa(df[11][4], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][1][], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][2][], ExcelReaders.ExcelErrorCell)
- @test isa(df[12][3][], ExcelReaders.ExcelErrorCell)
- @test DataValues.isna(df[12][4])
- @test df[13] == [NA, 3.4, "HKEJW", NA]
-
-# Too few colnames
- @test_throws ErrorException create_columns_from_iterabletable(load(filename, "Sheet1!C4:O7", header=true, colnames=[:c1, :c2, :c3, :c4]))
-
-# Test for constructing DataFrame with empty header cell
- data, names = create_columns_from_iterabletable(load(filename, "Sheet2!C5:E7"))
- @test names == [:Col1, :x1, :Col3]
-
-
-end
+@run_package_tests
diff --git a/test/test_excelfiles.jl b/test/test_excelfiles.jl
new file mode 100644
index 0000000..ed915f0
--- /dev/null
+++ b/test/test_excelfiles.jl
@@ -0,0 +1,147 @@
+@testitem "ExcelFiles" begin
+ using ExcelReaders
+ using IteratorInterfaceExtensions
+ using TableTraits
+ using TableTraitsUtils
+ using Dates
+ using DataValues
+ using DataFrames
+
+ for filename in [joinpath(@__DIR__, "TestData.xls"), joinpath(@__DIR__, "TestData.xlsx")]
+
+ efile = load(filename, "Sheet1")
+
+ @test sprint((stream, data) -> show(stream, "text/html", data), efile) == "| Some Float64s | Some Strings | Some Bools | Mixed column | Mixed with NA | Float64 with NA | String with NA | Bool with NA | Some dates | Dates with NA | Some errors | Errors with NA | Column with NULL and then mixed |
|---|
| 1.0 | "A" | true | 2.0 | 9.0 | 3.0 | "FF" | #NA | 2015-03-03T00:00:00 | 1965-04-03T00:00:00 | #DIV/0! | #DIV/0! | #NA |
| 1.5 | "BB" | false | "EEEEE" | "III" | #NA | #NA | true | 2015-02-04T10:14:00 | 1950-08-09T18:40:00 | #N/A | #N/A | 3.4 |
| 2.0 | "CCC" | false | false | #NA | 3.5 | "GGG" | #NA | 1988-04-09T00:00:00 | 19:00:00 | #REF! | #NAME? | "HKEJW" |
| 2.5 | "DDDD" | true | 1.5 | true | 4.0 | "HHHH" | false | 15:02:00 | #NA | #NAME? | #NA | #NA |
"
+
+ @test sprint((stream, data) -> show(stream, "application/vnd.dataresource+json", data), efile) == "{\"schema\":{\"fields\":[{\"name\":\"Some Float64s\",\"type\":\"number\"},{\"name\":\"Some Strings\",\"type\":\"string\"},{\"name\":\"Some Bools\",\"type\":\"boolean\"},{\"name\":\"Mixed column\",\"type\":\"string\"},{\"name\":\"Mixed with NA\",\"type\":\"string\"},{\"name\":\"Float64 with NA\",\"type\":\"number\"},{\"name\":\"String with NA\",\"type\":\"string\"},{\"name\":\"Bool with NA\",\"type\":\"boolean\"},{\"name\":\"Some dates\",\"type\":\"string\"},{\"name\":\"Dates with NA\",\"type\":\"string\"},{\"name\":\"Some errors\",\"type\":\"string\"},{\"name\":\"Errors with NA\",\"type\":\"string\"},{\"name\":\"Column with NULL and then mixed\",\"type\":\"string\"}]},\"data\":[{\"Some Float64s\":1.0,\"Some Strings\":\"A\",\"Some Bools\":true,\"Mixed column\":2.0,\"Mixed with NA\":9.0,\"Float64 with NA\":3.0,\"String with NA\":\"FF\",\"Bool with NA\":null,\"Some dates\":\"2015-03-03T00:00:00\",\"Dates with NA\":\"1965-04-03T00:00:00\",\"Some errors\":{\"errorcode\":7},\"Errors with NA\":{\"errorcode\":7},\"Column with NULL and then mixed\":null},{\"Some Float64s\":1.5,\"Some Strings\":\"BB\",\"Some Bools\":false,\"Mixed column\":\"EEEEE\",\"Mixed with NA\":\"III\",\"Float64 with NA\":null,\"String with NA\":null,\"Bool with NA\":true,\"Some dates\":\"2015-02-04T10:14:00\",\"Dates with NA\":\"1950-08-09T18:40:00\",\"Some errors\":{\"errorcode\":42},\"Errors with NA\":{\"errorcode\":42},\"Column with NULL and then mixed\":3.4},{\"Some Float64s\":2.0,\"Some Strings\":\"CCC\",\"Some Bools\":false,\"Mixed column\":false,\"Mixed with NA\":null,\"Float64 with NA\":3.5,\"String with NA\":\"GGG\",\"Bool with NA\":null,\"Some dates\":\"1988-04-09T00:00:00\",\"Dates with NA\":\"19:00:00\",\"Some errors\":{\"errorcode\":23},\"Errors with NA\":{\"errorcode\":29},\"Column with NULL and then mixed\":\"HKEJW\"},{\"Some Float64s\":2.5,\"Some Strings\":\"DDDD\",\"Some Bools\":true,\"Mixed column\":1.5,\"Mixed with NA\":true,\"Float64 with NA\":4.0,\"String with NA\":\"HHHH\",\"Bool with NA\":false,\"Some dates\":\"15:02:00\",\"Dates with NA\":null,\"Some errors\":{\"errorcode\":29},\"Errors with NA\":null,\"Column with NULL and then mixed\":null}]}"
+
+ @test sprint(show, efile) == "4x13 Excel file\nSome Float64s │ Some Strings │ Some Bools │ Mixed column │ Mixed with NA\n──────────────┼──────────────┼────────────┼──────────────┼──────────────\n1.0 │ A │ true │ 2.0 │ 9.0 \n1.5 │ BB │ false │ \"EEEEE\" │ \"III\" \n2.0 │ CCC │ false │ false │ #NA \n2.5 │ DDDD │ true │ 1.5 │ true \n... with 8 more columns: Float64 with NA, String with NA, Bool with NA, Some dates, Dates with NA, Some errors, Errors with NA, Column with NULL and then mixed"
+
+ @test TableTraits.isiterabletable(efile) == true
+ @test IteratorInterfaceExtensions.isiterable(efile) == true
+ @test showable("text/html", efile) == true
+ @test showable("application/vnd.dataresource+json", efile) == true
+
+ @test isiterable(efile) == true
+
+ full_dfs = [create_columns_from_iterabletable(load(filename, "Sheet1!C3:O7")), create_columns_from_iterabletable(load(filename, "Sheet1"))]
+ for (df, names) in full_dfs
+ @test length(df) == 13
+ @test length(df[1]) == 4
+
+ @test df[1] == [1., 1.5, 2., 2.5]
+ @test df[2] == ["A", "BB", "CCC", "DDDD"]
+ @test df[3] == [true, false, false, true]
+ @test df[4] == [2, "EEEEE", false, 1.5]
+ @test df[5] == [9., "III", NA, true]
+ @test df[6] == [3., NA, 3.5, 4]
+ @test df[7] == ["FF", NA, "GGG", "HHHH"]
+ @test df[8] == [NA, true, NA, false]
+ @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), Date(1988, 4, 9), Dates.Time(15, 2, 0)]
+ @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
+ @test eltype(df[11]) == ExcelReaders.ExcelErrorCell
+ @test df[12][1][] isa ExcelReaders.ExcelErrorCell
+ @test df[12][2][] isa ExcelReaders.ExcelErrorCell
+ @test df[12][3][] isa ExcelReaders.ExcelErrorCell
+ @test df[12][4] == NA
+ @test df[13] == [NA, 3.4, "HKEJW", NA]
+ end
+
+ df, names = create_columns_from_iterabletable(load(filename, "Sheet1!C4:O7", header=false))
+ @test names == [:x1,:x2,:x3,:x4,:x5,:x6,:x7,:x8,:x9,:x10,:x11,:x12,:x13]
+ @test length(df[1]) == 4
+ @test length(df) == 13
+ @test df[1] == [1., 1.5, 2., 2.5]
+ @test df[2] == ["A", "BB", "CCC", "DDDD"]
+ @test df[3] == [true, false, false, true]
+ @test df[4] == [2, "EEEEE", false, 1.5]
+ @test df[5] == [9., "III", NA, true]
+ @test df[6] == [3, NA, 3.5, 4]
+ @test df[7] == ["FF", NA, "GGG", "HHHH"]
+ @test df[8] == [NA, true, NA, false]
+ @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), DateTime(1988, 4, 9), Dates.Time(15, 2, 0)]
+ @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
+ @test isa(df[11][1], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][2], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][3], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][4], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][1][], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][2][], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][3][], ExcelReaders.ExcelErrorCell)
+ @test DataValues.isna(df[12][4])
+ @test df[13] == [NA, 3.4, "HKEJW", NA]
+
+ good_colnames = [:c1, :c2, :c3, :c4, :c5, :c6, :c7, :c8, :c9, :c10, :c11, :c12, :c13]
+
+ df, names = create_columns_from_iterabletable(load(filename, "Sheet1!C4:O7", header=false, colnames=good_colnames))
+ @test names == good_colnames
+ @test length(df[1]) == 4
+ @test length(df) == 13
+ @test df[1] == [1., 1.5, 2., 2.5]
+ @test df[2] == ["A", "BB", "CCC", "DDDD"]
+ @test df[3] == [true, false, false, true]
+ @test df[4] == [2, "EEEEE", false, 1.5]
+ @test df[5] == [9., "III", NA, true]
+ @test df[6] == [3, NA, 3.5, 4]
+ @test df[7] == ["FF", NA, "GGG", "HHHH"]
+ @test df[8] == [NA, true, NA, false]
+ @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), DateTime(1988, 4, 9), Dates.Time(15, 2, 0)]
+ @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
+ @test isa(df[11][1], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][2], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][3], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][4], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][1][], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][2][], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][3][], ExcelReaders.ExcelErrorCell)
+ @test DataValues.isna(df[12][4])
+ @test df[13] == [NA, 3.4, "HKEJW", NA]
+
+ # Test for saving DataFrame to XLSX
+ input = (Day = ["Nov. 27","Nov. 28","Nov. 29"], Highest = [78,79,75]) |> DataFrame
+
+ mktempdir() do path
+ file = save(joinpath(path, "file.xlsx"), input)
+ output = load(joinpath(path, "file.xlsx"), "Sheet1") |> DataFrame
+ @test input == output
+ end
+
+ # Test for saving DataFrame to XLSX with sheetname keyword
+ input = (Day = ["Nov. 27","Nov. 28","Nov. 29"], Highest = [78,79,75]) |> DataFrame
+ mktempdir() do path
+ file = save(joinpath(path, "file.xlsx"), input, sheetname="SheetName")
+ output = load(joinpath(path, "file.xlsx"), "SheetName") |> DataFrame
+ end
+
+ df, names = create_columns_from_iterabletable(load(filename, "Sheet1", colnames=good_colnames))
+ @test names == good_colnames
+ @test length(df[1]) == 4
+ @test length(df) == 13
+ @test df[1] == [1., 1.5, 2., 2.5]
+ @test df[2] == ["A", "BB", "CCC", "DDDD"]
+ @test df[3] == [true, false, false, true]
+ @test df[4] == [2, "EEEEE", false, 1.5]
+ @test df[5] == [9., "III", NA, true]
+ @test df[6] == [3, NA, 3.5, 4]
+ @test df[7] == ["FF", NA, "GGG", "HHHH"]
+ @test df[8] == [NA, true, NA, false]
+ @test df[9] == [Date(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), DateTime(1988, 4, 9), Dates.Time(15, 2, 0)]
+ @test df[10] == [Date(1965, 4, 3), DateTime(1950, 8, 9, 18, 40), Dates.Time(19, 0, 0), NA]
+ @test isa(df[11][1], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][2], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][3], ExcelReaders.ExcelErrorCell)
+ @test isa(df[11][4], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][1][], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][2][], ExcelReaders.ExcelErrorCell)
+ @test isa(df[12][3][], ExcelReaders.ExcelErrorCell)
+ @test DataValues.isna(df[12][4])
+ @test df[13] == [NA, 3.4, "HKEJW", NA]
+
+ # Too few colnames
+ @test_throws ErrorException create_columns_from_iterabletable(load(filename, "Sheet1!C4:O7", header=true, colnames=[:c1, :c2, :c3, :c4]))
+
+ # Test for constructing DataFrame with empty header cell
+ data, names = create_columns_from_iterabletable(load(filename, "Sheet2!C5:E7"))
+ @test names == [:Col1, :x1, :Col3]
+ end
+end