Lint project

This commit is contained in:
n07070
2026-02-02 12:37:15 +01:00
parent 98299cc281
commit 3769e17444

View File

@@ -1,46 +1,72 @@
# Welcome to the LittlePrynter's source code.
# This program expose a web interface, with user authentification, that makes it possible to print messages from the web.
# It also exposes a API, making it possible to print and interface with much of the printer's abilities.
"""
Welcome to the LittlePrynter's source code.
This program expose a web interface, with user authentification,
that makes it possible to print messages from the web.
It also exposes a API, making it possible to print and interface
with much of the printer's abilities.
# We first define the connection to the printer itself,
# Then we build the API around Flask,
# Then we build the web interface, using the simple Jinja2 templating.
We first define the connection to the printer itself,
Then we build the API around Flask,
Then we build the web interface, using the simple Jinja2 templating.
# We support two modes :
# The first is a simple mode, where a computer, connected to a thermal printer, runs this program and exposes a web interface that makes use of the client's camera
# The seconde is booth mode, where a Raspberry Pi is connected to a thermal printer, a button and a flash. The web interface exists but may not be used, as the press of the button with take a picture and activate the flash while simply informing the web page.
We support two modes :
The first is a simple mode, where a computer, connected to a thermal printer,
runs this program and exposes a web interface that makes use of the client's camera
The seconde is booth mode, where a Raspberry Pi is connected to a thermal printer,
a button and a flash.
The web interface exists but may not be used, as the press of the button with take
a picture and activate the flash while simply informing the web page.
"""
# Following are the librairies we import,
from flask import Flask, request, render_template, flash, abort, redirect, url_for, make_response, jsonify # Used for the web framework
import sys
import os # For VARS from the shell.
import pprint # To pretty print JSON
import toml # Used for the config file parsing
from flask import (
Flask,
request,
render_template,
flash,
redirect,
url_for,
jsonify,
) # Used for the web framework
import werkzeug.exceptions
from flask_socketio import SocketIO
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from printer import Printer # The wrapper for the printer class
from raspberry import Raspberry # The Raspberry pi control Class
from web import Web # Wrapper for the web routes and API
import toml # Used for the config file parsing
import pprint # To pretty print JSON
import time # To sleep
import os # For VARS from the shell.
# Variables
app = Flask(__name__)
socketio = SocketIO(app)
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif"}
# Load the configuration file
try:
app.logger.debug("Loading config file...")
configuration_file = toml.load("configuration/config.toml")
with open("configuration/config.toml", "r", encoding="utf-8") as f:
configuration_file = toml.load(f)
except TypeError:
app.logger.error("Unable to load the config file: invalid type or is a list containing invalid types")
exit(-1)
except toml.TomlDecodeError:
app.logger.error("An error occured while decoding the file")
exit(-1)
except Exception as e:
app.logger.error("Error while loading file : " + str(e))
exit(-1)
app.logger.error(
"Unable to load the config file: invalid type or is a list containing invalid types"
)
sys.exit(-1)
except toml.TomlDecodeError as e:
app.logger.error(
"An error occured while decoding the file %s , error at %s:%s",
str(e.doc),
str(e.colno),
str(e.lineno),
)
sys.exit(-1)
except OSError as e:
app.logger.error("Error while loading file %s ", str(e))
sys.exit(-1)
app.logger.debug("Config file loaded !")
@@ -51,69 +77,69 @@ UPLOAD_FOLDER = str(configuration_file["printer"]["upload_folder"])
try:
os.mkdir(UPLOAD_FOLDER)
app.logger.debug(f"Directory '{UPLOAD_FOLDER}' created successfully.")
app.logger.debug("Directory %s created successfully.", UPLOAD_FOLDER)
except FileExistsError:
app.logger.debug(f"Directory '{UPLOAD_FOLDER}' already exists.")
app.logger.debug("Directory %s already exists.", UPLOAD_FOLDER)
except PermissionError:
app.logger.error(f"Permission denied: Unable to create '{UPLOAD_FOLDER}'.")
except Exception as e:
app.logger.error(f"An error occurred: {e}")
app.logger.error("Permission denied: Unable to create %s", UPLOAD_FOLDER)
# Output the config file
if os.getenv('LIPY_DEBUG') == True:
if os.getenv("LIPY_DEBUG") is True:
pprint.pprint(configuration_file)
# We define the app module used by Flask
app.secret_key = configuration_file["secrets"]["flask_secret_key"]
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['ALLOWED_EXTENSIONS'] = ALLOWED_EXTENSIONS
app.config['MAX_CONTENT_LENGTH'] = 10 * 1000 * 1000 # Maximum 3Mb for a file upload
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
app.config["ALLOWED_EXTENSIONS"] = ALLOWED_EXTENSIONS
app.config["MAX_CONTENT_LENGTH"] = 10 * 1000 * 1000 # Maximum 3Mb for a file upload
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Printer connection
# Uses the class defined in the printer.py file
printer = Printer(app,0x04b8, 0x0e28)
printer = Printer(app, 0x04B8, 0x0E28)
printer.init_printer()
# Find out if we are running on a Raspberry Pi
rpi = Raspberry(printer,
rpi = Raspberry(
printer,
app,
socketio,
configuration_file['rpi']['button_gpio_port_number'], configuration_file['rpi']['indicator_gpio_port_number'],
configuration_file['rpi']['flash_gpio_port_number'],
configuration_file['rpi']['flash'] )
configuration_file["rpi"]["button_gpio_port_number"],
configuration_file["rpi"]["indicator_gpio_port_number"],
configuration_file["rpi"]["flash_gpio_port_number"],
configuration_file["rpi"]["flash"],
)
RASPBERRY_PI_CONNECTED = rpi.is_raspberry_pi()
#############################################################
# Web & API routes
#############################################################
web = Web(app, printer)
if __name__ == "__main__":
app.run(debug=True, use_reloader=False, host='0.0.0.0', ssl_context='adhoc')
app.run(debug=True, use_reloader=False, host="0.0.0.0", ssl_context="adhoc")
limiter = Limiter(
get_remote_address,
app=app,
default_limits=["1500 per day", "500 per hour"]
get_remote_address, app=app, default_limits=["1500 per day", "500 per hour"]
)
@app.route('/')
@app.route("/")
@limiter.limit("1/second", override_defaults=False)
def index():
"""Return the web interface index"""
app.logger.debug("Loading index")
return render_template('index.html')
return render_template("index.html")
@app.route('/webcam')
@app.route("/webcam")
@limiter.limit("1/second", override_defaults=False)
def webcam():
"""Returns the webcam web interface"""
app.logger.debug("Loading webcam interface")
return render_template('webcam.html')
return render_template("webcam.html")
# API routes
# The api has the following methods
@@ -121,71 +147,71 @@ def webcam():
# api/auth/{login,logout}
# api/status/{paper,ping,stats}
# If you just call the api route, you get a help back.
@app.route('/api')
@app.route('/api/print')
@app.route("/api")
@app.route("/api/print")
@limiter.limit("1/second", override_defaults=False)
def api_index():
"""Returns a how-to for the API"""
app.logger.debug("Loading API")
return render_template("api.html")
@app.route('/api/print/sms', methods=['POST'])
@app.route("/api/print/sms", methods=["POST"])
@limiter.limit("6/minute", override_defaults=False)
def api_print_sms():
"""Prints a short message on a printer"""
app.logger.debug("Printing an sms")
try:
txt = request.form["txt"]
sign = request.form["signature"]
except Exception as e:
app.logger.error("Whoops, no forms submitted or missing signature :" + str(e))
flash("Whoops, no forms submitted or missing signature : " + str(e))
except werkzeug.exceptions.BadRequestKeyError as e:
app.logger.error(
"Whoops, no forms submitted or missing signature : %s ", str(e)
)
flash("Whoops, no forms submitted or missing signature : %s", str(e))
return redirect(url_for("index"))
web.print_sms(txt, sign)
return redirect(url_for("index"))
@app.route('/api/print/img', methods=['POST'])
@app.route("/api/print/img", methods=["POST"])
@limiter.limit("6/minute", override_defaults=False)
def api_print_image():
"""Prints an image on a printer"""
app.logger.debug("Printing an image")
try:
sign = request.form["signature"]
except Exception as e:
app.logger.error("Whoops, no forms submitted or missing signature :" + str(e))
flash("Whoops, no forms submitted or missing signature : " + str(e))
app.logger.error("Whoops, no forms submitted or missing signature : %s", str(e))
flash("Whoops, no forms submitted or missing signature : %s ", str(e))
return redirect(url_for("index"))
if request.method == 'POST':
if request.method == "POST":
# check if the post request has the file part
try:
if 'img' not in request.files:
app.logger.error("Whoops, no images submitted :" + str(e))
flash("Whoops, no images submitted : " + str(e))
else:
file = request.files['img']
except Exception as e:
app.logger.error('Error getting the files :' + str(e))
flash('Error getting the files :' + str(e))
if "img" not in request.files:
app.logger.error("Whoops, no images submitted : %s ", str(e))
app.logger.error("Error getting the files : %s", str(e))
flash("Whoops, no images submitted : %s", str(e))
return redirect(url_for("index"))
file = request.files["img"]
# If the user does not select a file, the browser submits an
# empty file without a filename.
if file.filename == '':
if file.filename == "":
app.logger.error("Submitted file has no filename !")
flash("Submitted file has no filename !")
return redirect(url_for("index"))
try:
app.logger.debug("Sending the image to the printer.")
web.print_image(file, sign)
except Exception as e:
app.logger.error("The image could not be printed because : " + str(e) )
flash("The image could not be printed because : " + str(e))
app.logger.error("The image could not be printed because : %s ", str(e))
flash("The image could not be printed because : %s ", str(e))
return redirect(url_for("index"))
else:
@@ -193,56 +219,69 @@ def api_print_image():
flash("Method not allowed")
return redirect(url_for("index"))
flash('Picture printed ! '),
flash("Picture printed ! ")
return redirect(url_for("index"))
@app.route('/api/camera/picture')
@app.route("/api/camera/picture")
def camera_picture():
# Returns a picture taken by the camera
"""Returns a picture taken by the camera"""
if RASPBERRY_PI_CONNECTED:
try:
return rpi.camera_picture()
except Exception as e:
return jsonify({'message': 'Error getting the stream : ' + e}), 500
return jsonify({"message": "Error getting the stream : " + e}), 500
else:
return jsonify({'message': 'No camera present'}), 500
return jsonify({"message": "No camera present"}), 500
@app.route('/login')
@app.route("/login")
@limiter.limit("1/second", override_defaults=False)
def login_page():
"""Unsued, logins"""
# web.login(username,password)
return redirect(url_for("index"))
@app.route('/logout')
@app.route("/logout")
@limiter.limit("1/second", override_defaults=False)
def logout_page():
"""Unused, logout"""
# web.logout(username, password)
return redirect(url_for("index"))
@app.errorhandler(429)
def ratelimit_handler(e):
flash("Rate limit reached, please slow down :) ( Currently at "+ e.description + ")", 'error')
app.logger.debug('Rate limit reached ' + str(e.description))
"""Handle rate limits"""
flash(
"Rate limit reached, please slow down :) ( Currently at " + e.description + ")",
"error",
)
app.logger.debug("Rate limit reached %s " , str(e.description))
return redirect(url_for("index"))
@app.route("/ping")
@limiter.exempt
def ping():
flash("🏓 Pong !",'info')
app.logger.debug('🏓 Pong !')
"""Returns a pong"""
flash("🏓 Pong !", "info")
app.logger.debug("🏓 Pong !")
return redirect(url_for("index"))
@socketio.on('ping')
def handle_message(data):
app.logger.debug('Received : ' + str(data))
socketio.emit('pong',"Pong !")
@socketio.on('get_camera_status')
@socketio.on("ping")
def handle_message(data):
"""Handle sockets pings"""
app.logger.debug("Received : %s " , str(data))
socketio.emit("pong", "Pong !")
@socketio.on("get_camera_status")
def camera_status():
app.logger.debug('Client asked if we had a camera')
"""Returns camera status to a socket"""
app.logger.debug("Client asked if we had a camera")
if RASPBERRY_PI_CONNECTED:
socketio.emit("camera_status", True)
else: