Engee documentation
Notebook

Airport scoreboard with dynamic table in GenieFramework

In this article, we will look at creating a web application - an airport scoreboard with a table of flights. To get acquainted with the basic logic of web applications in GenieFramework, we recommend that you read the introductory статьей. Here we will focus on working with tables through DataTable, interacting with CSV files and dynamically updating data using an asynchronous loop. We will pay special attention to why it is used to display the table. DataTable Rather than just DataFrame.

board.png

Application Code

using GenieFramework
using Stipple
using Stipple.Tables
using DataFrames
using Dates
using CSV

function generate_flight_data()
DataFrame(
Flight number=>[SU123, AF456, BA789],
Destination=>[Moscow, Paris, London],
        Departure time => [Dates.format(now() + Hour(rand(1:5)), HH:MM) for _ in 1:3],
        Status => [On Time, Delayed, Cancelled]
)
end

@app begin
    @out current_time = Current time:  * Dates.format(now(), HH:MM:SS)
@out flight_table = DataTable(DataFrame()) # Empty table initially

    @onchange isready begin
        initial_df = generate_flight_data()
        CSV.write(flight_data.csv, initial_df)
        flight_table = DataTable(initial_df)

        @async while true
            sleep(5)
current_time = Current time:  * Dates.format(now(), HH:MM:SS)
df = CSV.read(flight_data.csv, DataFrame, types=String)
df.Status[rand(1:3)] = rand([On Time, Delayed, Canceled])
CSV.write(flight_data.csv, df)
            flight_table = DataTable(df)
        end
    end
end

function ui()
    [
        h3({{current_time}}),
        table(:flight_table; dense=true, flat=true)
    ]
end

@page(/, ui)

Program logic: step-by-step analysis

Connecting packages

The following packages are used:

  • GenieFramework: The main framework for creating web applications.
  • Stipple: Provides reactivity and communication between the server and the interface.
  • Stipple.Tables: Provides DataTable to display tables in the web interface.
  • DataFrames, Dates, CSV: Needed for working with tabular data, time formatting, and file operations.
    Each package plays its own role, providing a minimal set of tools for the application.

Data generation

Function generate_flight_data() creates a flight table:

  • Returns an object DataFrame with four columns:
    • FlightNumber: Flight numbers (SU123, AF456, BA789).
    • Destination: Destinations (Moscow, Paris, London).
    • DepartureTime Departure time in the HH:MM format, based on the current time plus a random offset (1-5 hours).
    • Status: Flight statuses (On Time, Delayed, Cancelled).
  • This function emulates the initial data for the scoreboard, which will then be changed during the operation of the application.

Data model: variables

Block @app defines the data model:

  • @out current_time: A reactive variable containing a string with the current time (Current time: HH:MM:SS). It is initially set at startup and updated later.
  • @out flight_table: A reactive type variable DataTable, initially empty (DataTable(DataFrame())). It will be filled with data when the application is loaded.
    These variables are available for display in the interface and automatically update it when it changes.

Data model: initialization and updating

The handler @onchange isready controls the application logic:

  • isready and his role:
    • isready — this is the built-in reactive variable Stipple, which initially has the value false. She becomes true when the client (browser) has fully loaded the page and established a connection to the server via WebSocket.
    • @onchange isready means that the code inside the block is executed once when isready changes from false on true that is, at the first successful download of the application on the client side. This does not mean that updates are limited to this moment only. — @onchange it runs the code once, but we can initiate persistent processes inside it.
    • We use @onchange isready to ensure that data initialization and the start of the update cycle occur only after the interface is ready for operation. Without this, the code could execute too early, before establishing communication with the client, which would lead to synchronization errors.
  • Initialization:
    • The initial table is being created initial_df through generate_flight_data().
    • Data is written to a file flight_data.csv.
    • flight_table updated with DataTable(initial_df).
  • Asynchronous loop:
  • Starts via @async while true, which creates a background task.
    • Every 5 seconds (sleep(5)):
  • Updated current_time.
    • Data is being read from a file with the parameter types=String to save the string format.
    • The status of one flight is randomly changed.
    • Updated data is written to a file and assigned flight_table.

Interface

Function ui() sets the appearance:

  • h3({{current_time}}): Displays the current time, which is updated automatically.
  • table(:flight_table; dense=true, flat=true): Creates a table linked to flight_table.
    • dense=true makes the table compact.
    • flat=true removes shadows for a simple design.
    • DataTable automatically generates headlines from column names DataFrame.

Page Registration

  • @page(/, ui): Indicates that the interface is accessible via the root route /. This completes the configuration of the application.

Why DataTable Rather than just DataFrame?

DataFrame — this is a powerful Julia tool for working with tabular data in code. It is great for data manipulation: filtering, sorting, and calculations. However, by itself DataFrame It is not intended to be displayed in a web interface. It represents data in the program's memory, but it does not have built-in logic for transferring it to the browser or rendering it as an HTML table.

DataTable from Stipple.Tables solves this problem:

  • Reactivity: DataTable integrates with the Stipple reactivity system. When changing the data in flight_table The interface is updated automatically without the need to manually redraw the table.
  • Formatting for the web: DataTable converts DataFrame to a structure compatible with Quasar (the framework used by Stipple for the interface), adding headers and data in the desired format.
  • Simplicity: Use table(:flight_table) allows you to display data without writing complex HTML or JavaScript code. If we used just DataFrame, I would have to manually convert it to an array of dictionaries (Vector{Dict}) and pass it to the interface, which complicates the code.

Thus, DataFrame — this is the raw data, and DataTable — a bridge between them and the web interface, providing convenience and automation.


How do I launch the app?

Go to the folder with the current script and execute the cell below.

As a result, a window with a web application should open.

If it doesn't open in a separate tab, click on the link from the output cell.

  • match(r'(https?://[^']+)' allows you to find a link based on the output of the engee function.genie.start using regular expressions
  • Markdown.parse it is used to display clickable links in the output cell.
In [ ]:
using Markdown
cd(@__DIR__)

app_url = string(engee.genie.start(string(@__DIR__,"/app.jl")))

Markdown.parse(match(r"'(https?://[^']+)'",app_url)[1])

Conclusion

The application demonstrates the creation of an airport scoreboard with a dynamic table. We looked at generating data, saving it to a file, updating it through an asynchronous loop, and displaying it using DataTable. Each block of code performs its own task, providing a minimalistic and functional implementation. DataTable it turned out to be a key element that simplifies the integration of tabular data into the web interface, which cannot be achieved with the usual DataFrame. To fully understand the logic, we recommend taking a quick look at the entire code — this will help you see how all the parts are interconnected.