mirror of
https://github.com/bendtherules/pytest_gui_status.git
synced 2026-08-18 21:52:18 +00:00
Added gui htmlpy code, todo.txt - maintain it
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
import htmlPy
|
||||||
|
from utils import render_template
|
||||||
|
import os
|
||||||
|
import redis
|
||||||
|
import dateutil.parser
|
||||||
|
from datetime import datetime
|
||||||
|
import humanfriendly
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
env_redis_port = os.environ.get("pytest_status_port")
|
||||||
|
if env_redis_port:
|
||||||
|
REDIS_PORT = int(env_redis_port)
|
||||||
|
else:
|
||||||
|
REDIS_PORT = 5946
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(htmlPy.Object):
|
||||||
|
# GUI callable functions have to be inside a class.
|
||||||
|
# The class should be inherited from htmlPy.Object.
|
||||||
|
dict_state_desc = {
|
||||||
|
"start": "Starting Tests",
|
||||||
|
"collect": "Collecting Tests",
|
||||||
|
"runtest": "Running Tests",
|
||||||
|
"end": "Finished Tests"
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, app_gui):
|
||||||
|
super(Controller, self).__init__()
|
||||||
|
self.i = 0
|
||||||
|
self.app_gui = app_gui
|
||||||
|
self.last_state = None
|
||||||
|
# Initialize the class here, if required.
|
||||||
|
|
||||||
|
@htmlPy.Slot()
|
||||||
|
def redraw(self):
|
||||||
|
# from main import app as app_gui
|
||||||
|
print(self.app_gui.dir_name)
|
||||||
|
|
||||||
|
redis_db = redis.StrictRedis(host='localhost', port=REDIS_PORT, db=0)
|
||||||
|
# make sure that this is correct redis db
|
||||||
|
try:
|
||||||
|
assert redis_db.get("PYTEST_STATUS_DB") == "1"
|
||||||
|
except AssertionError, e:
|
||||||
|
print("Got redis db, but it is not related to pytest status")
|
||||||
|
raise e
|
||||||
|
dir_name = self.app_gui.dir_name
|
||||||
|
hash_dir_name = redis_db.hget("directories_to_hash", dir_name)
|
||||||
|
|
||||||
|
if hash_dir_name is None:
|
||||||
|
raise Exception("dir_name = {dir_name} not found in redis db".format(
|
||||||
|
dir_name=dir_name))
|
||||||
|
|
||||||
|
dict_state = {}
|
||||||
|
dict_state["dir_name"] = dir_name
|
||||||
|
dict_state["dir_name_topfolder"] = os.path.basename(dir_name)
|
||||||
|
|
||||||
|
dict_state["state"] = redis_db.get("{hash_a}_state".format(hash_a=hash_dir_name))
|
||||||
|
dict_state["state_desc"] = self.dict_state_desc[dict_state["state"]]
|
||||||
|
dict_state["last_updated"] = redis_db.get("{hash_a}_last_updated".format(hash_a=hash_dir_name))
|
||||||
|
|
||||||
|
dict_state["last_updated_obj"] = dateutil.parser.parse(dict_state["last_updated"])
|
||||||
|
last_updated_rel = (datetime.now() - dict_state["last_updated_obj"]).seconds # in seconds
|
||||||
|
if last_updated_rel > 60:
|
||||||
|
# when >1 min, reolve only upto min
|
||||||
|
last_updated_rel = last_updated_rel - (last_updated_rel % 60)
|
||||||
|
dict_state["last_updated_friendly"] = humanfriendly.format_timespan(last_updated_rel)
|
||||||
|
|
||||||
|
dict_state["collect"] = redis_db.lrange("{hash_a}_collect".format(hash_a=hash_dir_name), 0, -1)
|
||||||
|
dict_state["pass"] = redis_db.lrange("{hash_a}_pass".format(hash_a=hash_dir_name), 0, -1)
|
||||||
|
dict_state["fail"] = redis_db.lrange("{hash_a}_fail".format(hash_a=hash_dir_name), 0, -1)
|
||||||
|
dict_state["skip"] = redis_db.lrange("{hash_a}_skip".format(hash_a=hash_dir_name), 0, -1)
|
||||||
|
|
||||||
|
if (not self.last_state) or (self.last_state != dict_state):
|
||||||
|
print("called redraw")
|
||||||
|
self.app_gui.html = render_template(self.app_gui, "index.html", dict_state)
|
||||||
|
|
||||||
|
self.last_state = dict_state
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import htmlPy
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from PyQt4.QtGui import QApplication
|
||||||
|
import PyQt4.QtCore as QtCore
|
||||||
|
|
||||||
|
# Import back-end functionalities
|
||||||
|
from gui_backend import Controller
|
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
dir_name_raw = sys.argv[1]
|
||||||
|
else:
|
||||||
|
dir_name_raw = "."
|
||||||
|
dir_name = os.path.abspath(dir_name_raw)
|
||||||
|
|
||||||
|
# GUI initializations
|
||||||
|
app = htmlPy.AppGUI(title=u"{dir_name} - Test Status".format(dir_name=dir_name),
|
||||||
|
developer_mode=True, width=150, height=80)
|
||||||
|
app.window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
|
||||||
|
screen_geo = QApplication.desktop().availableGeometry()
|
||||||
|
screen_topright = screen_geo.topRight()
|
||||||
|
app.x_pos = (screen_topright.x() - 20) - app.width
|
||||||
|
app.y_pos = (screen_topright.y() + 20)
|
||||||
|
# import pdb;pdb.set_trace()
|
||||||
|
|
||||||
|
# GUI configarg
|
||||||
|
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
app.static_path = os.path.join(BASE_DIR, "static/")
|
||||||
|
app.template_path = os.path.join(BASE_DIR, "tmpl/")
|
||||||
|
app.dir_name = dir_name
|
||||||
|
|
||||||
|
# Register back-end functionalities
|
||||||
|
app_backend = Controller(app)
|
||||||
|
app.bind(app_backend)
|
||||||
|
|
||||||
|
# GUI render config
|
||||||
|
# app.html = u""
|
||||||
|
app_backend.redraw()
|
||||||
|
|
||||||
|
|
||||||
|
# Start application
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.start()
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{dir_name}}</title>
|
||||||
|
<style>
|
||||||
|
*
|
||||||
|
{
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
margin: 2px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.last-test-statusviz
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
.statusdot
|
||||||
|
{
|
||||||
|
height: 100%;
|
||||||
|
{% if collect|length > 0 %}
|
||||||
|
width: {{100/(collect|length)}}%;
|
||||||
|
{% else %}
|
||||||
|
width: 0;
|
||||||
|
{% endif %}
|
||||||
|
/*width: 1em;*/
|
||||||
|
padding: 1px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.statusdot-fill
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.fill-pass
|
||||||
|
{
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
.fill-fail
|
||||||
|
{
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
.last-test-statusviz
|
||||||
|
{
|
||||||
|
overflow: hidden;
|
||||||
|
border: 2px solid black;
|
||||||
|
}
|
||||||
|
.last-test-timerelative
|
||||||
|
{
|
||||||
|
font-size: 0.75em;
|
||||||
|
height: 1em;
|
||||||
|
/*overflow: hidden;*/
|
||||||
|
}
|
||||||
|
.last-test-dirname
|
||||||
|
{
|
||||||
|
font-size: 0.75em;
|
||||||
|
/*height: 1em;*/
|
||||||
|
/*position: relative;*/
|
||||||
|
}
|
||||||
|
/*.last-test-dirname-redacted
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 999px;
|
||||||
|
font-size: 0.75em;
|
||||||
|
text-align: right;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<!-- <script type="text/javascript" src="./refresh.js"></script> -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
// debugger
|
||||||
|
document.onload = setInterval(function(){Controller.redraw()},1000)
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="section-last-test">
|
||||||
|
<div class="state">
|
||||||
|
{{ state_desc }}
|
||||||
|
</div>
|
||||||
|
<div class="last-test-statusviz">
|
||||||
|
{% for testname in collect %}
|
||||||
|
<div class="statusdot statusdot-{{loop.index0}}">
|
||||||
|
<!-- {{ testname }} -->
|
||||||
|
{% if testname in pass %}
|
||||||
|
<div class="statusdot-fill fill-pass"></div>
|
||||||
|
{% elif testname in fail %}
|
||||||
|
<div class="statusdot-fill fill-fail"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="last-test-timerelative">
|
||||||
|
Got {{ last_updated_friendly }} ago
|
||||||
|
</div>
|
||||||
|
<div class="last-test-dirname">
|
||||||
|
Folder : {{ dir_name_topfolder }}
|
||||||
|
<!-- <div class="last-test-dirname-redacted"> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
setTimeout(Controller.redraw, 500)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
Todo
|
||||||
|
------
|
||||||
|
|
||||||
|
1. move rendering update to js using diff-dom + nunjucks ( to be decided )
|
||||||
|
2. (-done manually, add tests)check intermediate test steps like collect, testrun
|
||||||
|
3. In plugin, set attributes in chunk using pipeline, using `with`
|
||||||
|
|
||||||
|
Done
|
||||||
|
------
|
||||||
|
|
||||||
|
1. (---) Get dirname from cmdline
|
||||||
|
2. (---) responsive width of test
|
||||||
|
3. (---) Time should update automatically
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import jinja2
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def render_template(app, tmpl_name, tmpl_params):
|
||||||
|
with open(os.path.join(app.template_path, tmpl_name)) as f_tmpl:
|
||||||
|
tmpl_content = f_tmpl.read()
|
||||||
|
return jinja2.Template(tmpl_content).render(tmpl_params)
|
||||||
Reference in New Issue
Block a user