diff --git a/projects/tentacles/src/python/tentacles/__main__.py b/projects/tentacles/src/python/tentacles/__main__.py index 46ec85b..c7fb82c 100644 --- a/projects/tentacles/src/python/tentacles/__main__.py +++ b/projects/tentacles/src/python/tentacles/__main__.py @@ -116,14 +116,21 @@ def serve(hostname: str, port: int, config: Path, trace: bool): if config: with open(config, "rb") as fp: app.config.update(tomllib.load(fp)) + print(app.config) + # Run migrations once at startup rather than when connecting + with closing(db_factory(app)) as db: + db.migrate() + + # Configuring cherrypy is kinda awful cherrypy.server.unsubscribe() server = cherrypy._cpserver.Server() cherrypy.config.update( { "environment": "production", "engine.autoreload.on": False, + "log.screen.on": True, } ) cherrypy.tree.graft(app, "/") @@ -135,7 +142,6 @@ def serve(hostname: str, port: int, config: Path, trace: bool): server.subscribe() # Spawn the worker thread(s) - Worker(cherrypy.engine, app, db_factory, poll_printers, frequency=5).start() Worker(cherrypy.engine, app, db_factory, assign_jobs, frequency=5).start() Worker(cherrypy.engine, app, db_factory, push_jobs, frequency=5).start() diff --git a/projects/tentacles/src/python/tentacles/blueprints/admin_ui.py b/projects/tentacles/src/python/tentacles/blueprints/admin_ui.py index ad55e5b..e0068d1 100644 --- a/projects/tentacles/src/python/tentacles/blueprints/admin_ui.py +++ b/projects/tentacles/src/python/tentacles/blueprints/admin_ui.py @@ -103,6 +103,26 @@ def handle_add_printer(): return render_template("printers.html.j2") +@BLUEPRINT.route("/admin/printers/edit", methods=["GET"]) +@requires_admin +def get_edit_printers(): + pid = int(request.args.get("id", "-1")) + if row := ctx.db.fetch_printer(pid=pid): + return render_template("edit_printer.html.j2", printer=row) + else: + flash("No such printer", category="error") + return redirect("/admin"), 404 + + +@BLUEPRINT.route("/admin/printers/edit", methods=["POST"]) +@requires_admin +def handle_edit_printers(): + args = request.form.copy() + args["id"] = int(args["id"]) + ctx.db.edit_printer(**request.form) + return redirect("/admin") + + @BLUEPRINT.route("/admin/files", methods=["POST"]) @requires_admin def manipulate_files(): diff --git a/projects/tentacles/src/python/tentacles/db.py b/projects/tentacles/src/python/tentacles/db.py index d19ad1e..6126c51 100644 --- a/projects/tentacles/src/python/tentacles/db.py +++ b/projects/tentacles/src/python/tentacles/db.py @@ -82,7 +82,22 @@ class Db(Queries): self._conn.row_factory = self._factory self._conn.isolation_level = None # Disable automagical transactions self._cursor = self._conn.cursor() - self.create_tables() + + def migrate(self): + self.migration_0000_create_migrations() + existing_migrations = {it.name for it in self.list_migrations()} + for query in sorted( + ( + q + for q in _queries.available_queries + if q.startswith("migration_") and q not in existing_migrations + ) + ): + log.warn("Applying migration %s", query) + getattr(self, query)() + digest = sha3_256() + digest.update(getattr(_queries, query).sql.encode()) + self.record_migration(name=query, fingerprint=digest.hexdigest()) def begin(self): self._conn.execute("BEGIN") diff --git a/projects/tentacles/src/python/tentacles/schema.sql b/projects/tentacles/src/python/tentacles/schema.sql index 6093a96..1e36258 100644 --- a/projects/tentacles/src/python/tentacles/schema.sql +++ b/projects/tentacles/src/python/tentacles/schema.sql @@ -1,4 +1,31 @@ --- name: create_tables# +-- name: migration-0000-create_migrations +CREATE TABLE IF NOT EXISTS migrations ( + id INTEGER PRIMARY KEY AUTOINCREMENT + , name TEXT + , fingerprint TEXT + , executed_at TEXT DEFAULT (datetime('now')) + , UNIQUE(name) +); + +-- name: list-migrations +SELECT + * +FROM migrations +ORDER BY + datetime(executed_at) ASC +; + +-- name: record-migration! +INSERT INTO migrations ( + name + , fingerprint +) +VALUES ( + :name + , :fingerprint +); + +-- name: migration-0001-create_tables# -- Initialize the core db tables. Arguably migration 0. ---------------------------------------------------------------------------------------------------- -- User structures @@ -347,6 +374,7 @@ SELECT p.id , p.name , p.url + , p.stream_url , p.api_key , p.last_poll_date , s.name as status @@ -360,6 +388,7 @@ SELECT p.id , p.name , p.url + , p.stream_url , p.api_key , p.last_poll_date , s.name as status @@ -388,6 +417,17 @@ WHERE id = :pid ; +-- name: edit-printer +UPDATE printers +SET + name = :name + , url = :url + , stream_url = :stream_url + , api_key = :api_key +WHERE + id = :id +; + ---------------------------------------------------------------------------------------------------- -- Files ---------------------------------------------------------------------------------------------------- diff --git a/projects/tentacles/src/python/tentacles/static/css/_skeleton.scss b/projects/tentacles/src/python/tentacles/static/css/_skeleton.scss index f28bf6c..2b7ca2e 100644 --- a/projects/tentacles/src/python/tentacles/static/css/_skeleton.scss +++ b/projects/tentacles/src/python/tentacles/static/css/_skeleton.scss @@ -59,6 +59,8 @@ .columns:first-child { margin-left: 0; } + .equal.columns {} + .one.column, .one.columns { width: 4.66666666667%; } .two.columns { width: 13.3333333333%; } diff --git a/projects/tentacles/src/python/tentacles/static/css/style.scss b/projects/tentacles/src/python/tentacles/static/css/style.scss index 526b184..0ea459b 100644 --- a/projects/tentacles/src/python/tentacles/static/css/style.scss +++ b/projects/tentacles/src/python/tentacles/static/css/style.scss @@ -54,6 +54,10 @@ label { margin-bottom: 20px; } +.u-flex1 { + flex: 1; +} + @media (max-width: 760px) { .file, .printer, .key, .job { flex-direction: column; @@ -65,3 +69,20 @@ label { } } } + +.row.webcams { + display: flex; + justify-content: space-between; + + img { + object-fit: contain; + } + + .webcam { + padding-right: 10px; + } + + .webcam:last-child { + padding-right: 0px; + } +} diff --git a/projects/tentacles/src/python/tentacles/templates/edit_printer.html.j2 b/projects/tentacles/src/python/tentacles/templates/edit_printer.html.j2 new file mode 100644 index 0000000..1f3b8f1 --- /dev/null +++ b/projects/tentacles/src/python/tentacles/templates/edit_printer.html.j2 @@ -0,0 +1,36 @@ +{% extends "base.html.j2" %} +{% block content %} +

Edit printer

+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
+
+{% endblock %} diff --git a/projects/tentacles/src/python/tentacles/templates/index.html.j2 b/projects/tentacles/src/python/tentacles/templates/index.html.j2 index 392412e..fefaa1c 100644 --- a/projects/tentacles/src/python/tentacles/templates/index.html.j2 +++ b/projects/tentacles/src/python/tentacles/templates/index.html.j2 @@ -1,5 +1,9 @@ {% extends "base.html.j2" %} {% block content %} +
+ {% include "streams.html.j2" %} +
+
{% include "jobs_list.html.j2" %}
diff --git a/projects/tentacles/src/python/tentacles/templates/printers_list.html.j2 b/projects/tentacles/src/python/tentacles/templates/printers_list.html.j2 index 5eeaed0..ebf7206 100644 --- a/projects/tentacles/src/python/tentacles/templates/printers_list.html.j2 +++ b/projects/tentacles/src/python/tentacles/templates/printers_list.html.j2 @@ -1,30 +1,30 @@

Printers

{% with printers = ctx.db.list_printers() %} - {% if printers %} - {% for printer in printers %} - {% with id, name, url, _api_key, last_poll, status = printer %} -
-
- {{name}} - {{url}} - {{status}} - {{last_poll}} -
- {# FIXME: How should these action buttons work? #} -
- Test - Edit - Remove -
-
- {% endwith %} - {% endfor %} + {% if printers %} + {% for printer in printers %} +
+
+ {{printer.name}} + {{printer.url}} + {{printer.status}} + {{printer.last_poll_date}} +
+ {# FIXME: How should these action buttons work? #} +
{% if ctx.is_admin %} - Add a printer + Test + Edit + Remove {% endif %} - {% else %} - No printers available. {% if ctx.is_admin %}Configure one!{% else %}Ask the admin to configure one!{% endif %} - {% endif %} +
+
+ {% endfor %} + {% if ctx.is_admin %} + Add a printer + {% endif %} + {% else %} + No printers available. {% if ctx.is_admin %}Configure one!{% else %}Ask the admin to configure one!{% endif %} + {% endif %} {% endwith %}
diff --git a/projects/tentacles/src/python/tentacles/templates/streams.html.j2 b/projects/tentacles/src/python/tentacles/templates/streams.html.j2 new file mode 100644 index 0000000..16d8713 --- /dev/null +++ b/projects/tentacles/src/python/tentacles/templates/streams.html.j2 @@ -0,0 +1,12 @@ +{% import "macros.html.j2" as macros %} +

Webcams

+{% with printers = ctx.db.list_printers() %} +
+ {% for printer in printers if printer.stream_url %} +
+ + +
+ {% endfor %} +
+{% endwith %}