Stipple.jl icon indicating copy to clipboard operation
Stipple.jl copied to clipboard

Displaying table with columns of type different from String

Open PGimenez opened this issue 2 years ago • 2 comments
trafficstars

A Genie Builder user reported this issue:

We found a bug with the table display. Basically, whenever we send a table with column values that JSON3 can't directly handle, the display isn't shown. For example, the attached MWE has a table with units from Unitful.jl and it is not. The fix consists of converting all entries of the table to string first with string.(dataframe) before sending to the Quasar UI components.

module App
# set up Genie development environmet
using GenieFramework
using DataFrames
using Unitful
@genietools

# add your data analysis code
function mean(x)
    sum(x) / length(x)
end

# add reactive code to make the UI interactive
@app begin
    # reactive variables are tagged with @in and @out
    @in N = 0
    @out msg = "The average is 0."
    # @private defines a non-reactive variable
    @private result = 0.0

    @out table = DataTable(DataFrame(a = rand(5) * u"m", b = rand(5) * u"s"))

    # watch a variable and execute a block of code when
    # its value changes
    @onchange N begin
        # the values of result and msg in the UI will
        # be automatically updated
        result = mean(rand(N))
        msg = "The average is $result."
        table = DataTable(DataFrame(a = rand(5) * u"m", b = rand(5) * u"s"))
    end
end

# register a new route and the page that will begin
# loaded on access
@page("/", "app.jl.html")
end

<h4> Hello Genie! </h4>
<p> Generated {{N}} random numbers. </p>
<p v-text="msg"></p>
<q-slider v-model="N" :label="true"></q-slider>
<p>This is the default view for the application.</p>
<p>You can change this view by editing the file <code>app.jl.html</code>.</p>
<q-table :data="table.data"></q-table>

PGimenez avatar Jul 04 '23 15:07 PGimenez

You could define your own render function for unitful types, e.g.:

function Stipple.render(x::Union{Unitful.AbstractQuantity, Unitful.Unitlike})
    io = IOBuffer()
    show(io, x)
    String(take!(io))
end
Stipple.render(x::Array{<:Union{AbstractQuantity, Unitful.Unitlike}, N}) where N = Stipple.render.(x)

and add recursive rendering for Dicts

Stipple.render(d::AbstractDict) = OrderedDict(keys(d) .=> render.(values(d)))
Stipple.render(d::Array{<:AbstractDict, N}) where N = render.(d)
function Stipple.render(t::T) where {T<:DataTable}
   render(StippleUI.Tables.data(t))
end

then

julia> dt = DataTable(DataFrame(a = rand(5) * u"m", b = rand(5) * u"s"));
julia> render(dt)
OrderedDict{String, Vector} with 2 entries:
  "data"    => OrderedDict{String, Any}[OrderedDict("__id"=>1, "b"=>"0.8485991568795399 s", "a"=>"0.4107245790663422 m"), …
  "columns" => Column[Column("a", false, "a", :left, "a", true), Column("b", false, "b", :left, "b", true)]

@essenciary We might consider recursive rendering for Dicts and Vectors in general?

hhaensel avatar Jul 05 '23 23:07 hhaensel

Thought a bit about my last sentence. General recursive rendering in Vectors is certainly not a good idea, as it would largely decrease the performance when rendering vectors of numbers. But for special types, e.g. DataTables that would be a good idea. Still not sure what the best approach would be.

hhaensel avatar Aug 20 '23 07:08 hhaensel