Progress Bar AJAX COLDFUSION JQUERY

I have a process that takes files from our main server and pushes them over to a different public server.

I am able to use queries to compare how many of the files have been moved.   Each time a loop is completed, the public server's table is updated by 1 record, so querying the public database at any point would show updated progress each time its checked, and the main server table has a fixed number of matching records that would be easy to figure out a percentage complete.

I have scoured google and experts exchange and found nothing but ways to make spinning wheels or animated gifs that do nothing to compare how far along a process is.  I am a beginner programmer and most familiar with coldfusion, and can make something like this work by constantly refreshing the page, manually or even with forced page redirects, but I know there must be a way to call the information and display it with a visual progress indicator without having to build in forced page refreshes.
weekapaugAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Julian HansenCommented:
The simplest way is nested a div with a background that is aligned by the % complete.

HTML
<div id="progress"><div class="background"></div><span></span></div>

Open in new window

CSS
<style type="text/css">
#progress {
  position: relative;
  width: 300px;
  border: 1px solid green;
  height: 20px;
  text-align: center;
  line-height: 20px;
  overflow: hidden;
  color: #333;
}
#progress .background {
  position: absolute;
  left: 100%;
  width: 300px;
  background-color: rgba(66,139, 202,0.5);
  height: 20px;
}
</style>

Open in new window

JavaScript / JQuery
<script>
var progress = -100;
var increment = 10;

setInterval(function() {
  progress += increment;
  if (progress > 0) {
    progress = -100;
  }
  console.log(progress);
  $('#progress .background').css({left: progress + '%'});
  $('#progress span').html(100 + progress + '%');
}, 1000);
</script>

Open in new window

Working sample here
weekapaugAuthor Commented:
Julian,

I understand the code somewhat but I'm not sure how to tie it to the process within the javascript.

What is occurring is a user on the website requests a particular ID# which then causes a script to run that fetches records, files, photos, from our main server into their user database.

So first we determine how many records there are on our side that match, then we use a loop to push the matching records one by one onto their end.  So after each file is done, their mysql database is updated along the way.

I have all the information regarding total records to send, how many were already sent, and how many left to go by doing simple queries at any point on the users database, but of course it will only show when its manually requested.  

How can I use this progress bar, or where do i need to insert the mysql queries that provide the information to the progress bar about where its at in the process on the fly?
_agx_Commented:
One possibility is create a CFC with 2 functions and call them via ajax.

1) First function starts the long process and updates progress via a session variable
2) Second function reads the progress in the session variable

A quick and dirty example mostly ripped from the jQuery docs:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>jQuery UI Progressbar - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
  <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
  <script src="//code.jquery.com/jquery-1.10.2.js"></script>
  <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>  
  <script type="text/javascript">
	// DEMO ONLY:  Check status every 1 second (too frequent for real app)
	var statusInterval = 1000;
	
	function startLongProcess() {
	    // start process
		$.ajax({
		   url: 'YourComponent.cfc?method=runLongProcess'
		   , dataType: 'plain'
		   });
		// start tracking progress   
		checkProgress();
	}
	
	function checkProgress() {
		$.ajax({
		   url: 'YourComponent.cfc?method=checkFileProgress&returnformat=json'
		   , dataType: 'json'
		   , success: function(response){
				$( "#progressbar" ).progressbar({
					value: response.PERCENTCOMPLETE
				});
				
				// if job is not yet complete, reschedule status check
				// otherwise, stop checking .. 
				if (response.PERCENTCOMPLETE < 100) {
					window.setTimeout(checkProgress, statusInterval);
				}
				else {
					window.clearTimeout();
				}
		   },
		   error: function(msg){
			   console.log(msg);
		   }
		});
	
	}
  </script>
</head>
<body>
 
<a href="javascript:startLongProcess()">Start Process</a> 
File Progress: <div id="progressbar"></div>
 
</body>
</html>

Open in new window


YourComponent.cfc:

<cfcomponent>
	<cffunction name="checkFileProgress" access="remote" returntype="struct">
	    <!--- check progress of process --->
		<cfset Local.result = { percentComplete = 0} >
		<cfif structKeyExists(SESSION, "processRunning")>
			<cfset Local.result.percentComplete = SESSION.percentComplete>
		</cfif>
		<cfreturn Local.result>
	</cffunction>

	<cffunction name="runLongProcess" access="remote" returntype="void">
		<!--- 
			DEMO ONLY: Simulate progress .... 
			Replace this with your file transfer process.  After
			each transfer, update progress value in session variable.
		--->
		
		<!--- initialize progress --->
		<cfset session.processRunning = true>
		<cfset session.percentComplete = 0>
		
		<cfloop from="1" to="100" index="Local.percentComplete">
			<!--- do some work here, then update percent complete --->
			<cfset session.percentComplete = Local.percentComplete>
			<!--- sleep for a bit to simulate real process --->
			<cfset sleep(500)>
		</cfloop>
		
		<!--- mark as completed --->
		<cfset session.processRunning = false>
	</cffunction>
</cfcomponent>

Open in new window

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

weekapaugAuthor Commented:
I will work on what i can understand of this and come back with more questions.

Its still way over my head I think, since I still dont know at which point in this code I would insert the mysql queries to check progress or if thats even necessary.  Also the amount it would be incremented would be dynamic since some people may have 25 records and others may have 180...

My logic was initially to query the total available records against the total records added to the users side.  When it successfully finished sending a file, its marked as done in the users mysql table as the last part of  loop.

So if I have a variable named MAINTOTAL=180 set with a mysql query record count against the main database,  and then use another mysql query to the users database and see USERTOTAL=18 then its USERDONEPERCENT=10 (percent).  And then loop until it hits 100 showing the progress as it works.

However there will many different variables regarding total available so I am also looking for ways to make the incremental value dynamic.  IE.  2 out of 20 is also 10%
Julian HansenCommented:
I would insert the mysql queries to check progress or if thats even necessary
Remember - mysql is server side and this runs in the client.
Without knowing that much about your application you probably are going to be looking at an AJAX call to the server on a timer to check status which then updates the progress bar % based on the returned value.
weekapaugAuthor Commented:
Right, I understand that.  Couldnt I make an html page which displays the results of a mysql query and use that response to disply to the user finished with 12 of 83 total records, etc.

This is what I am working on doing by way of writing an html page that queries the server, shows the result and feeds it back somehow?  

I was thinking even if i had a spinning wheel or animated gif that didnt update by progress just to make it simpler, but then let the text underneath update based on a query result written inside an html page and push away from the page when complete.
_agx_Commented:
I still dont know at which point in this code I would insert the mysql queries to check progress or if thats even necessary.  Also the amount it would be incremented would be dynamic since some people may have 25 records and others may have 180...

You could use queries, but the example above uses session variables instead.  Basically you fire off the file transfer process via an asynchronous (no wait) ajax request.  

      $.ajax({ url: 'YourComponent.cfc?method=runLongProcess'  .....

The request invokes a CF function that transfers the files AND updates the progress using session variables,  The example function ie "runLongProcess"  contains dummy code for demo purposes.  Replace the code inside it with your real code.  The only thing you need to do differently is update a SESSION variable each time you've completed transferring a file.  I don't know what your actual code looks like, but assume it contains some kind of loop.  Each time you loop, recalculate the percent complete dynamically and store it in session variable, ie 20, 30, 40....   100. In pseudo code:


       
<cffunction name="runLongProcess" access="remote" returntype="void">

             <!--- initialize progress to 0 before you start transferring files --->
              <cfset SESSSION.PercentComplete = 0>

               <!--- start transferring files ....--->
               <cfloop ....>

                       ... transfer a file and re-calculate percent complete

                       <!--- store percent complete in session variable ---->
                      <cfset SESSSION.PercentComplete = USERDONEPERCENT>
               </cfloop>

        </cffunction>

Open in new window


After firing off the transfer process, the main page fires off another ajax request to the checkFileProgress() function.  That CF function simply reads the value in the SESSION variable and returns it.  The JS function updates the progress bar and keeps calling itself until the progress reaches 100%.  

         $.ajax({ url: 'YourComponent.cfc?method=checkFileProgress&returnformat=json' ......

If you prefer, you could use queries instead of session variables.  Simply remove the session variables from the 1st function, and change the 2nd function to calculate the progress using queries instead:

<cffunction name="checkFileProgress" access="remote" returntype="struct">
	    <!--- 
                 run some queries to get 
                         A) the total # of files and 
                         B) total processed so far 
                 ... finally calculate the percent complete
                --->
	   <cfset Local.result = { percentComplete = qryTotalComplete / qryTotalFilesToProcess } >
           <cfreturn Local.result>
</cffunction>

Open in new window

weekapaugAuthor Commented:
Well I found code that works perfectly on IE and Foxfire, but for some reason chrome only works one time then when refreshed it will continue adding values to the previous result.

Heres what works though on the other 2 browsers just fine

<head> 
<script> 
var init = function() 
{ 
document.getElementById('cfpbLabel').style.display = 'block'; 
ColdFusion.ProgressBar.show('pBar'); 
ColdFusion.ProgressBar.start('pBar'); 
} 

function onFinish()
{
	alert('Done');
}

</script> 

</head> 

<cfform> 
<div id="cfpbLabel" style="display:none"> 
Saving File: 
</div> 
<cfprogressbar 
name="pBar" 
autodisplay=false 
bind="url:getHQMLS_progress2.cfm?record=#record#" 
onComplete="onFinish" 
width="400"	> 
<cfset ajaxOnLoad('init')> 
</cfform>

Open in new window


THE URL its bound to is a separate cfm file and has the following code.  This allows me to pass the variable of the record number to query how many total records are there.

<cfquery name="total_recs" datasource="maindata">
select totalrecs
from records
where record=#record#
</cfquery>

<cfset totalrecs=#total_recs.totalrecs#>
<cfset rec_prog=1/#totalrecs#>

<!--- use a count to indicate progress ---> 
<cfif not isdefined('session.count')> 
<cfset session.count = 1> 
<cfelse> 
<cfset session.count = session.count + 1 > 
</cfif> 
<!--- the struct to be sent back; using the populate the status and message components of the progressbar ---> 
<cfset data = {status=session.count * #rec_prog#,message=(round(session.count * #rec_prog# * 100)) & "% #session.count# of #totalrecs#"}> 
<!--- clear count from session to start afresh the next time the program is run ---> 
<cfif session.count eq #totalrecs#> 
<cfset structdelete(session,"count")>
<cfset structdelete(session,"status")>
</cfif> 
<!--- data sent back via URL binds must use SerializeJSON() ---> 
<cfoutput>#SerializeJSON(data)#</cfoutput>

Open in new window


To see how this works without having to have a record# in the url you could hard code a value for the totalrecs variable rather than querying for it and you should see that it works in 2 browsers fine, but does weird things in chrome.  It seems it refuses to let go of the previous value after the first try and keeps adding to it.
_agx_Commented:
Yeah, essentially that's exactly what the previous sample does, just with jquery instead of cfprogressbar.  Personally, I try and avoid the CF ajax stuff as it's old and kinda buggy sometimes.  

Glad you found something that works for you.
weekapaugAuthor Commented:
Well I figured it out for chrome.

The problem was that the session was not getting wiped because for some reason chrome does math differently and the session.count was never getting reset.

I changed the line on a wild guess referencing


<cfif session.count eq #totalrecs#>

to

 <cfif session.count gte #totalrecs#>

and it successfully wiped out the old session.  This didnt affect the other 2 browsers with identical code but on chrome it needed to be GTE (greater than or equal to) to run the session clearing code within the cfif statement

I would love to NOT use that cfprogress bar, and use the code you supplied but I am at a complete loss on how to structure it inside my website.   The loop and file transfer is happening remotely via a  <cfftp putfile> and I dont think I can nor do I need to monitor a remote process.

The first step of the entire page uses a cfhttp url to run a remote server page.  The first thing it does is queries for the total records then inserts a record into the local mysql table to tell how many incoming records to expect.

Once the looping has started remotely, at the end of each file transfer, it inserts the remote filename, to the local database table.  So the only thing I have to do to monitor the local progress is to query the local table for matching files for that record number and when the #totalrecs# variable matches the #getlocalrecs# variable its done.

I will gladly give all the points if you can help me figure out how to write this without using dummied down coldfusion markup.  The last example was still over my head since it referenced a loop and file transfers that I'm not monitoring directly.  Also, I had no idea where this line would go specifically...          

$.ajax({ url: 'YourComponent.cfc?method=checkFileProgress&returnformat=json' ......

I only need to compare the #totalrecs# variable to the #getlocalrecs# variable.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JSON

From novice to tech pro — start learning today.