clean up how we create toasts() [no longer try to reuse dom elements, just add new ones each time], support persistent notifications and close button or not [via separate booleans], created a clear message route and use that now in templates/base.html to clear FE messages. This will break for check dups as I am not setting persistence / close buttons correctly for those jobs, that is next. Converted move_files to new format

This commit is contained in:
2023-01-09 13:13:51 +11:00
parent 1ba9bf4312
commit 56c2d586b6
6 changed files with 89 additions and 91 deletions

34
TODO
View File

@@ -1,32 +1,32 @@
### 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)
-- [DONE] (re)start this code if any new jobs is created (on move ONLY FOR NOW
-- [DONE] make js StatusMsg() take a 'data' (json response) and process that
-- [DONE] make it include message from the API endpoint doing the work, not in the js code...
-- [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] 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
-- [DONE] clean up has to be a POST back to server to clear DB (jscript/async has no other way)
-- [TODO] go through the crazy persistent status msgs for checkdups, etc. and make sure they all work
* should be using jsonify to return real json to my API calls, e.g:
use make_response( jsonify (... ) )
-- [TODO] all POSTs should follow new status behaviour (unless its a form POST, as that immediately renders the form in flask and will show the Status)
all GETs stay as is for now (as they expect a html reply, not data, and then html is base.html+ and it handles the status)
-- ONLY time this will fail is multiple status messages (which can occur I believe if a Wake of job mgr and then a job is created in DB, the success will only be shown)
-- [TODO] simple fix will be to get 'hidden' jobs (wake job_mgr and maybe? metadata) to post failure events to the JobMgr_FE DB queue
-- [TODO] when ALL converted, replace 'alert="..."' with 'status="..."'
* delete files should behave like /move_files (stay on same page) as well as the status messages above
* change the rotation code to use that jpeg util to reduce/remove compression loss?
* ignore face should ignore ALL matching faces (re: Declan)
* should be using jsonify to return real json to my API calls, e.g:
response = make_response(
jsonify(
{"message": str(FLAMSG_ERR_SEC_ACCESS_DENIED), "severity": "danger"}
),
401,
)
response.headers["Content-Type"] = "application/json"
return response
* 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),
@@ -39,6 +39,8 @@
--> OR? https://jsmpeg.com/
--> OR? convert all videos to mp4/webm
* consider whether FE_MSG should exist without a job #, and/or a sep. table, etc.
* delete folder
* allow joblog search (less needed with logs visible on a given file now)

View File

@@ -590,7 +590,10 @@ def move_files():
for el in request.form:
jex.append( JobExtra( name=f"{el}", value=request.form[el] ) )
job=NewJob( "move_files", 0, None, jex )
return make_response( jsonify( job_id=job.id, status="success" ) )
return make_response( jsonify(
job_id=job.id,
message=f"Created&nbsp;<a class='link-light' href=/job/{job.id}>Job #{job.id}</a>&nbsp;to move selected file(s)",
status="success", alert="success" ) )
@login_required
@app.route("/viewlist", methods=["POST"])

View File

@@ -64,7 +64,7 @@ function MoveSubmit()
// reorder the images via ecnt again, so highlighting can work
document.mf_id=0; $('.figure').each( function() { $(this).attr('ecnt', document.mf_id ); document.mf_id++ } )
$('#dbox').modal('hide')
$.ajax({ type: 'POST', data: $('#mv_fm').serialize(), url: '/move_files', success: function(data){ st=Object; st.message="Created&nbsp;<a class='link-light' href=/job/" + data.job_id + ">Job #" + data.job_id + "</a>&nbsp;to move selected file(s)"; st.alert="success"; StatusMsg(st); CheckForJobs(); return false; } })
$.ajax({ type: 'POST', data: $('#mv_fm').serialize(), url: '/move_files', success: function(data){ console.log(data); StatusMsg(data); CheckForJobs(); return false; } })
}
// show the DBox for a move file, includes all thumbnails of selected files to move

View File

@@ -1,66 +1,56 @@
// global
var next_toast_id=1
function NewToast(data)
{
// make new div, include data.alert as background colour, and data.message as toast body
d_id='st' + String(next_toast_id)
div='<div id="' + d_id + '"'
if( data.persistent === true )
div+=' data-bs-autohide="false"'
div +=' class="toast hide align-items-center border-0'
if( data.alert == "success" || data.alert == "danger" )
div += ' text-white'
div += ' bg-' + data.alert
div += `" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
`
div += data.message
div += ' </div>'
if( data.cant_close !== true )
{
div += ' <button type="button" class="btn-close me-2 m-auto'
if( data.alert === "success" || data.alert === "danger" )
div += ' btn-close-white'
div += ' " data-bs-dismiss="toast" aria-label="Close"></button>'
}
div += `
</div>
</div>
`
// insert this as the first element in the status_container
$('#status_container').prepend(div)
// make sure we have a new id for next toast
next_toast_id++
return d_id
}
// 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 )
el=NewToast(st)
$('#' + el ).toast("show")
// if there is a job_id, then clear the message for it or it will be picked up again on reload
// BUT, we dont want to do this immediately, should hook on close, but for
// now, we will do this to get a first pass working
if( st.job_id !== undefined )
{
// 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")
$.ajax( { type: 'POST', url: '/clearmsgforjob/'+st.job_id, success: function(data) { } } )
}
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='<div id="st' + tid + '" class="toast hide align-items-center border-0'
if( st.alert == "success" || st.alert == "danger" )
div += ' text-white'
div += ' bg-' + st.alert
div += `" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
`
div += st.message
div += `
</div>
<button type="button" class="btn-close me-2 m-auto
`
if( st.alert == "success" || st.alert == "danger" )
div =+ ' btn-close-white'
div += `
" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</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
@@ -95,7 +85,7 @@ function CheckForJobs()
SetActiveJobsBadge(data.num_active_jobs)
if( data.num_active_jobs > 0 )
{
setTimeout( function() { CheckForJobCompletion() }, 1000 );
setTimeout( function() { CheckForJobs() }, 1000 );
}
},
} )

15
job.py
View File

@@ -90,7 +90,6 @@ def GetJM_Message():
# ClearJM_Message: used in html to clear any message just displayed
################################################################################
def ClearJM_Message(id):
print("ClearJM_Message called")
PA_JobManager_Message.query.filter(PA_JobManager_Message.id==id).delete()
db.session.commit()
return
@@ -313,9 +312,21 @@ def CheckForJobs():
for msg in PA_JobManager_Message.query.all():
print("there is a PA_J_MGR status message" )
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 } )
sts.append( { 'message': u+msg.message, 'alert': msg.alert, 'job_id': msg.job_id } )
return make_response( jsonify( num_active_jobs=num, sts=sts ) )
###############################################################################
# / -> 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("/clearmsgforjob/<id>", methods=["POST"])
@login_required
def ClearMessageForJob(id):
PA_JobManager_Message.query.filter(PA_JobManager_Message.job_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( 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)

View File

@@ -161,7 +161,7 @@
{% if not InDBox %}
{%block script_content %}{% endblock script_content %}
<div id="status_container" class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
<div id="status_container" class="position-fixed top-1 end-0 p-1" style="z-index: 11">
<div id="st1" class="toast hide align-items-center text-white bg-success border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
@@ -172,24 +172,16 @@
</div>
<script>
$(document).ready(function() {
<!-- f/e messages are resident in memory of the page being rendered, just process now -->
<!-- f/e messages are resident in memory of the page being rendered, just process now (stay this way so faster/dont need DB for some message) -->
{% if GetMessage()|length %}
msg = "{{ GetMessage()|safe }}"
msg=msg.replace('href=', 'class=link-light href=')
st=Object; st.message=msg; st.alert='{{GetAlert()}}'; StatusMsg(st)
alert( '{{GetAlert()}}' )
<!-- call ClearStatus: strictly not needed as we are near finished rendering, and any new page will lose it from memory (better to be explicit) -->
{{ ClearStatus() }}
{% endif %}
CheckForJobs()
/*
<!-- this this is totally useless as can only live for this 1 page render anyway -->
{{ ClearStatus() }}
{% if GetJM_Message() != None and GetJM_Message().alert == "success" %}
msg="{{GetJM_Message().message}}"
msg=msg.replace('href=', 'class=link-light href=')
st=Object; st.message=msg; st.alert="success"; StatusMsg(st)
StatusMsg(st)
{% set dont_print=ClearJM_Message(GetJM_Message().id) %}
{% endif %}
*/
} )
</script>
</body>