Afterquery graphing works.
diff --git a/app.py b/app.py
index 9a87349..309d60c 100644
--- a/app.py
+++ b/app.py
@@ -21,10 +21,24 @@
BROADCAST = 'ff:ff:ff:ff:ff:ff'
+DEFAULT_FIELDS = ['seq', 'rate']
+AVAIL_FIELDS = ['seq', 'mcs', 'rate', 'retry',
+ 'type', 'typestr', 'dbm_antsignal', 'dbm_antnoise']
loader = tornado.template.Loader('.')
+def _Esc(s):
+ """Like tornado.escape.url_escape, but only escapes &, #, and %."""
+ out = []
+ for c in s:
+ if c in ['&', '#', '%']:
+ out.append('%%%02X' % ord(c))
+ else:
+ out.append(c)
+ return ''.join(out)
+
+
def GoogleLoginRequired(func):
def Handler(self, *args, **kwargs):
user = users.get_current_user()
@@ -42,6 +56,7 @@
create_time = ndb.DateTimeProperty(auto_now_add=True)
filename = ndb.StringProperty()
show_hosts = ndb.StringProperty(repeated=True)
+ show_fields = ndb.StringProperty(repeated=True)
aliases = ndb.PickleProperty()
@@ -92,7 +107,8 @@
capdefault = PcapData.get_or_insert(str('*'), show_hosts=[], aliases={})
pcapdata = PcapData.get_or_insert(str(blob_info.key()),
filename=blob_info.filename,
- show_hosts=[], aliases={})
+ show_hosts=[], aliases={},
+ show_fields=[])
try:
boxes = _Boxes(blob_info)
except ValueError as e:
@@ -113,6 +129,8 @@
checked = {}
for b, n in cutboxes:
checked[b] = (n > cutoff * 10)
+ if not pcapdata.show_fields:
+ pcapdata.show_fields = DEFAULT_FIELDS
for b in boxes.keys():
if b not in aliases:
aliases[b] = capdefault.aliases.get(b, b)
@@ -121,7 +139,9 @@
boxes=cutboxes,
other=other,
aliases=aliases,
- checked=checked)
+ checked=checked,
+ show_fields=dict((i, 1) for i in pcapdata.show_fields),
+ avail_fields=AVAIL_FIELDS)
class SaveHandler(_BaseHandler):
@@ -142,11 +162,66 @@
pcapdata.aliases[b] = b
if self.request.get('show-%s' % b):
pcapdata.show_hosts.append(b)
+
+ pcapdata.show_fields = []
+ for k in AVAIL_FIELDS:
+ if self.request.get('key-%s' % k):
+ pcapdata.show_fields.append(k)
+
capdefault.put()
pcapdata.put()
- self.response.write('done')
+ url = ('%s?hosts=%s&keys=%s'
+ % (self.request.url.replace('/save/', '/json/'),
+ _Esc(','.join(pcapdata.show_hosts)),
+ _Esc(','.join(pcapdata.show_fields))))
+ self.redirect('//afterquery.appspot.com/?url=%s&chart=traces' % _Esc(url))
+class JsonHandler(_BaseHandler):
+ @GoogleLoginRequired
+ def get(self, blobres):
+ # TODO(apenwarr): allow http-level caching
+ blob_info = blobstore.BlobInfo.get(str(urllib.unquote(blobres)))
+ pcapdata = PcapData.get_or_insert(str(blob_info.key()),
+ show_hosts=[], aliases={})
+ aliases = pcapdata.aliases
+ show_hosts = self.request.get('hosts').split(',')
+ reader = blob_info.open()
+ out = collections.defaultdict(list)
+ keys = self.request.get('keys', 'seq,rate').split(',')
+ timebase = 0
+ for i, (p, frame) in enumerate(wifipacket.Packetize(reader)):
+ if not timebase: timebase = p.pcap_secs
+ ta = p.get('ta')
+ if ta not in show_hosts and aliases.get(ta) not in show_hosts:
+ ta = '~other' # '~' causes it to sort last in the list
+ elif ta in aliases:
+ ta = aliases[ta]
+ ra = p.get('ra')
+ if ra not in show_hosts and aliases.get(ra) not in show_hosts:
+ ra = '~other' # '~' causes it to sort last in the list
+ elif ra in aliases:
+ ra = aliases[ra]
+ out[(ta,ra)].append(('%.6f' % (p.pcap_secs - timebase),
+ tuple(p.get(i) for i in keys)))
+ sessions = list(sorted(out.keys(), key=lambda k: k))
+ headers = ['secs']
+ data = []
+ extra = []
+ for sesskey in sessions:
+ ta, ra = sesskey
+ for k in keys:
+ if ta.startswith('~'): ta = ta[1:]
+ if ra.startswith('~'): ra = ra[1:]
+ headers.append('%s to %s (%s)' % (ta, ra, k))
+ for secs, values in out[sesskey]:
+ data.append([secs] + extra + list(values))
+ extra += [None] * len(keys)
+ j = json.dumps([headers] + data)
+ if self.request.get('jsonp'):
+ j = '%s(%s)' % (self.request.get('jsonp'), j)
+ self.response.write(j)
+
settings = dict(
debug = 1,
)
@@ -156,4 +231,5 @@
(r'/upload', UploadHandler),
(r'/view/([^/]+)$', ViewHandler),
(r'/save/([^/]+)$', SaveHandler),
+ (r'/json/([^/]+)$', JsonHandler),
], **settings)
diff --git a/view.html b/view.html
index ec8003b..98b8770 100644
--- a/view.html
+++ b/view.html
@@ -7,6 +7,15 @@
Size: {{'%.2f' % (blob.size/1e6)}} Mbytes<p>
<form action="/save/{{blob.key()}}" method="POST">
+
+ <div style='padding: 1em'>
+ {% for k in avail_fields %}
+ <input type='checkbox' name='key-{{k}}' id='key-{{k}}' value='{{k}}'
+ {{'checked' if show_fields.get(k) else ''}}
+ ><label for='key-{{k}}'>{{k}}</label></input>
+ {% end %}
+ </div>
+
<table>
<tr>
<th>Show?</th>