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 Float64sSome StringsSome BoolsMixed columnMixed with NAFloat64 with NAString with NABool with NASome datesDates with NASome errorsErrors with NAColumn with NULL and then mixed
1.0"A"true2.09.03.0"FF"#NA2015-03-03T00:00:001965-04-03T00:00:00#DIV/0!#DIV/0!#NA
1.5"BB"false"EEEEE""III"#NA#NAtrue2015-02-04T10:14:001950-08-09T18:40:00#N/A#N/A3.4
2.0"CCC"falsefalse#NA3.5"GGG"#NA1988-04-09T00:00:0019:00:00#REF!#NAME?"HKEJW"
2.5"DDDD"true1.5true4.0"HHHH"false15: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 Float64sSome StringsSome BoolsMixed columnMixed with NAFloat64 with NAString with NABool with NASome datesDates with NASome errorsErrors with NAColumn with NULL and then mixed
1.0"A"true2.09.03.0"FF"#NA2015-03-03T00:00:001965-04-03T00:00:00#DIV/0!#DIV/0!#NA
1.5"BB"false"EEEEE""III"#NA#NAtrue2015-02-04T10:14:001950-08-09T18:40:00#N/A#N/A3.4
2.0"CCC"falsefalse#NA3.5"GGG"#NA1988-04-09T00:00:0019:00:00#REF!#NAME?"HKEJW"
2.5"DDDD"true1.5true4.0"HHHH"false15: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