Permalink
Browse files

Allow interactively sorting the tables generated by test/wild.sh.

This allows you to quickly zero in on parse failures in a huge
directory, e.g. alpine's aports repo.

License note:

The web/table/* library is borrowed from code I wrote for google/rappor
on Github.
  • Loading branch information...
Andy Chu
Andy Chu committed Oct 12, 2017
1 parent b567c49 commit 29480271531aa08b12d7954abf9fe8f0c772ee04
View
@@ -54,9 +54,10 @@ print-manifest() {
#egrep '^dokku|^wwwoosh|^oil' _tmp/wild/MANIFEST.txt
#head -n $NUM_TASKS _tmp/wild/MANIFEST.txt
#egrep -- 'mesos' _tmp/wild/MANIFEST.txt
#cat _tmp/wild/MANIFEST.txt
cat _tmp/wild/MANIFEST.txt
#egrep -- '^aports' _tmp/wild/MANIFEST.txt
egrep -- '^gherkin' _tmp/wild/MANIFEST.txt
#egrep -- '^gherkin' _tmp/wild/MANIFEST.txt
#egrep -- '^bash' _tmp/wild/MANIFEST.txt
}
parse-all() {
@@ -93,6 +94,8 @@ make-report() {
_link \
$PWD/web/wild.css \
$PWD/web/osh-to-oil.{html,js,css} \
$PWD/web/ajax.js \
$PWD/web/table/table-sort.js \
_tmp/wild/www
}
View
@@ -1,38 +1,16 @@
#!/usr/bin/python
#!/usr/bin/env python
"""
wild_report.py
"""
#import csv
import json
import glob
import os
import sys
from collections import defaultdict
import urllib
import jsontemplate
# TODO:
# - Add table-lib.js so we can sort the results!
# - this will identify the 2 failures in Alpine!
# - Measure internal process time
# - Show totals for the directory underneath the tables?
# - or at least you want a top level dir, above WILD
# - Maybe organize it into dirs
# - shlib/ # shell libraries
# - os/ # kernel and distros
# - langs/
# - esoteric/
# - cloud/
# - google/
# - scripts/ # misc scripts
# - other/
# JSON Template Evaluation:
#
# - {.if}{.or} is confusing
@@ -56,8 +34,7 @@
F = {
'commas': lambda n: '{:,}'.format(n),
# NOTE: matches urlesc in handlers/lists.py
'urlesc': urllib.quote_plus,
#'urlesc': urllib.quote_plus,
}
def MakeHtmlGroup(title_str, body_str):
@@ -74,10 +51,14 @@ def MakeHtmlGroup(title_str, body_str):
<head>
<title>{.template TITLE}</title>
<script type="text/javascript" src="{base_url}ajax.js"></script>
<script type="text/javascript" src="{base_url}table-sort.js"></script>
<link rel="stylesheet" type="text/css" href="{base_url}wild.css" />
</head>
<body>
<body onload="initPage(gUrlHash, gTableStates, kStatusElem);"
onhashchange="onHashChange(gUrlHash, gTableStates, kStatusElem);">
<p id="status"></p>
{.template NAV}
{.template BODY}
@@ -110,7 +91,17 @@ def MakeHtmlGroup(title_str, body_str):
'WILD/{rel_path}',
"""\
{.section dirs}
<table>
<table id="dirs">
<colgroup> <!-- for table-sort.js -->
<col type="number">
<col type="number">
<col type="number">
<col type="number">
<col type="number">
<col type="number">
<col type="number">
<col type="case-insensitive">
</colgroup>
<thead>
<tr>
<td>Files</td>
@@ -158,7 +149,16 @@ def MakeHtmlGroup(title_str, body_str):
</p>
{.section files}
<table>
<table id="files">
<colgroup> <!-- for table-sort.js -->
<col type="number">
<col type="case-insensitive">
<col type="number">
<col type="number">
<col type="number">
<col type="case-insensitive">
<col type="case-insensitive">
</colgroup>
<thead>
<tr>
<td>Lines</td>
@@ -234,6 +234,27 @@ def MakeHtmlGroup(title_str, body_str):
</table>
{.end}
<!-- page globals -->
<script type="text/javascript">
var gUrlHash = new UrlHash(location.hash);
var gTableStates = {};
var kStatusElem = document.getElementById('status');
function initPage(urlHash, tableStates, statusElem) {
var e1 = document.getElementById('dirs');
var e2 = document.getElementById('files');
var t = [];
if (e1) { t.push(e1); }
if (e2) { t.push(e2); }
makeTablesSortable(urlHash, t, tableStates);
updateTables(urlHash, tableStates, statusElem);
}
function onHashChange(urlHash, tableStates, statusElem) {
updateTables(urlHash, tableStates, statusElem);
}
</script>
""")
@@ -249,6 +270,10 @@ class DirNode:
def __init__(self):
self.files = {} # filename -> stats for success/failure, time, etc.
self.dirs = {} # subdir name -> Dir object
# Or should this be self.totals? So the root node has it.
# Then you can show self.dirs.totals in WriteHtmlFiles. Yes.
self.dir_totals = {} # subdir -> summed stats
# show all the non-empty stderr here?
View
@@ -0,0 +1,149 @@
// Minimal AJAX library.
//
// The motivation is that we want to generate PNG, JSON, CSV, etc. from R.
// And maybe some HTML fragments. But we don't want to generate a different
// skeleton for every page. It's nice just to hit F5 and see the changes
// reloaded. It's like "PHP in the browser'.
'use strict';
// Append a message to an element. Used for errors.
function appendMessage(elem, msg) {
elem.innerHTML += msg + '<br />';
}
// jQuery-like AJAX helper, but simpler.
// Requires an element with id "status" to show errors.
//
// Args:
// errElem: optional element to append error messages to. If null, then
// alert() on error.
// success: callback that is passed the xhr object.
function ajaxGet(url, errElem, success) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true /*async*/);
xhr.onreadystatechange = function() {
if (xhr.readyState != 4 /*DONE*/) {
return;
}
if (xhr.status != 200) {
var msg = 'ERROR requesting ' + url + ': ' + xhr.status + ' ' +
xhr.statusText;
if (errElem) {
appendMessage(errElem, msg);
} else {
alert(msg);
}
return;
}
success(xhr);
};
xhr.send();
}
function jsonGet(url, errElem, success) {
ajaxGet(url, errElem, function(xhr) {
try {
var j = JSON.parse(xhr.responseText);
} catch (e) {
appendMessage(errElem, `Parsing JSON in ${url} failed`);
}
success(j);
});
}
function htmlEscape(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
//
// UrlHash
//
// helper
function _decode(s) {
var obj = {};
var parts = s.split('&');
for (var i = 0; i < parts.length; ++i) {
if (parts[i].length === 0) {
continue; // quirk: ''.split('&') is [''] ? Should be a 0-length array.
}
var pair = parts[i].split('=');
obj[pair[0]] = pair[1]; // for now, assuming no =
}
return obj;
}
function _encode(d) {
var parts = [];
for (var name in d) {
var s = name;
s += '=';
var value = d[name];
s += encodeURIComponent(value);
parts.push(s);
}
return parts.join('&');
}
// UrlHash Constructor.
// Args:
// hashStr: location.hash
function UrlHash(hashStr) {
this.reset(hashStr);
}
UrlHash.prototype.reset = function(hashStr) {
var h = hashStr.substring(1); // without leading #
// Internal storage is string -> string
this.dict = _decode(h);
}
UrlHash.prototype.set = function(name, value) {
this.dict[name] = value;
};
UrlHash.prototype.del = function(name) {
delete this.dict[name];
};
UrlHash.prototype.get = function(name ) {
return this.dict[name];
};
// e.g. Table states have keys which start with 't:'.
UrlHash.prototype.getKeysWithPrefix = function(prefix) {
var keys = [];
for (var name in this.dict) {
if (name.indexOf(prefix) === 0) {
keys.push(name);
}
}
return keys;
};
// Return a string reflecting internal key-value pairs.
UrlHash.prototype.encode = function() {
return _encode(this.dict);
};
// Useful for AJAX navigation. If UrlHash is the state of the current page,
// then we override the state with 'attrs' and then return a serialized query
// fragment.
UrlHash.prototype.modifyAndEncode = function(attrs) {
var copy = {}
// NOTE: Object.assign is ES6-only
// https://googlechrome.github.io/samples/object-assign-es6/
Object.assign(copy, this.dict, attrs);
return _encode(copy);
};
Oops, something went wrong.

0 comments on commit 2948027

Please sign in to comment.