Huge change, removed Status class and all "alert" messages are now shown as BS toast() and are via the DB and handled async in the F/E in jscript via Ajax. Fixed BUG-113 where toasts() were repeating. Removed many of the explicit alert messages (other than errors) and hooked {New|Finish}Job to consistently send messages to the F/E. Other messages (F/E without a job, like save settings) now use this model as well. Finally converted most of the older POST responses to formal json

This commit is contained in:
2023-01-11 13:50:05 +11:00
parent 2be2c504b2
commit a29cbb143c
15 changed files with 162 additions and 217 deletions

55
job.py
View File

@@ -5,7 +5,6 @@ from settings import Settings
from main import db, app, ma
from sqlalchemy import Sequence, func
from sqlalchemy.exc import SQLAlchemyError
from status import st, Status
from datetime import datetime, timedelta
import pytz
import socket
@@ -68,24 +67,22 @@ class Job(db.Model):
# DB. has to be about a specific job_id and is success/danger, etc. (alert)
# and a message
################################################################################
class PA_JobManager_Message(db.Model):
class PA_JobManager_Message(PA,db.Model):
__tablename__ = "pa_job_manager_fe_message"
id = db.Column(db.Integer, db.Sequence('pa_job_manager_fe_message_id_seq'), primary_key=True )
job_id = db.Column(db.Integer, db.ForeignKey('job.id') )
alert = db.Column(db.String)
level = db.Column(db.String)
message = db.Column(db.String)
persistent = db.Column(db.Boolean)
cant_close = db.Column(db.Boolean)
job = db.relationship ("Job" )
def __repr__(self):
return f"<id: {self.id}, job_id: {self.job_id}, alert: {self.alert}, message: {self.message}, job: {self.job}"
################################################################################
# GetJM_Message: used in html to display any message for this front-end
################################################################################
def GetJM_Message():
msg=PA_JobManager_Message.query.first()
msg=PA_JPA_JobManager_MessageobManager_Message.query.first()
return msg
################################################################################
@@ -111,20 +108,20 @@ def GetNumActiveJobs():
# should never really be needed, but was useful when developing / the job
# engine got 'stuck' when jobs were run in parallel
################################################################################
def WakePAJobManager():
def WakePAJobManager(job_id):
try:
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((PA_JOB_MANAGER_HOST, PA_JOB_MANAGER_PORT))
s.close()
except Exception as e:
st.SetMessage(f"Failed to connect to job manager, has it crashed? Exception was:{e}", "danger")
SetFELog( message=f"Failed to connect to job manager, has it crashed? Exception was:{e}", level="danger", persistent=True, job_id=job_id )
return
###############################################################################
# NewJob takes a name (which will be matched in pa_job_manager.py to run
# the appropriate job - which will update the Job() until complete
###############################################################################
def NewJob(name, num_files="0", wait_for=None, jex=None ):
def NewJob(name, num_files="0", wait_for=None, jex=None, desc="No description provided" ):
job=Job(start_time='now()', last_update='now()', name=name, state="New", num_files=num_files,
current_file_num=0, current_file='',
wait_for=wait_for, pa_job_state="New" )
@@ -134,8 +131,9 @@ def NewJob(name, num_files="0", wait_for=None, jex=None ):
db.session.add(job)
db.session.commit()
SetFELog( message=f'Created <a class="link-light" href="/job/{job.id}">Job #{job.id}</a> to {desc}', level="success" )
WakePAJobManager()
WakePAJobManager(job.id)
return job
################################################################################
@@ -161,6 +159,20 @@ def FinishJob(job, last_log, state="Completed", pa_job_state="Completed"):
if job.state=="Failed":
WithdrawDependantJobs( job, job.id, "dependant job failed" )
db.session.commit()
if state=="Completed" :
level="success"
elif state=="Withdrawn" :
level="warning"
SetFELog( message=last_log, level=level )
return
################################################################################
# This allows a log to be picked up in jscript on the FE
################################################################################
def SetFELog(message, level="success", job_id=None, persistent=False, cant_close=False):
m=PA_JobManager_Message( message=message, level=level, job_id=job_id, persistent=persistent, cant_close=cant_close)
db.session.add(m)
db.session.commit()
return
################################################################################
@@ -230,7 +242,7 @@ def joblog(id):
@app.route("/wakeup", methods=["GET"])
@login_required
def wakeup():
WakePAJobManager()
WakePAJobManager(job_id=None)
return redirect("/")
################################################################################
@@ -259,7 +271,7 @@ def stale_job(id):
db.engine.execute( f"delete from pa_job_manager_fe_message where job_id = {id}" )
db.session.commit()
WakePAJobManager()
WakePAJobManager(job.id)
return redirect("/jobs")
################################################################################
@@ -311,11 +323,12 @@ def joblog_search():
@login_required
def CheckForJobs():
num=GetNumActiveJobs()
print( f"called: /checkforjobs -- num={num}" )
sts=[]
for msg in PA_JobManager_Message.query.all():
u='<a class="link-light" href="' + url_for('joblog', id=msg.job_id) + '">Job #' + str(msg.job_id) + '</a>: '
sts.append( { 'message': u+msg.message, 'alert': msg.alert, 'job_id': msg.job_id, 'persistent': msg.persistent, 'cant_close': msg.cant_close } )
u=''
if 'Job #' not in msg.message and msg.job_id:
u='<a class="link-light" href="' + url_for('joblog', id=msg.job_id) + '">Job #' + str(msg.job_id) + '</a>: '
sts.append( { 'id': msg.id, 'message': u+msg.message, 'level': msg.level, 'job_id': msg.job_id, 'persistent': msg.persistent, 'cant_close': msg.cant_close } )
return make_response( jsonify( num_active_jobs=num, sts=sts ) )
###############################################################################
@@ -330,6 +343,18 @@ def ClearMessageForJob(id):
# no real need for this response, as if it succeeded/failed the F/E ignores it
return make_response( jsonify( status="success" ) )
###############################################################################
# / -> POST -> looks for pa_job_manager status to F/E jobs and sends json of
# them back to F/E (called form internal/js/jobs.js:CheckForJobs()
################################################################################
@app.route("/clearmsg/<id>", methods=["POST"])
@login_required
def ClearMessage(id):
PA_JobManager_Message.query.filter(PA_JobManager_Message.id==id).delete()
db.session.commit()
# no real need for this response, as if it succeeded/failed the F/E ignores it
return make_response( jsonify( id=id, status="success" ) )
###############################################################################
# This func creates a new filter in jinja2 to format the time from the db in a
# way that is more readable (converted to local tz too)