Panel informacyjno-kontrolny #5: Śledzenie postępu testów

Przychodzi czas, gdy jesteśmy już tak ciekawi wyniku testów, że zaczyna nas boleć palec od odświeżania strony, na której znajdują się wyniki testów. Wtedy z pomocą przychodzi nam automatyczne odświeżanie danych znajdujących się na tej stronie.

flask python

Panel informacyjno-kontrolny (#5) - Śledzenie postępu testów


Przychodzi czas, gdy jesteśmy już tak ciekawi wyniku testów, że zaczyna nas boleć palec od odświeżania strony. Wtedy z pomocą przychodzi nam automatyczne odświeżanie strony lub samych danych znajdujących się na tej stronie. Pierwsze rozwiązanie może jest sensowne przy niewielkich stronach. Jednak gdy w grę wchodzą grafiki i duża ilość danych. Czasem nawet wykonywane w background’zie skrypty, druga opcja jest o wiele bardziej pożądana. Co więc pomoże nam w śledzeniu postępu testów w czasie rzeczywistym? Oczywiście przez wszystkich uwielbiana JavaScript, a dokładniej jej młodsza siostra jQuery.

Back-end

Oczywiście jak to zwykle bywa, polecam zapoznać się najpierw z poprzednią odsłoną tej serii, gdyż znajdziesz tam kod, który będziemy rozbudowywać: 

Na początek okroimy kod z app.py do koniecznego minimum ponieważ nie będziesz już potrzebował wstrzykiwać danych z Pythona podczas wczytywania strony. Główna funkcja renderowania template’u ograniczy się w tym wypadku tylko do wczytania strony index.html, która właśnie jest naszym template’m:

from flask import Flask, render_template
import os
import json
 
 
class TestLogger:
    def __init__(self):
        self.info_list = self.get_info()
 
 
    @staticmethod
    def get_info():
        return os.popen('cat logs/pytest.logs').read()
 
 
    def list_of_lines(self):
        return self.info_list.splitlines()
 
 
app = Flask(__name__)
 
 
@app.route("/")
def hello():
    return render_template('index.html')
 
 
 
 
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

Pewnie zastanawiasz się (lub nie) co w import’ach robi json. Otóż będziemy go przesyłać co jakiś czas do naszej strony. Zawierał będzie aktualne dane na temat stanu wywoływanych testów. Do tego będziemy potrzebowali dodatkowej funkcji, ja nazwałem ją sys_info(), funkcja ta będzie odpowiedzialna za odświeżanie danych na stronie. Abyśmy mieli możliwość wywołania tej funkcji z perspektywy kodu strony (index.html), potrzebować będziemy odpowiedniego dekoratora. @app.route(‚/sys_info.json’) w prosty sposób tworzy odwołanie do pliku json sys_info.json. Jego zawartość jest generowana za pomocą funkcji json.dumps ze słownikiem jako argumentem tej funkcji. Mam nadzieję, że nie zbyt skomplikowałem te zaledwie cztery linijki kodu

@app.route('/sys_info.json')
def sys_info():
    list_of_data = list()
    return json.dumps({"variable": list_of_data})

Oczywiście tak przygotowany kod niewiele nam zwróci, jedynie klucz variable z przypisaną pustą tablicą znaków. Wykorzystajmy więc z wcześniej napisaną klasę TestLogger i uzupełnijmy ten słownik o przydatne dla nas informacje. Najpierw zainicjuj klasę i przypisz do zmiennej. W moim kodzie jest to zmienna test_logger. Następnie przy jej pomocy pobierz listę zawartości linii pliku loga przy pomocy funkcji list_of_lines(). Oprócz danych z loga przydatną informacją, którą możemy umieścić w json’ie jest ilość linii jaką zawiera log, wystarczy, że ‚policzymy’ ilość komórek naszej listy. Teraz możemy, wywołując tę funkcję, uzyskać dane, które chcemy odświeżać na stronie:

from flask import Flask, render_template
import os
import json
 
 
class TestLogger:
    def __init__(self):
        self.info_list = self.get_info()
 
 
    @staticmethod
    def get_info():
        return os.popen('cat logs/pytest.logs').read()
 
 
    def list_of_lines(self):
        return self.info_list.splitlines()
 
 
app = Flask(__name__)
 
 
@app.route("/")
def hello():
    return render_template('index.html')
 
 
@app.route('/sys_info.json')
def sys_info():
    test_logger = TestLogger()
    list_of_lines = test_logger.list_of_lines()
    return json.dumps({"len": len(test_logger.list_of_lines()),"variable": list_of_lines})
 
 
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

Front-end

Zacznijmy od stworzenia pliku index.html całkowicie od podstaw. Przyda nam się stworzenie głównej części gdzie umieścimy jeszcze przez chwilę tajemniczy skrypt JavySkrypt, w którym kolejno umieścimy div o id równym „test_info„. Będzie to kluczowe, gdyż potem odwołamy się do tej części kodu, by ją uzupełnić o dane zaczytane z pliku json. Następnie wszystkie części składowe strony domykamy:

<html>
<script type=text/javascript src="{{url_for('static', filename='jquery.js') }}"></script>
<body>
<div id="test_info"> </div>
</body>
</html>

Jak widzimy powyżej potrzebujemy pliku jquery.js. Możemy go ściągnąć bezpośrednio ze strony zawierającej tę bibliotekę JavaScript:

Ściągnięty pliku umieść w folderze static, który powinien znajdować się tam, gdzie plik app.py czy folder templates. Jeśli go tam nie ma to utwórz folder static, a następnie umieść w nim plik jquery.js. Poniżej gotowy kod index.hml, który będzie co sekundę (1000 milisekund w 18-stej linijce) odwoływał się do pliku json’a z danymi (linia 9). Następnie i tak zebrane dane obrobi na potrzeby HTML’a tak by każda linia loga znajdowała się kolejno jedna pod drugą. Do tego służy zawartość funkcji for w linii 13. Po tym następuje podmiana tych danych (linijka 16) w miejsce oznaczone id równym test_info (w moim przypadku jest to div w linijce 4):

<html>
<script type=text/javascript src="{{url_for('static', filename='jquery.js') }}"></script>
<body>
<div id="test_info"> </div>
<script>
setInterval(
function update_values() {
            $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
            $.getJSON($SCRIPT_ROOT+"/sys_info.json",
                function(data) {
		    var i;
		    var text = "";
		    for (i = 0; i < data.len; i++) {
			text += data.variable[i] + "<br>";
		    }
                    $("#test_info").html(text)
                });
        },1000)
</script>
</body>
</html>

Choć na pierwszy rzut oka może się wydawać przerażające używanie JavaScript, to należy pamiętać, że może ona wbrew pozorom znacznie ułatwić tworzenie wszelkiego rodzaju paneli webowych. Szczególnie gdy chcemy je odświeżać i obrabiać w czasie rzeczywistym.

Prawie koniec

Myślę, że tak przygotowani możecie tworzyć bardzo przydatne narzędzia webowe oparte o back-end pisany w Python’ie co pozwala bezpośrednio pracując z wcześniej napisanymi testami. W moim planie dla tej serii zostało jeszcze jedno zagadnienie, którym jest uruchomienie testów z poziomu panelu.

close

Newsletter