Stipple.jl can't get notify working

Hi there
julia noob here. Starting to use Stipple.jl and hit a stumbling block caused by my ignorance. I am using zmq to pass an ipm who’s payload can insert a row or update an existing row in a dataframe. I have the concept working in Dash but want to look into using Stipple.jl. I am using dataframes for a specific reason and don’t want to use Dict or Arrays right now. I cannot figure out how to set up the notify function to “refresh” the Stipple model. The Stipple.jl code below generates

Received request: STK~MA~OPTION_IMPLIED_VOL~900.0
message to add : STK MA iv 900.0
ERROR: LoadError: type Ptr has no field DataTable

I am defining the DataTable like this.


@reactive mutable struct TontineModel <: ReactiveModel
  tontine_data::R{DataTable} = DataTable(df_table)   
end

So I have a script to generate test data that should feed the Stipple code

using ZMQ
using CSV
using DataFrames
context = Context()
socket = Socket(context, PUSH)
ZMQ.connect(socket, "tcp://localhost:5555")
fields  = ["LAST","OPTION_IMPLIED_VOL","VOLUME","IV"]


function generate_TWS_messages()

        for test in 1:1000

            source = "STK"

            value = round((rand( (2, 9))* 100);digits = 2)

            symbol = rand(symbols.Sym)

            field = rand(fields)

            send_message = (source * "~" * symbol * "~" * field * "~"* string(value) )
                    
            # send_message = test_new_message(symbols)

            println(send_message)

            ZMQ.send(socket, send_message)             #"Hello" )
        end
end


generate_TWS_messages()


ZMQ.send(socket,"END")

# Making a clean exit.
ZMQ.close(socket)
ZMQ.close(context)

and the Stipple.jl code. So a zmq message comes in and either inserts or updates a row in the dataframe. When this happens I want to update the UI dataframe.


using Stipple
using StippleUI
using StipplePlotly

using CSV, DataFrames, Dates
using ZMQ

dash_columns = ["sym","price","sdmove","hv20","hv10","hv5","iv","iv%ile","prc%ile","volume"]
 
df_table = DataFrame([col => (col == "sym" ? String : Float64)[] for col in dash_columns ])


zmq_dash = Dict("LAST" => "price","CLOSE" => "price","OPTION_IMPLIED_VOL" => "iv",
                         "VOLUME"  => "volume","IV" => "iv","IV_PERCENTILE" => "iv%ile" ,"HV20" => "hv20",
                         "HV10" => "hv10","HV5" => "hv5" ,"PRICE_PERCENTILE" => "prc%ile")





@reactive mutable struct TontineModel <: ReactiveModel
  tontine_data::R{DataTable} = DataTable(df_table)   
end


function ui(model::TontineModel)
  page(
    model, class="container", title="title TONTINE2 ", head_content=Genie.Assets.favicon_support(),
    prepend = style(
    ),

    [
      heading("heading Tontine2 7/18/22")

      row([
        cell(class="st-module", [
          h5("h5 tontine data")
          table(:tontine_data)
        ])
      ])
    ]
  )
end

route("/") do
  TontineModel |> init |> ui |> html
end

up(9000; async = true, server = Stipple.bootstrap())


context = Context()

socket = Socket(context, PULL)

ZMQ.bind(socket, "tcp://*:5555")

println("starting IN Socket 5555")

println("entering main, waiting for message")

while true
             
   message = String(ZMQ.recv(socket))
   println("Received request: $message")
   if message == "END"
      println("dying")
      ZMQ.close(socket)
      ZMQ.close(context)
      break
   end

   global in_source,sym_in,field_in ,value_in  = split( message , "~")

   global value_fl = parse(Float64, value_in) # convert to Float64

   try
     global field_out = zmq_dash[ field_in]                # ie field_in "OPTION_IMPLIED_VOL" => field_out "iv"

     println("message to add : ", in_source," ",sym_in," ",field_out," " ,value_in)

   catch e

     println("field_in : ", field_in, " not in cols" )

   end

   if sym_in in df_table[!,:sym]

     df_table[findfirst(==(sym_in),df_table.sym),findfirst(==(field_out),names(df_table))] = value_fl

   else

      push!(df_table, (sym_in, 0.0, 0.0, 0.0 , 0.0, 0.0 , 0.0 , 0.0 , 0.0 , 0.0 ) )
      df_table[findfirst(==(sym_in),df_table.sym),findfirst(==(field_out),names(df_table))] = value_fl

   end 

   #notify(TontineModel.tontine_data )

   # try  TontineModel.layout.DataTable  FOUND IN REPL TAB EXPANSION OF TontineModel

   notify(TontineModel.layout.DataTable)

end

All hail @hhaensel for taking the time to help a noob. This is HIS code not mine, you know this because IT WORKS, it’s got a much better approach and there is lashings of things for noobs to unpack. Just take a look at the random generator, or the manner in which the dataframe is updated. Clearly a man who knows where his towel is.

The fact that, ONCE AGAIN, my inability to read is front and center. I found this code from @hhaensel to be an excellent learning tool.
theakson

#https://github.com/GenieFramework/Stipple.jl/discussions/126

using Stipple
using StippleUI
using StipplePlotly

using CSV, DataFrames, Dates

dash_columns = ["sym","price","sdmove","hv20","hv10","hv5","iv","iv%ile","prc%ile","volume"]

df = DataFrame([col => (col == "sym" ? String : Float64)[] for col in dash_columns])
df_table = Observable(df)

zmq_dash = Dict("LAST" => "price","CLOSE" => "price","OPTION_IMPLIED_VOL" => "iv",
                         "VOLUME"  => "volume","IV" => "iv","IV_PERCENTILE" => "iv%ile" ,"HV20" => "hv20",
                         "HV10" => "hv10","HV5" => "hv5" ,"PRICE_PERCENTILE" => "prc%ile")

@reactive mutable struct TontineModel <: ReactiveModel
    tontine_data::R{DataTable} = DataTable(df_table[])
end

function ui(model::TontineModel)
    page(
        model, class="container", title="title TONTINE2 ", head_content=Genie.Assets.favicon_support(),

        [
        heading("heading Tontine2 7/18/22")

        row([
            cell(class="st-module", [
            h5("h5 tontine data")
            table(:tontine_data)
            ])
        ])
        ]
    )
end

function handlers(model)
    on(df_table) do _
        notify(model.tontine_data)
    end

    model
end

route("/") do
    TontineModel |> init |> handlers |> ui |> html
end

# up(9000; async = true, server = Stipple.bootstrap())
up()


for _ in 1:10
    # generate a random entry
    message = join([
        ["A", "B", "C", "D", "E", "F", "G"][rand(1:7)],
        ["a", "b", "c", "d", "e", "f", "g"][rand(1:7)],
        collect(keys(zmq_dash))[rand(1:length(zmq_dash))],
        string(rand())
    ], "~")
    sleep(0.5)

    global in_source, sym_in, field_in, value_in = split(message , "~")
    global value_fl = parse(Float64, value_in) # convert to Float64

    try
        global field_out = zmq_dash[field_in]                # ie field_in "OPTION_IMPLIED_VOL" => field_out "iv"
        println("message to add : $in_source, $sym_in, $field_out, $value_in")
    catch e
        println("field_in : ", field_in, " not in cols" )
    end

    sym_in in df_table[][!,:sym] || push!(df_table[], (sym_in, 0.0, 0.0, 0.0 , 0.0, 0.0 , 0.0 , 0.0 , 0.0 , 0.0))
    df_table[][findfirst(==(sym_in), df_table[].sym), findfirst(==(field_out), names(df_table[]))] = value_fl
    notify(df_table)
end