add static site exporter, and adjust urls to be friendlier to a flat filesystem directory

This commit is contained in:
Tim Buckley 2015-07-29 12:01:29 -06:00
parent f8f7676952
commit cd1e75fb21
7 changed files with 122 additions and 26 deletions

98
export_static.py Normal file
View File

@ -0,0 +1,98 @@
import os
import shutil
import django
from argparse import ArgumentParser
from django.test import RequestFactory
from django.core.urlresolvers import resolve
from stackviz.parser import tempest_subunit
EXPORT_PATHS = [
'/index.html',
'/tempest_summary.html',
'/tempest_aggregate.html'
]
def fake_render_view(path):
factory = RequestFactory()
request = factory.get(path)
match = resolve(path)
response = match.func(request, *match.args, **match.kwargs)
if hasattr(response, "render"):
response.render()
return response
def export_single_page(path, dest_dir):
dest_file = path
if dest_file.startswith('/'):
dest_file = dest_file[1:]
with open(os.path.join(dest_dir, dest_file), 'w') as f:
content = fake_render_view(path).content
f.write(content)
def main():
parser = ArgumentParser(description="Generates a self-contained, static "
"StackViz site at the given path.")
parser.add_argument("path",
help="The output directory. Will be created if it "
"doesn't already exist.")
parser.add_argument("--ignore-bower",
help="Ignore missing Bower components.")
args = parser.parse_args()
if not args.ignore_bower:
if not os.listdir(os.path.join('stackviz', 'static', 'components')):
print "Bower components have not been installed, please run " \
"`bower install`"
return 1
if os.path.exists(args.path):
if os.listdir(args.path):
print "Destination exists and is not empty, cannot continue"
return 1
os.mkdir(args.path)
print "Copying static files ..."
shutil.copytree(os.path.join('stackviz', 'static'),
os.path.join(args.path, 'static'))
for path in EXPORT_PATHS:
print "Rendering:", path
export_single_page(path, args.path)
for run_id in range(tempest_subunit.get_repositories()[0].count()):
print "Rendering views for tempest run #%d" % run_id
export_single_page('/tempest_timeline_%d.html' % run_id, args.path)
export_single_page('/tempest_results_%d.html' % run_id, args.path)
print "Exporting data for tempest run #%d" % run_id
export_single_page('/tempest_api_tree_%d.json' % run_id, args.path)
export_single_page('/tempest_api_raw_%d.json' % run_id, args.path)
# TODO
# export_single_page('tempest_api_details_%d.json' % run_id, args.path)
if __name__ == '__main__':
# remove leading / from static URL to give them correct filesystem paths
import stackviz.settings as settings
settings.STATIC_URL = settings.STATIC_URL[1:]
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "stackviz.settings")
django.setup()
main()

View File

@ -71,7 +71,7 @@ function createTable(data, run) {
tbl.setAttribute("id","table-run-" + run);
tbl.setAttribute("class","table table-bordered table-hover table-striped");
var header = tbl.createTHead();
header.innerHTML = '<tr><th><a href="/tempest/timeline/' + run + '"> Run #' + run + '</a></th></tr>';
header.innerHTML = '<tr><th><a href="tempest_timeline_' + run + '.html"> Run #' + run + '</a></th></tr>';
for (var key in data_dict) {
var row = tbl.insertRow();
var c1 = row.insertCell();
@ -92,7 +92,7 @@ function createTables(run_id) {
//outer function so callback can use i "synchronously"
//TODO: Sort tables when inserting so they appear in correct order
!function(ii) {
d3.json("/tempest/api/tree/" + i + "/", function(error, data) {
d3.json("tempest_api_tree_" + i + ".json", function(error, data) {
if (error) throw error;
//create a table for the info
createTable(data, ii);

View File

@ -77,7 +77,7 @@
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
d3.json("/tempest/api/tree/" + run_id + "/", function(error, root) {
d3.json("tempest_api_tree_" + run_id + ".json", function(error, root) {
if (error) throw error;
displayFailingTests(root);

View File

@ -22,19 +22,19 @@
<a href="#"><i class="fa fa-bar-chart-o fa-fw"></i> Tempest<span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li>
<a href="/tempest/summary"><i class="fa fa-bar-chart-o fa-fw"></i> Summary</a>
<a href="tempest_summary.html"><i class="fa fa-bar-chart-o fa-fw"></i> Summary</a>
</li>
<li>
<a href="/tempest/results"><i class="fa fa-clock-o fa-fw"></i> Results</a>
<a href="tempest_results_{{ tempest_latest_run }}.html"><i class="fa fa-clock-o fa-fw"></i> Results</a>
</li>
<li>
<a href="/tempest/timeline"><i class="fa fa-calendar fa-fw"></i> Timeline</a>
<a href="tempest_timeline_{{ tempest_latest_run }}.html"><i class="fa fa-calendar fa-fw"></i> Timeline</a>
</li>
<li>
<!--<li>
<a href="/tempest/"><i class="fa fa-database fa-fw"></i> Compare</a>
</li>
</li>-->
<li>
<a href="/tempest/aggregate"><i class="fa fa-bar-chart-o fa-fw"></i> Aggregate Results</a>
<a href="tempest_aggregate.html"><i class="fa fa-bar-chart-o fa-fw"></i> Aggregate Results</a>
</li>
</ul>
<!-- /.nav-second-level -->

View File

@ -123,7 +123,7 @@ var showDetails = function(item) {
showInfo(item);
d3.json("/tempest/api/details/{{run_id}}/" + item.name, function(error, data) {
d3.json("tempest_api_details_{{run_id}}_" + item.name + ".json", function(error, data) {
if (error) {
throw error;
}
@ -149,7 +149,7 @@ window.addEventListener('load', function() {
var selectedItem = null;
var selectedValue = null;
loadTimeline("/tempest/api/raw/{{run_id}}/", {
loadTimeline("tempest_api_raw_{{run_id}}.json", {
container: $("#timeline-container")[0],
onClick: function(d) {
var self = d3.select(this);

View File

@ -9,6 +9,7 @@ urlpatterns = patterns('',
# url(r'^blog/', include('blog.urls')),
url(r'^$', IndexView.as_view()),
url(r'^tempest/', include('stackviz.views.tempest.urls')),
url(r'^devstack/', include('stackviz.views.devstack.urls')),
url(r'^index.html$', IndexView.as_view(), name="index"),
url(r'^tempest_', include('stackviz.views.tempest.urls')),
url(r'^devstack_', include('stackviz.views.devstack.urls')),
)

View File

@ -11,35 +11,32 @@ from .api import (TempestRunTreeEndpoint,
urlpatterns = patterns('',
url(r'^results/(?P<run_id>\d+)/$',
url(r'^results_(?P<run_id>\d+).html$',
ResultsView.as_view(),
name='tempest_results'),
url(r'^results/$',
url(r'^results.html$',
LatestResultsView.as_view(),
name='tempest_results_latest'),
url(r'^timeline/(?P<run_id>\d+)/$',
url(r'^timeline_(?P<run_id>\d+).html$',
TimelineView.as_view(),
name='tempest_timeline'),
url(r'^timeline/$',
TimelineLatestView.as_view(),
name='tempest_timeline_latest'),
url(r'^api/tree/(?P<run_id>\d+)/$',
url(r'^api_tree_(?P<run_id>\d+).json$',
TempestRunTreeEndpoint.as_view(),
name='tempest_api_tree'),
url(r'^api/raw/(?P<run_id>\d+)/$',
url(r'^api_raw_(?P<run_id>\d+).json$',
TempestRunRawEndpoint.as_view(),
name='tempest_api_raw'),
url(r'^api/details/(\d+)/([^/]+)/$',
url(r'^api_details_(\d+)_([^/]+).json$',
TempestRunDetailsEndpoint.as_view(),
name='tempest_api_details'),
url(r'^aggregate/$',
url(r'^aggregate.html$',
AggregateResultsView.as_view(),
name='aggregate_results'),
name='tempest_aggregate_results'),
url(r'^summary/$',
url(r'^summary.html$',
SummaryView.as_view(),
name='summary_results')
name='tempest_summary_results')
)