Stomping down the dot regressions

This commit is contained in:
Reid 'arrdem' McKenzie 2023-07-08 21:00:25 -06:00
parent c6ba7bde1f
commit 4faa5a4e06
12 changed files with 71 additions and 22 deletions

View file

@ -1,10 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path from pathlib import Path
from attrs import define import re
from typing import Optional, Tuple from typing import Optional, Tuple
import re from attrs import define
OPTION_PATTERN = re.compile("; (?P<key>[a-z0-9_]+) = (?P<value>.*?)\n") OPTION_PATTERN = re.compile("; (?P<key>[a-z0-9_]+) = (?P<value>.*?)\n")

View file

@ -12,9 +12,12 @@ from time import sleep
from types import GeneratorType, new_class from types import GeneratorType, new_class
from typing import Optional from typing import Optional
from aiosql.aiosql import _make_driver_adapter as get_adapter from aiosql.aiosql import (
from aiosql.query_loader import QueryLoader _make_driver_adapter as get_adapter,
)
from aiosql.queries import Queries from aiosql.queries import Queries
from aiosql.query_loader import QueryLoader
_sqlite = get_adapter("sqlite3") _sqlite = get_adapter("sqlite3")
_loader = QueryLoader(_sqlite, None) _loader = QueryLoader(_sqlite, None)
@ -211,6 +214,10 @@ class Db(Queries):
super().refresh_key(kid=kid, expiration=(datetime.now() + ttl).isoformat()) super().refresh_key(kid=kid, expiration=(datetime.now() + ttl).isoformat())
def start_job(self, *, jid: int):
super().start_job(jid=jid)
super().update_job_status(jid=jid, status="running")
def finish_job(self, *, jid: int, state: str, message: Optional[str] = None): def finish_job(self, *, jid: int, state: str, message: Optional[str] = None):
super().finish_job(jid=jid, state=state, message=message) super().finish_job(jid=jid, state=state, message=message)

View file

@ -79,6 +79,10 @@ SELECT
, fa.nozzle_diameter , fa.nozzle_diameter
, fa.filament_id , fa.filament_id
, (SELECT name FROM filament WHERE id = fa.filament_id) AS filament_name , (SELECT name FROM filament WHERE id = fa.filament_id) AS filament_name
, (SELECT name FROM job_statuses WHERE id = j.status_id) AS status
, j.started_at
, j.cancelled_at
, j.finished_at
FROM jobs j FROM jobs j
INNER JOIN files f INNER JOIN files f
ON j.file_id = f.id ON j.file_id = f.id
@ -86,6 +90,7 @@ LEFT JOIN file_analysis fa
ON fa.file_id = f.id ON fa.file_id = f.id
WHERE WHERE
finished_at IS NULL finished_at IS NULL
AND cancelled_at IS NULL
AND (:uid IS NULL OR j.user_id = :uid) AND (:uid IS NULL OR j.user_id = :uid)
AND f.id IS NOT NULL AND f.id IS NOT NULL
; ;
@ -103,12 +108,17 @@ LIMIT 1
-- name: list-job-history -- name: list-job-history
SELECT SELECT
* j.*
, fa.id as analysis_id
, (SELECT name FROM job_statuses WHERE id = j.status_id) AS `status` , (SELECT name FROM job_statuses WHERE id = j.status_id) AS `status`
FROM jobs j FROM jobs j
INNER JOIN files f
ON j.file_id = f.id
LEFT JOIN file_analysis fa
ON fa.file_id = f.id
WHERE WHERE
finished_at IS NOT NULL finished_at IS NOT NULL
AND (:uid IS NULL OR user_id = :uid) AND (:uid IS NULL OR j.user_id = :uid)
ORDER BY ORDER BY
datetime(finished_at) DESC datetime(finished_at) DESC
LIMIT 25 LIMIT 25
@ -201,3 +211,10 @@ WHERE
user_id = :uid user_id = :uid
AND file_id = :fid AND file_id = :fid
; ;
-- name: update-job-status!
UPDATE jobs
SET status_id = (SELECT id FROM job_statuses WHERE name = :status)
WHERE
id = :jid
;

View file

@ -54,6 +54,10 @@ label {
margin-bottom: 20px; margin-bottom: 20px;
} }
.mr-auto {
margin-right: auto;
}
.u-flex1 { .u-flex1 {
flex: 1; flex: 1;
} }

View file

@ -22,6 +22,10 @@
background-color: $secondary_green; background-color: $secondary_green;
} }
.dot.analyzing {
background-color: $black;
}
.dot.queued { .dot.queued {
background-color: $secondary_blue; background-color: $secondary_blue;
} }
@ -43,7 +47,7 @@
background-color: $red; background-color: $red;
} }
.dot--basic { .dot--active {
animation: blink 2s infinite; animation: blink 2s infinite;
} }

View file

@ -6,6 +6,8 @@
<div class="file row u-flex"> <div class="file row u-flex">
<div class="details six columns u-flex u-flex-wrap"> <div class="details six columns u-flex u-flex-wrap">
<span class="file-name">{{ file.filename }}</span> <span class="file-name">{{ file.filename }}</span>
</div>
<div class="six columns u-flex u-flex-wrap">
<span class="file-sucesses"><label>Successes</label>{{ file.print_successes }}</span> <span class="file-sucesses"><label>Successes</label>{{ file.print_successes }}</span>
<span class="file-failures"><label>Failures</label>{{ file.print_failures }}</span> <span class="file-failures"><label>Failures</label>{{ file.print_failures }}</span>
</div> </div>

View file

@ -5,7 +5,7 @@
{% for job in jobs %} {% for job in jobs %}
<div class="job row u-flex"> <div class="job row u-flex">
<div class="details six columns u-flex u-flex-wrap"> <div class="details six columns u-flex u-flex-wrap">
<div class="job-filename u-flex"> <div class="job-filename u-flex mr-auto">
<label for="filename">File</label> <label for="filename">File</label>
<span name="filename">{{ctx.db.fetch_file(ctx.uid, job.file_id).filename or "it's a secret"}}</span> <span name="filename">{{ctx.db.fetch_file(ctx.uid, job.file_id).filename or "it's a secret"}}</span>
</div> </div>
@ -25,7 +25,7 @@
<div class="two columns"> <div class="two columns">
<div class="job-status u-flex"> <div class="job-status u-flex">
<label for="state">Status</label> <label for="state">Status</label>
<div class="dot {{ macros.job_state(job) }} tooltip bottom" data-text="{{ macros.job_state(job) }}" style="--dot-size: 1em;"> </div> <div class="dot {{ macros.job_state(job) }} {{ macros.job_active(job) }} tooltip bottom" data-text="{{ macros.job_state(job) }}" style="--dot-size: 1em;"> </div>
</div> </div>
</div> </div>
<div class="controls four columns u-flex u-ml-auto"> <div class="controls four columns u-flex u-ml-auto">

View file

@ -18,7 +18,7 @@
{% if job.started_at %} {% if job.started_at %}
<div class="job-runtime u-flex"> <div class="job-runtime u-flex">
<label for="runtime">Runtime</label> <label for="runtime">Runtime</label>
<span name="Runtime">{{ (datetime.now() - datetime.fromisoformat(job.started_at)) }}</span> <span name="Runtime">{{ (datetime.utcnow() - datetime.fromisoformat(job.started_at)) }}</span>
</div> </div>
{% else %} {% else %}
<div class="job-constraint u-flex"> <div class="job-constraint u-flex">
@ -34,7 +34,7 @@
<div class="two columns"> <div class="two columns">
<div class="job-status u-flex"> <div class="job-status u-flex">
<label for="state">Status</label> <label for="state">Status</label>
<div class="dot {{ macros.job_state(job) }} tooltip bottom" data-text="{{ macros.job_state(job) }}" style="--dot-size: 1em;"> </div> <div class="dot {{ macros.job_state(job) }} {{ macros.job_active(job) }} tooltip bottom" data-text="{{ macros.job_state(job) }}" style="--dot-size: 1em;"> </div>
</div> </div>
</div> </div>
<div class="controls u-flex u-ml-auto"> <div class="controls u-flex u-ml-auto">

View file

@ -33,10 +33,19 @@
{% endmacro %} {% endmacro %}
{% macro job_state(job) %} {% macro job_state(job) %}
{{ 'queued' if (not job.finished_at and not job.printer_id and not job.cancelled_at) else {{ job.status if job.finished_at else
'running' if (not job.finished_at and job.printer_id and not job.cancelled_at) else 'cancelling' if job.cancelled_at else
'cancelling' if (not job.finished_at and job.cancelled_at) else 'analyzing' if not job.analysis_id else
job.status }} 'queued' if (not job.printer_id and not job.started_at) else
'running' if job.printer_id else
job.status
}}
{% endmacro %}
{% macro job_active(job) %}
{{
'dot--active' if not job.finished_at else ''
}}
{% endmacro %} {% endmacro %}
{# #################################################################################################### #} {# #################################################################################################### #}

View file

@ -7,10 +7,11 @@ Supporting the core app with asynchronous maintenance tasks.
Mostly related to monitoring and managing Printer state. Mostly related to monitoring and managing Printer state.
""" """
import os
from contextlib import closing from contextlib import closing
from datetime import datetime, timedelta
from functools import cache from functools import cache
import logging import logging
import os
from pathlib import Path from pathlib import Path
from pprint import pformat from pprint import pformat
from typing import Callable from typing import Callable
@ -18,9 +19,8 @@ from urllib import parse as urlparse
from cherrypy.process.plugins import Monitor from cherrypy.process.plugins import Monitor
from fastmail import FastMailSMTP from fastmail import FastMailSMTP
from flask import Flask as App, render_template
from gcode import analyze_gcode_file from gcode import analyze_gcode_file
from flask import Flask as App
from flask import render_template
from octorest import OctoRest as _OR from octorest import OctoRest as _OR
from requests import Response from requests import Response
from requests.exceptions import ( from requests.exceptions import (
@ -256,8 +256,12 @@ def pull_jobs(app: App, db: Db) -> None:
printer_state = {"disconnected": True, "error": True} printer_state = {"disconnected": True, "error": True}
if job_state.get("progress", {}).get("completion", 0.0) == 100.0: if job_state.get("progress", {}).get("completion", 0.0) == 100.0:
log.info(f"Job {job.id} has succeeded") # Debounce jobs which JUST started from immediately completing due to a data race
db.finish_job(jid=job.id, state="success") start_date = datetime.fromisoformat(job.started_at)
runtime = datetime.utcnow() - start_date
if runtime >= timedelta(seconds=15): # 3 polling cycles
log.info(f"Job {job.id} has succeeded")
db.finish_job(jid=job.id, state="success")
elif printer_state.get("error"): elif printer_state.get("error"):
log.warn(f"Job {job.id} has failed") log.warn(f"Job {job.id} has failed")

View file

@ -6,6 +6,7 @@ import logging
import pytest import pytest
from tentacles.db import Db from tentacles.db import Db
# FIXME: Should this be an autouse fixture? Maybe. Doesn't buy much tho. # FIXME: Should this be an autouse fixture? Maybe. Doesn't buy much tho.
logging.addLevelName(logging.DEBUG - 5, "TRACE") logging.addLevelName(logging.DEBUG - 5, "TRACE")
logging.TRACE = logging.DEBUG - 5 logging.TRACE = logging.DEBUG - 5

View file

@ -3,10 +3,10 @@
import re import re
from gcode import ( from gcode import (
parse_prusa_config_str,
OPTION_PATTERN,
analyze_gcode_str, analyze_gcode_str,
GcodeAnalysis, GcodeAnalysis,
OPTION_PATTERN,
parse_prusa_config_str,
) )