Where to Discuss?

Local Group

Preface

Goal: Add common feature such as configuration, terminal interface, and web server

This is a multiparts article.

Optionally, we need a web server, to refactor the HTML document into chunks. So we can develop easier.

As I promised, we will discuss about simple web server. We will utilize AIOHTTP as web server and Jinja2 as templating engine.


15: Static Sites with AIOHTTP

Web Server: We Serve Stuff

Instead of openong file from time to time. We can utilize simple web server. This would make our project more professional. And also, other people can have a look at from other pc as well, as long as it is in the same network.

There are some web server choices including Flask and AIOHTTP. You can pick whataver suit you best.

Official Documentation

Python Source Code

For convenience, I present the source code as well.

Serving HTML

The code is simple. I grab it from official documentation.

Instead of open `23-show.html’ from file manager, to either firefox or chrome, we can utilize web server.

import logging
from aiohttp import web

async def root_handler(request):
    return web.HTTPFound('/23-show.html')

def main():
  logging.basicConfig(level=logging.DEBUG)

  app = web.Application()

  app.router.add_route('*', '/', root_handler)
  app.router.add_static('/', '../site')

  web.run_app(app)

main()

Python: AIOHTTP: Serving HTML

Serving Static Files

All assets, such as bulma css and custom javascript, can be called from a static folder such as site.

  app.router.add_route('*', '/', root_handler)
  app.router.add_static('/', '../site')

Running Web Server

Running web server is as simple as running script.

 python 26-jinja2.py
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)

Python: AIOHTTP: Run

Preview in Web Browser

So we can we this http protocol in our web browser.

Python: AIOHTTP: Preview Web Browser

You might wondering the result between: localhost:8080, 127.0.0.1:8080, and 0:0:0:0:8080.

Logging

Since we have this line:

import logging

def main():
  logging.basicConfig(level=logging.DEBUG)

Everytime, we load a file, we have this bunch of long log.

INFO:aiohttp.access:192.168.0.67 [23/Jan/2023:17:26:52 +0000] "GET /favicon.ico HTTP/1.1" 404 173 "http://192.168.0.191:8080/" "Mozilla/5.0 (Linux; Android 5.1; A1601) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Mobile Safari/537.36"

Python: AIOHTTP: Logging

Access From Network

Now we can check our IP Address from command line.

❯ ip addr show wlan0

Python: AIOHTTP: ip addr show wlan0

If you need some reference on linux, you might consider to read this article:

From command above I know that, my ip address is 192.168.0.191.

Now we can open, as you see in statusbar in web browser below:

  • http://192.168.0.191:8080/

Python: AIOHTTP: Access From Network

Now connect your smartphone to your wifi network, and open the same address.

Python: AIOHTTP: Oppo Smartphone

My Oppo F1S show about the same result as above.


16: Serving Jinja2 Template

Enhance IOHTTP with Jinja2 Template

We also need to refactor our HTML into a bunch of template.

The HTML Problem

Where is Waldo?

HTML is not modular. As you can see in figure below, our HTML is getting complex day by day, and tend to be error prone.

Python: AIOHTTP + Jinja2: HTML Problem

You can try to search code part such as Bulma tab. This wil give you sometime to find whatever you seek. This could be even more stressfull in limited time.

Python Source Code

We you can just: copy, paste, and then run.

First we need to prepare handler and template file.

import logging
import sys

import jinja2
import aiohttp_jinja2
from aiohttp import web

class HomeHandler(web.View):

  @aiohttp_jinja2.template("index.jinja")
  async def get(self):
    return {}

Python: AIOHTTP + Jinja2: Brief

And then inject jinja2 in loader.

def main():
  logging.basicConfig(level=logging.DEBUG)

  app = web.Application()

  # setup jinja2 
  aiohttp_jinja2.setup(app,
    loader=jinja2.FileSystemLoader('../templates'))

  app.router.add_get('/', HomeHandler, name="index")
  app.router.add_static('/', '../site')

  web.run_app(app)

main()

Python: AIOHTTP + Jinja2: Main

Running Web Server

And finally, run again web server using script.

 python 26-jinja2.py
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)

Python: AIOHTTP + Jinja2: Run

As you can see, there is no such differences, in either script and web browser. They all output the same this as previous.

17: Jinja2 Templates

Separate HTML into chunks.

The good thing with small chunks is, we can examine each HTML code, without slowly getting insane by the complexity.

But how exatcly does it looks?

The directory Tree

Consider have a look at our directory tree:

❯ exa ../templates -T
../templates
├── content-progress.jinja
├── content-report.jinja
├── content-review.jinja
├── head.jinja
├── index.jinja
├── layout.jinja
├── page-title.jinja
├── tab-contents.jinja
└── tab-names.jinja

Python: Jinja2: Directory Tree

Hmmm… Where should I start?

Layout Tree

Confused?

Consider have a look again. We are going to have this layout structure.

layout.jinja
└── head.jinja

index.jinja
├── page-title.jinja
├── tab-names.jinja
└── tab-contents.jinja
    ├── content-progress.jinja
    ├── content-review.jinja
    └── content-report.jinja

Layout

This might give you better understanding, on how the html sliced into chunks happened.

<!DOCTYPE html>
<html lang="en">
{% include 'head.jinja' %}
<body>

    {%block body%}{%endblock%}

</body>
</html>

Python: Jinja2: Layout

Simple right?

How about the code below

{% include 'head.jinja' %}

What is inside the head?

You can add meta, css link, or script here:

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, initial-scale=1.0"/>

    <title>Your mission. Good Luck!</title>

    <link rel="stylesheet" href="bulma.min.css">
    <link rel="stylesheet" href="hover.css">
    <link rel="stylesheet" href="animate.css">

    <script src="22-tabs.js"></script>
    <script src="23-script.js"></script>
</head>

Python: Jinja2: Head

Index

Extending Layout

Our python script load index.jinja right? What is the relationship with layout?

{% extends "layout.jinja" %}
{% block body %}
<div class="columns box m-1 is-multiline">
    <div class="column is-full has-text-centered">
      {% include 'page-title.jinja' %}
    </div>

    <div class="column is-full">
      {% include 'tab-names.jinja' %}
    </div>

    <div class="column is-full">
      {% include 'tab-contents.jinja' %}
    </div>
</div>
{% endblock %}

Python: Jinja2: Index

This clearly show our previous structure:

index.jinja
├── page-title.jinja
├── tab-names.jinja
└── tab-contents.jinja
    ├── content-progress.jinja
    ├── content-review.jinja
    └── content-report.jinja

Now we can have a peek, one template at a time:

Page Title

Index > Page Title

This is soooo short.

      <h1 class="title is-3 has-text-dark
                 hvr-underline-from-center"
        >Your mission. Good Luck!</h1>

Python: Jinja2: Page Title

We should do it often!

Tab Names

Index > Tab Names

Now we can concentrate on Bulma Tab instead of looking, where the code reside.

      <div class="tabs is-centered">
        <ul class="tab-headers">
          <li data-target="progress" id="tab-progress"
              class="hvr-pulse-grow is-active">
            <a><span>Progress</span></a>
          </li>
          <li data-target="review" id="tab-review"
              class="hvr-pulse-grow">
            <a><span>Review</span></a>
          </li>
          <li data-target="report" id="tab-report"
              class="hvr-pulse-grow">
            <a><span>Report</span></a>
          </li>
        </ul>
      </div>

Python: Jinja2: Tab Names

Tab Contents

Index > Tab Contents

The rest follow. and you can even include sub template as well.

      <div class="tab-contents has-text-centered">
        <div id="progress"
             class="animate__animated animate__bounceInLeft">
          {% include 'content-progress.jinja' %}
        </div>
        <div id="review"
             class="animate__animated animate__bounceInLeft">
          {% include 'content-review.jinja' %}
        </div>
        <div id="report"
             class="animate__animated animate__bounceInLeft">
          {% include 'content-report.jinja' %}
        </div>
      </div>

Python: Jinja2: Tab Contents

Progress Content

Index > Tab Contents > Progress Content

Now you can manage each content. For example for this daily spreadsheet, this is the HTML representation

  <div class="columns">
    <div class="column is-full">
      Sender Timestamp: <span id="d-timestamp"></span>
      <br/>
      Connection Status: <span id="d-status"></span>
    </div>
  </div>

  <table class="table mx-auto">
  <thead>
    ...
  </thead>
  <tbody>
    ...
  </tbody>
  </table>

Python: Jinja2: Progress Content

Review Content

Index > Tab Contents > Review Content

And osi for the global spreadsheet, this is the HTML representation

  <div class="columns">
    <div class="column is-full">
      Sender Timestamp: <span id="c-timestamp"></span>
      <br/>
      Connection Status: <span id="c-status"></span>
    </div>
  </div>

  <table class="table mx-auto">
  <thead>
    ...
  </thead>
  <tbody>
    ...
  </tbody>
  </table>

Python: Jinja2: Review Content

Report Content

Index > Tab Contents > Report Content

And finally, our chart… Still under construction here.

          <h3 class="title is-4">Report</h3>
          <p>Lorem ipsum dolor sit amet,
             consectetur adipiscing elit.
             <b>Quisque in faucibus magna.</b></p>

Python: Jinja2: Report Content

No need to be perfect.

Preview in Browser

Nothing chance.

The result is exactly the same with, our previous HTML site.

Python: Jinja2:

We can keep the site persistent.

More Jinja2 Example

If you want to learn more about Jinja, you can visit my site:

Or even my Pelican Bulma showcase at my repository:


What is Next 🤔?

We will continue integrating web logging with rich panel using StringIO.

Consider continue reading [ Excel - Monitor - Logging Panel ].