Skip to main content

A Flask server for React data dashboards

Project description

Cardboard

Project planning


A simple data dashboard for use in Flask apps. Each card on the board manages its own WebSocket connection to the server to be abe to handle multiple high bandwidth data sources such as audio or high frequency sensor data which could otherwise saturate a single multiplexed Flask SocketIO connection.

While this project was intended for use on localhost, it should be able to be deployed to the web as well, though we haven't needed to try this yet. This library is being created for a very specific use case in a private project, but perhaps someone out there will find it useful too.

Prerequisites

  • Python 3.7+
  • Npm 10.8+
  • GNU Make 3.8+

Development

Project Initialization

After cloning the repository, install backend and frontend dependencies. The make init target will install both backend and frontend dependencies.

Install Dependencies

make init

Alternatively, you can separately install the backend dependencies with pip and the frontend dependencies with npm.

Install Backend Dependencies with Pip

From the project root run pip install:

pip install -r requirements.txt

Install Frontend Dependencies with npm

From the cardboard_ui directory, run npm install:

cd cardboard_ui
npm install

Start the Development Servers

For development, the Flask development server can be used to automatically reload when backend Python file changes are detected. The Vite development server can be used to automatically reload when front-end Javascript or CSS files change. The Flask server runs on http://127.0.0.1:5000. The Vite server runs on http://127.0.0.1:5173. When running the development servers, access the app from a webbrowser using the Vite server at http://127.0.0.1:5173.

  1. Start the Flask development server:
make start_flask
  1. Start the Vite development server:
make start_vite

Start the WSGI Production Server

For production, Gunicorn is used to run the WSGI app on http://127.0.0.1:5000. The Vite server is not used. The front-end resources must be compiled and packaged and will be served by the production server.

  1. Start the Gunicorn WSGI server:
make start_wsgi

Installation of Published Packages

This section describes how to install the cardboard packages for development and production. This assumes cardboard is being integrated into a Python Flask application with a front-end built using Vite with the React plugin. In this context, we assume the basic directory structures:

project-root/
  .venv/
  flask-dir/  
    app.py
  ui-dir/  
    node_modules/  
    src/  
      main.jsx
      index.css
    vite.config.js
    package.json  

Install Production Packages

Backend

cd <project-root>
pip install cardboard
cd <ui-dir>
npm install cardboard-ui

Installing from Local Dev Projects

This assums you've cloned the Cardboard git repository to cardboard-project-root.

Backend

pip install <cardboard-project-root>/dist/cardboard-<version>-py3-none-any.whl

Frontend

cd <cardboard-project-root>/cardboard_ui
npm link
cd <project-root>/<ui-dir>
npm link cardboard-ui

Installing from TestPyPi

Use this command to install from the TestPyPi registry instead of the proeuction PyPi registry.

pip install -i https://test.pypi.org/simple/ cardboard

Usage

  1. Create an configure the Flask app for either developmet or production mode.
  2. Load the Vite manifest
  3. Import the cardboard blueprint into the Flask app.
  4. Register the blueprint.
  5. Load a json board configuration file.
  6. Add the cardboard javascript and css assets to the Flask index.html template
  7. Add a <div> with the id 'root' to the <body> of index.html.
  8. Render the Board component into the root div in main.jsx.

app.py

"""
Example Flask cardboard server
"""
from flask import Flask, render_template, send_from_directory, jsonify, request
from cardboard import cardboard, blueprints
import os
import json

# Load environment variables
FLASK_ENV = os.environ.get("FLASK_ENV", default="production")
VITE_MANIFEST = os.environ.get("VITE_MANIFEST", default="./test_ui/dist/.vite/manifest.json")

# If development, serve static resources from the ui src, otherwise serve from ui dist
if FLASK_ENV == "development":
    app = Flask(__name__, template_folder="templates", static_folder="../test_ui/src")
else:
    app = Flask(__name__, template_folder="templates", static_folder="../test_ui/dist")

# Load the Vite manifest
manifest_file = "./test_ui/dist/.vite/manifest.json"
with open(manifest_file, "r") as f:
    manifest = json.load(f)

# Register cardboard blueprint routes
app.register_blueprint(blueprints.cardboard_blueprint)


@app.route("/")
def index():
    """
    Serve the index.html template, pass it the development mode and the server url
    :return: rendered index.html template
    """
    development = FLASK_ENV == "development"
    cardboard_server="http://127.0.0.1:5000"
    return render_template("index.html", development=development, cardboard_server=cardboard_server)


@app.route('/src/<path:file>')
def serve_src(file):
    """
    Serve the Vite .jsx or .css assets defined in the Vite manifest.
    This will be used only in production mode.  In development, the assets get served by the Vite dev server.
    :param file:
    :return:
    """
    global manifest
    path = request.path
    if path.startswith("/"):
        path = path[1:]

    if path.endswith(".css"):
        key = path.replace(".css", ".jsx")
        if key not in manifest:
            return jsonify({"Error": f"Manifest entry not found: {key}"}), 404
        if 'css' not in manifest[key]:
            return jsonify({"Error": f"No css for manifest entry {key}"}), 404
        css = manifest[key]['css']
        if len(css) == 0:
            return jsonify({"Error": f"CSS list for manifest entry {key} is empty"}), 404
        f = css[0]
    else:
        if path not in manifest:
            return jsonify({"Error": f"File not found: {path}"}), 404
        f = manifest[f'{path}']['file']
    return send_from_directory(app.static_folder, f)


if __name__ == "__main__":
    print(f"Hello cardboard")

    with open("./cards.json") as f:
        board_json = json.load(f)
        cardboard.configure_board(data=board_json)

    app.run(host="127.0.0.1", port=5000, debug=True)

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Hello, Cardboard</title>
  </head>

  <body style="width: 100vw; height: 100vh;">

    <!-- React container, Cardboard components well be rendered here -->
    <div id="root" style="width: 100%; height: 100%;"></div>

    <!-- Load the template data passed from the Flask server -->
    <script>
      const cardboard_server = "{{cardboard_server}}"
      const development = "{{development}}"
      console.log("cardboard_server=" + cardboard_server)
      console.log("development=" + development)
    </script>

    <!-- Refer to Vite Backend Integration documentation:            -->
    <!-- https://vite.dev/guide/backend-integration.html             -->

    {% if development %}

    <!-- If development mode, setup proxying to the Vite dev server. -->
    <!-- Make sure the Vite dev server is running on port 5173       -->
    <script type="module">
      console.log("DEV MODE!")
      import RefreshRuntime from 'http://localhost:5173/@react-refresh'
      RefreshRuntime.injectIntoGlobalHook(window)
      window.$RefreshReg$ = () => {}
      window.$RefreshSig$ = () => (type) => type
      window.__vite_plugin_react_preamble_installed__ = true
    </script>
    <script type="module" src="http://localhost:5173/@vite/client"></script>
    <script type="module" src="http://localhost:5173/src/main.jsx"></script>

    {% else %}

    <!-- If production mode, we serve the compiled assets from the flask server -->
    <!-- Vite dev server does not need to be running                            -->
    <script type="module" crossorigin src="/src/main.jsx"></script>
    <link rel="stylesheet" crossorigin href="/src/main.css">

    {% endif %}
  </body>
</html>

main.jsx

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { Board } from 'cardboard-ui'
import 'cardboard-ui/dist/style.css'

// Get the root div by Id and render the cardboard React component into it.
// Pass the cardboard_server template parameter (from the Flask server) to the Board component.
createRoot(document.getElementById('root')).render(
  <StrictMode>
  <div className="flex flex-col">
      <Board cardboard_server={cardboard_server} />
  </div>
  </StrictMode>,
)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cardboard_server-0.1.0.tar.gz (14.8 kB view details)

Uploaded Source

Built Distribution

cardboard_server-0.1.0-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

File details

Details for the file cardboard_server-0.1.0.tar.gz.

File metadata

  • Download URL: cardboard_server-0.1.0.tar.gz
  • Upload date:
  • Size: 14.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.12.2

File hashes

Hashes for cardboard_server-0.1.0.tar.gz
Algorithm Hash digest
SHA256 86fad435d1e40c970a5dd652471314a56acc5c40d6929982ea8bab5c4e2e9adf
MD5 44ee7d1138b08cfadb36828f305f7bbc
BLAKE2b-256 91dc5b73b6b1aeb3bfc6e61d39ad2886ede6b0ee0f9b2899ecd5a4d5b8efb206

See more details on using hashes here.

File details

Details for the file cardboard_server-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for cardboard_server-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ac89dac4e1e552fcc0a3959025fed6f1c37f5a6249e158387131bf4abba9ee8d
MD5 a5e81865f04e9ed28f71f201abe18d4e
BLAKE2b-256 60844be9c2968ed5003215ba074035e4123c8b8bbc3fb51caa37533f06f1401e

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page