Progress bar / text info během scriptu v Nette

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
MW
Člen | 626
+
0
-

Zdravím a prosím o pomoc..

sice jsem nějaká nakousnutá témata nalezl, ale řešení bohužel ne.
Jednoduše potřebuji nějaký progres či informaci zobrazovat během cyklu ve scriptu.

naivně jsem zkusil

for ($i=1; $i<10; $i++) {
            $this->flashMessage('bla'.$i);
            $this->invalidateControl('flash');
            sleep(1);
        }

Což ve výsledku vyplivne až na konci.. logicky…
Při pokusech s ob_flush a flus úspěšný nejsem..

Poradil by někdo, jak to udělat, prosím?

Editoval MW (17. 6. 2015 13:25)

iguana007
Člen | 970
+
+1
-

Tímto způsobem, jak si již napsal, to „logicky“ fungovat nemůže, protože z pohledu životního cyklu aplikace se jedna o jeden request/životní cyklus, kde je pouze jeden request a jedna response.

Aby si nějakou takovou informaci mohl v prohlížeči vykreslovat, tak bys ten složitý/dlouho běžící loop musel spustit někde na pozadí (třeba CRONem) a někam si ukládat mezistavy v jednotlivých cyklech.
V prohlížeči si pak volal ajaxem nějaký handle, který bude vracet číselný status dané operace a ty jej jen vykreslíš.

Složitější varianta by byla s využitím nějakého AJAX push engine, který by ti ten progres v browseru updatoval v browseru sám (odpadlo by ti to AJAX volání handle v intervalech – funguje to obdobně, jako push notifikace na iOS a Androidu) – jeden z nejznámějších je třeba: http://ape-project.org/ – ale jsou jich mraky jiných a je na tobě, který by sis vybral.

David Matějka
Moderator | 6445
+
0
-

Behem jednoho http pozadavku to bude sranda :)

slo by vyuzit ProgressEvent, ukazka pouziti: http://www.dave-bond.com/…gress-HMTL5/ Z php scriptu pak budes muset posilat treba po jednom znaku, coz bude signalizovat ten progres. Je tam ale nekolik problemu – musis zajistit, aby php ihned odesilalo na vystup, takze vypnout ruzny output_buffering, gzip a nejen pro php, ale i pro webserver. A pak bude problem, pokud budes chtit posilat dalsi response, jelikoz uz pred tim budes mit bordel atd, atd.

Ale zkusil jsem to a funguje to! :)

Radeji najdi nejaky jiny reseni, treba ze ten ukol predas ke zpracovani nejakemu rabbitmq a budes se periodicky dotazovat na stav.


Mala ukazka:

	public function handleProgress()
	{
		for ($i = 0; $i < 100; $i++) {
			echo '.';
			ob_flush();
			flush();
			usleep(20000);
		}
		exit;
	}
	<div id="progress"></div>
	<script>
		$(function () {
			$.ajax({
				type: 'GET',
				url: "/?do=progress",
				data: { },
				xhr: function () {
					var xhr = $.ajaxSettings.xhr();
					xhr.addEventListener("progress", function (evt) {
						/* nepouzivam lengthComputable, jelikoz nepouzivam Content-length hlavicku */
						$("#progress").html(Math.min(100, evt.loaded));
					}, false);
					return xhr;
				},
				success: function (data) {
					$("#progress").html("hotovo");
				}
			});
		});
	</script>

a v nginxu jsem musel nastavit:

gzip    off;
fastcgi_keep_conn on;

:P

Freema
Člen | 18
+
0
-

Já to dělám tak že v requestu ve kterým mi běží nějaký dlouhý cyklus nechávám cachovat soubor nebo do redisu jeho progress. Druhy ajax request který běží souběžně s tím prvním mi jen načítá ty data z cache v nějakým intervalu a tím pak vypočítavá délku progress barů. Je to trochu hodně psaního, ale docela to funguje lepší než ten flush() který mi přijde že není moc spolehlivý.

MW
Člen | 626
+
0
-

Dekuji !
No není to tak trivi, jak jsem si myslel…
Ale v zasade ukladani v cyklu nekam a dotazovat se JS by asi mohla byt ta cesta.

třeba do souboru? Myslite, ze by to slo?

To u ceho to potrebuju je proste import asi mil. vet do db a tak ze bych to ukladal nekam každý Xty cyklus do db nebo souboru a JS ho v cyklech cetl?

Existuje na tohle vůbec nejaky „cisty“ reseni? :)

Diky!

Michal Vyšinský
Člen | 608
+
0
-

Použil bych sockety a z PHP procesu pushoval do klienta informace o průběhu.

MW
Člen | 626
+
0
-

@DavidMatějka
Tak jak to je to prolitne a nic to neukaze jen HOTOVO.. ale testuju to na WAMPu… nevim jestli tam nemůže byt něco spatne …

Editoval MW (18. 6. 2015 15:43)

David Matějka
Moderator | 6445
+
0
-

@MW jak pisu, je potreba nastavit X veci, aby nedochazelo k zadnymu bufferovani ani komprimovani. nevim, co vse bude potreba u apache, hledej.. Ale tohle jsem psal spis jako zajimavost, lepsi bude pouzit nekterou z jinych uvedenych moznosti

MW
Člen | 626
+
0
-

Trochu si s tim hraju, tak jsem si řekl, ze udelam něco trivi přes session.. zda se, ze me to funguje jen problém je v tom, ze me k tomu cislu, které vraci handleProgress() vyhodi nejen cislo, ale i celou stranku…vč menu, a dalších věci.. proste celym HTML obsahem. Asi to blbe ctu tim JS…

Poradi někdo prosim? Diky a tady je kod:

šablona:

<script type="text/javascript">
    //Script
	$.ajax({
        url: {link longScript!},
        success: function(data) {
        }
    });
    //Progress
    function getProgress(){
        $.ajax({
            url: {link progress!},
            success: function(data) {
                $("#progress").html(data);
                if(data<10){
                    getProgress();
                }
            }
        });
    }
    getProgress();
</script>
<div id="progress"></div>

presenter:

public function handleLongScript() {
        $session = $this->getSession();

            for($i=1;$i<=10;$i++){

            $progress = $session->getSection('progress');
            $progress->val = $i;
            $session->close();

            sleep(1);
            }
    }
    public function handleProgress() {
        echo $this->getSession('progress')->val;
    }

Editoval MW (19. 6. 2015 12:05)

akadlec
Člen | 1326
+
0
-

musíš to terminovat jinak ti to pojede dále. ale osobně bych to hodil když už tak do payloadu a poslal JSON output.

MW
Člen | 626
+
0
-

No je fakt, ze je nesikovny i to v js…

if(data<10){
getProgress();
}

A co to, ze me ten js nacita celou stranku?

Payload by byl asi i rychlejsi co?

Terminuje se to tim, ze jsou data>10 … nebo?

Editoval MW (19. 6. 2015 20:56)

Lukeluha
Člen | 130
+
0
-
public function handleProgress()
{
	$this->payload->data = $this->getSession('progress')->val;
	$this->sendPayload(); // toto ti odešle json response
}

v JS potom

success: function(data) {
                $("#progress").html(data.data);
                if(data.data<10){
                    getProgress();
                }
            }

Editoval Lukeluha (19. 6. 2015 21:01)

Myiyk
Člen | 321
+
0
-

Šlo by to vyřešit bez instalací dalších věcí na server. Musíš nastavit apache, aby zpracovávalo více požadavků současně, min. 2

Přes AJAX budeš volat požadavek na tu akci, která se dlouho zpracovává.
V pravidelných intervalech budeš volat další AJAX na dotaz, v kolika jsi procentech.

Když první volání vrátí odpověď, ukončíš aktualizaci procent.

Funkce zpracování dat musí někde (DB, soubor, atd) zapsat informace o procentech.
A druhá funkce která to má zjistit, se k tomu nějak musí dostat.

Nevýhodou je, že tvůj požadavek nemůžu být příliš složitý, aby se stihl provést.

Editoval Myiyk (20. 6. 2015 0:24)

MW
Člen | 626
+
0
-

Tak kdyby někdo mel zajem, toto je funkcni verze. Budu rad za jakekoliv návrhy na zlepseni :)
Diky

latte:

<script type='text/javascript'>
    //Start the long running process

    function runScript(){
    document.getElementById('progress').style.visibility = 'visible';
    document.getElementById('actionbutton').innerHTML = 'Pracuji...';
    document.getElementById('actionbutton').setAttribute('disabled', 'disabled');

    $.ajax({
        url: {link longScript!},
        success: function(data) {
        }
    });
    getProgress();
    }

    function clearSession(){
    $.ajax({
        url: {link clearSession!}
    });
    }

    //Start receiving progress
    function getProgress(){
        $.ajax({
            url: {link progress!},
            success: function(data) {
                $('#progressinfo').html('Začínám...');
                if(data.data > 0) {
                    $('#progressinfo').html(data.data+ '%');
                }
                if(data.data < 100){
                    $('.progress-bar').css('width', data.data+'%').attr('aria-valuenow', data.data);
                    getProgress();
                }
                if(data.data === 100){
                $('.progress-bar').css('width', '100%').attr('aria-valuenow', 100);
                $('#progressinfo').html('Hotovo');
                clearSession();
                document.getElementById('actionbutton').innerHTML = 'Spustit znovu';
                document.getElementById('actionbutton').removeAttribute('disabled');
                }
            }
        });
    }
</script>

<div id=progress class='progress progress-striped' style='width: 400px; visibility: hidden;'>
    <div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100'>
        <div id='progressinfo'></div>
    </div>
</div><br />

<button id="actionbutton" type="submit" class="btn btn-warning btn-small" onclick="runScript()">Spustit akci</button><br /><br />

a presenter:

public function handleLongScript() {

            sleep(1);
            $session = $this->getSession();
            $progress = $session->getSection('progress');
            $progress->val = 0;
            $session->close();

            for($i=0;$i<=100;$i++){

            $session = $this->getSession();
            $progress = $session->getSection('progress');
            $progress->val = $i;
            $session->close();
            usleep(1000);
            }
            $i = 0;
    }

    public function handleProgress() {
         $this->payload->data = $this->getSession('progress')->val;
         $this->sendPayload();
    }

    public function handleClearSession() {
        $session = $this->getSession();
        $progress = $session->getSection('progress');

        $this->payload->data = NULL;
        $progress->remove();
        $session->close();
    }
akadlec
Člen | 1326
+
0
-

jen tak na okraj, proč mixuješ jquery s nonjquery? Konkrétně mám na mysli: getElementById metody. Samo to ničemu nevadí jen mě to tam bije do očí že kousek je tak a kousek je jinak.

MW
Člen | 626
+
0
-

No JS není zrovna moje domena a priznam se, ze se me to jinak nepodarilo.
Zkousel jsem klasicky selector $(), ale zaboha to neslo…

Asi by si to zaslouzilo upravit, aby to bylo spravne.. nevim co je lepsi… ale na to moje JS znalosti nedosahuji…

akadlec
Člen | 1326
+
+1
-

Možná si tam měl nějaký překlep, toto:

document.getElementById('progress').style.visibility = 'visible';
document.getElementById('actionbutton').innerHTML = 'Pracuji...';
document.getElementById('actionbutton').setAttribute('disabled', 'disabled');

pokud to chceš v jQuery musí jít zapsat takto:

$('#progress').css({'visibility': 'visible'});
$('#actionbutton').html('Pracuji...').prop('disabled', 'disabled');