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:
55
job.py
55
job.py
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user