From 1ba9bf4312da9b66c4af845ace4447e87cddb65f Mon Sep 17 00:00:00 2001 From: Damien De Paoli Date: Fri, 6 Jan 2023 17:37:15 +1100 Subject: [PATCH] renamed toast.js to jobs.js and moved Job related calls to jobs.py form files.py AND get job.py to allow job_mgr msgs to go to F/E via a POST of /checkforjobs (picked up in templates/base.html). move files also calls new CheckForJobs() to pick up when move job finishes without needing a page reload --- TODO | 21 ++++--- files.py | 32 ----------- internal/js/files_support.js | 2 +- internal/js/jobs.js | 103 +++++++++++++++++++++++++++++++++++ internal/js/toast.js | 40 -------------- job.py | 52 +++++++++++++++++- main.py | 4 +- templates/base.html | 21 +++---- 8 files changed, 180 insertions(+), 95 deletions(-) create mode 100644 internal/js/jobs.js delete mode 100644 internal/js/toast.js diff --git a/TODO b/TODO index be341d3..d5bb0bd 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,18 @@ ### GENERAL + * get all status messages to use toasts AND get func to also increase/descrease the job counter as appropriate) + - [DONE] all (success/creation) status messages use toasts + -- any non-sucess still stays a top of the page as an alert (for now?) + -- [DONE] make a helper func for setting toast body and use it in base.html && file_support.js + -- [DONE] make a helper func for setting 'Active Jobs' text/badge and call it when document ready (rather/both than start of base.html) + -- [DONE] trigger a timeout to play back pa_job_mgr message to FE and reset 'Active Jobs' text/badge until it hits 0 + -- [DONE] (re)start this code if any new jobs is created (on move only for now - in the long run all job creation should consider this POST/json model) + -- [TODO] fix base.html to only call toast() and only in document.ready() + -- [TODO] this means handling danger, and persistent / no time-out toast()s + -- [DONE] warning works now (colors, etc.) + -- [TODO] have not thought about danger at all (the StatusMsg() code should work) - but missing Clear*() and persistence + * [TODO] delete files should behave like /move_files (stay on same page, job start with toast(), etc.) + * [TODO] ALL job creation should follow above pattern + * change the rotation code to use that jpeg util to reduce/remove compression loss? * ignore face should ignore ALL matching faces (re: Declan) @@ -13,13 +27,6 @@ response.headers["Content-Type"] = "application/json" return response - * get all status messages to use toasts AND get func to also increase/descrease the job counter as appropriate) - - [DONE] all (success/creation) status messages use toasts - -- any non-sucess still stays a top of the page as an alert (always?) - -- [DONE] make a helper func for setting toast body and use it in base.html && file_support.js - -- [TODO] make a helper func for setting 'Active Jobs' text/badge and call it when document ready (rather/both than start of base.html) - -- should allow all JM Messages, or other messages to be an active Ajax poll until a job has completed - * should allow context menu from View thumbs (particularly useful on search) to show other files around this one by date (maybe that folder or something?) * could get better AI optim, by keeping track of just new files since scan (even if we did this from the DB), diff --git a/files.py b/files.py index 70e9774..27b6c51 100644 --- a/files.py +++ b/files.py @@ -141,38 +141,6 @@ class FileType(db.Model): def __repr__(self): return f"" -################################################################################ -# Class describing PA_JobManager_Message and in the DB (via sqlalchemy) -# the job manager can send a message back to the front end (this code) via the -# DB. has to be about a specific job_id and is success/danger, etc. (alert) -# and a message -################################################################################ -class PA_JobManager_Message(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) - message = db.Column(db.String) - job = db.relationship ("Job" ) - def __repr__(self): - return f"Job #" + data.job_id + " to move selected file(s)"; st.alert="success"; StatusMsg(st); return false; } }) + $.ajax({ type: 'POST', data: $('#mv_fm').serialize(), url: '/move_files', success: function(data){ st=Object; st.message="Created Job #" + data.job_id + " to move selected file(s)"; st.alert="success"; StatusMsg(st); CheckForJobs(); return false; } }) } // show the DBox for a move file, includes all thumbnails of selected files to move diff --git a/internal/js/jobs.js b/internal/js/jobs.js new file mode 100644 index 0000000..61f3fec --- /dev/null +++ b/internal/js/jobs.js @@ -0,0 +1,103 @@ +// create a bs toast in the status_container +// can reuse any that are hidden, OR, create a new one by appending as needed (so we can have 2+ toasts on screen) +function StatusMsg(st) +{ + console.log( 'StatusMsg() -> ' + st.alert + ': ' + st.message ) + // look for any '.hide' and '.toast' + if( $('.toast.hide').length !== 0 ) + { + // as there may be more than 1 and as bs toast deals with ordering on screen, so just grab first() + tid=$('.toast.hide').first().attr('id') + // reset body, and the text-* and bg-* class for success, danger, etc. + $('#'+tid ).find( '.toast-body').html(st.message) + $('#'+tid ).removeClass( function( index, className ) { return (className.match( /(^|\s)(bg-|text-)\S+/g) || []).join(' ') } ) + $('#'+tid ).addClass( 'bg-' + st.alert ) + // get rid of white on button (if its there) + $('#'+tid ).find( 'button' ).removeClass('btn-close-white') + if( st.alert == "success" || st.alert == "danger" ) + { + $('#'+tid ).addClass( 'text-white' ) + $('#'+tid ).find( 'button' ).addClass('btn-close-white') + } + // show the popup (by default it fades) + $('#'+tid ).toast("show") + } + else + { + // find the id of the 'last' toast (either there are none, or they are all visible [note: we are in the else already]) + tmp=$('.toast').last().attr('id') + // if none, there are no toasts at all, so make '#1' + if( tmp== '' ) + tid=1 + else + { + // skip 'st' at front of DOM id, and then increment to get id for new one + tid=tmp.substr(2) + tid++ + } + // make new div, include st.alert as background colour, and st.message as toast body + div=' + ` + // can be appended straight after st1 as the .toast("show") deals with display ordering + $('#st1').append(div) + // show the popup (by default it fades) + $('#st' + tid ).toast("show") + } +} + +// this will make the active jobs badge red with a > 0 value, or navbar colours +// if 0 (effectively hiding it, but leaving the same width) +function SetActiveJobsBadge(num_jobs) +{ + if( num_jobs > 0 ) + $('#num_active_jobs').removeClass("invisible") + else + $('#num_active_jobs').addClass("invisible") + $('#num_active_jobs').html(num_jobs) +} + +// this function is called either when we load and PA page and active jobs > 0 +// OR, when a new job is created in the F/E and therefore active jobs > 0 +// it keeps looking to see if the pa_job_manager has sent a response (via the DB) +// if so, it handles the response(s) with toast()s and updates the active job +// badge in the navbar. If all jobs are complete it stops calling itself +// after a 1 second timeout +function CheckForJobs() +{ + $.ajax( + { + type: 'POST', url: '/checkforjobs', + success: function(data) { + data.sts.forEach( + function(el) + { + StatusMsg(el) + } + ) + SetActiveJobsBadge(data.num_active_jobs) + if( data.num_active_jobs > 0 ) + { + setTimeout( function() { CheckForJobCompletion() }, 1000 ); + } + }, + } ) + return false; +} diff --git a/internal/js/toast.js b/internal/js/toast.js deleted file mode 100644 index b1ec308..0000000 --- a/internal/js/toast.js +++ /dev/null @@ -1,40 +0,0 @@ -// create a bs toast in the status_container -function StatusMsg(st) -{ - // look for first '.hide' in #status_container (there may be more than 1) - if( $('.toast.hide').length !== 0 ) - { - tid=$('.toast.hide').first().attr('id') - $('#'+tid ).find( '.toast-body').html(st.message) - $('#'+tid ).removeClass( function( index, className ) { return (className.match( /(^|\s)bg-\S+/g) || []).join(' ') } ) - $('#'+tid ).addClass( 'bg-' + st.alert ) - $('#'+tid ).toast("show") - } - else - { - tmp=$('.toast').last().attr('id') - if( tmp== '' ) - tid=1 - else - { - // skip 'st' - tid=tmp.substr(2) - tid++ - } - div=' - ` - // can be appended straight after st1 as the .toast("show") deals with display ordering - $('#st1').append(div) - $('#st' + tid ).toast("show") - } -} diff --git a/job.py b/job.py index 98b92e2..a59e9ae 100644 --- a/job.py +++ b/job.py @@ -1,16 +1,15 @@ from wtforms import SubmitField, StringField, FloatField, HiddenField, validators, Form from flask_wtf import FlaskForm -from flask import request, render_template, redirect +from flask import request, render_template, redirect, make_response, jsonify, url_for 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 -from flask_login import login_required, current_user import pytz import socket -from shared import PA_JOB_MANAGER_HOST, PA_JOB_MANAGER_PORT, NEWEST_LOG_LIMIT, OLDEST_LOG_LIMIT +from shared import PA, PA_JOB_MANAGER_HOST, PA_JOB_MANAGER_PORT, NEWEST_LOG_LIMIT, OLDEST_LOG_LIMIT from flask_login import login_required, current_user from sqlalchemy.dialects.postgresql import INTERVAL from sqlalchemy.sql.functions import concat @@ -63,6 +62,38 @@ class Job(db.Model): def __repr__(self): return "".format(self.id, self.start_time, self.last_update, self.name, self.state, self.num_files, self.current_file_num, self.current_file, self.pa_job_state, self.wait_for, self.extra, self.logs) +################################################################################ +# Class describing PA_JobManager_Message and in the DB (via sqlalchemy) +# the job manager can send a message back to the front end (this code) via the +# DB. has to be about a specific job_id and is success/danger, etc. (alert) +# and a message +################################################################################ +class PA_JobManager_Message(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) + message = db.Column(db.String) + job = db.relationship ("Job" ) + def __repr__(self): + return f" 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("/checkforjobs", methods=["POST"]) +@login_required +def CheckForJobs(): + num=GetNumActiveJobs() + sts=[] + print("CheckForJobs called" ) + for msg in PA_JobManager_Message.query.all(): + print("there is a PA_J_MGR status message" ) + u='Job # ' + str(msg.job_id) + ': ' + sts.append( { 'message': u+msg.message, 'alert': msg.alert } ) + return make_response( jsonify( num_active_jobs=num, sts=sts ) ) ############################################################################### # This func creates a new filter in jinja2 to format the time from the db in a diff --git a/main.py b/main.py index 425ea76..5cceddf 100644 --- a/main.py +++ b/main.py @@ -57,9 +57,9 @@ Compress(app) ################################# Now, import separated class files ################################### from ai import aistats -from files import Entry, GetJM_Message, ClearJM_Message +from files import Entry from person import Person -from job import Job, GetNumActiveJobs +from job import Job, GetNumActiveJobs, GetJM_Message, ClearJM_Message from settings import Settings from user import PAUser diff --git a/templates/base.html b/templates/base.html index 4ce23d7..f79a317 100644 --- a/templates/base.html +++ b/templates/base.html @@ -17,7 +17,7 @@ - + @@ -106,12 +106,7 @@ @@ -136,10 +131,11 @@ {% if GetJM_Message() != None %} {% set msg=GetJM_Message() %} + {% if request.endpoint != "fix_dups" and request.endpoint != "rm_dups" and request.endpoint != "stale_jobs" %} {% if msg.alert != "success" %}
- {% if msg.alert != "success" and msg.job.name != "checkdups" %} + {% if msg.job.name != "checkdups" %}