Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ajax Requests panel - proof of concept #253

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions debug_toolbar/media/debug_toolbar/js/toolbar.js
Expand Up @@ -83,6 +83,25 @@ window.djdt = (function(window, document, jQuery) {
});
return;
});
function refreshAjaxPanel() {
$.get('/__debug__/ajax_request/', function(data) {
$('#djDebugAjaxPanel .djDebugPanelContent div.scroll').html(data);
var num_reqs = $('#djDebugAjaxPanel .djDebugPanelContent tbody tr').length;
$('a.djDebugAjaxPanel small').html(num_reqs + ' requests');
});
}
$('#djDebug a.djAjaxLoad').live('click', function(e) {
e.preventDefault();
var req_id = $(this).attr('data-requestid');
$.get('/__debug__/ajax_request/' + req_id + '/', function(data) {
$('#djDebugWrapper').wrap('<div>').parent().html(data);
refreshAjaxPanel();
});
});
$('#djAjaxRefreshBtn').live('click', function(e) {
e.preventDefault();
refreshAjaxPanel();
});
function getSubcalls(row) {
var id = row.attr('id');
return $('.djDebugProfileRow[id^="'+id+'_"]');
Expand Down
6 changes: 3 additions & 3 deletions debug_toolbar/media/debug_toolbar/js/toolbar.min.js

Large diffs are not rendered by default.

22 changes: 16 additions & 6 deletions debug_toolbar/middleware.py
Expand Up @@ -3,6 +3,7 @@
"""
import imp
import thread
import datetime

from django.conf import settings
from django.http import HttpResponseRedirect
Expand Down Expand Up @@ -127,11 +128,20 @@ def process_response(self, request, response):
response.get('Content-Type', '').split(';')[0] in _HTML_TYPES:
for panel in toolbar.panels:
panel.process_response(request, response)
response.content = replace_insensitive(
smart_unicode(response.content),
self.tag,
smart_unicode(toolbar.render_toolbar() + self.tag))
if response.get('Content-Length', None):
response['Content-Length'] = len(response.content)
content = smart_unicode(response.content)
ddt_html = smart_unicode(toolbar.render_toolbar())
if self.tag in content:
response.content = replace_insensitive(content, self.tag,
ddt_html + self.tag)
if response.get('Content-Length', None):
response['Content-Length'] = len(response.content)
if request.is_ajax():
from debug_toolbar.panels.ajax import AjaxDebugPanel
try:
ajax_panel = toolbar.get_panel(AjaxDebugPanel)
except IndexError:
ajax_panel = None
if ajax_panel:
ajax_panel.record(request, ddt_html)
del self.__class__.debug_toolbars[ident]
return response
66 changes: 66 additions & 0 deletions debug_toolbar/panels/ajax.py
@@ -0,0 +1,66 @@
import time
import uuid
import inspect
import datetime

from django.utils.translation import ugettext_lazy as _
from debug_toolbar.panels import DebugPanel


class AjaxDebugPanel(DebugPanel):
"""
Panel that displays recent AJAX requests.
"""
name = 'Ajax'
template = 'debug_toolbar/panels/ajax.html'
has_content = True
session_key = 'debug_toolbar_ajax_requests'

def __init__(self, *args, **kwargs):
super(AjaxDebugPanel, self).__init__(*args, **kwargs)
self._num_requests = 0

def nav_title(self):
return _('Ajax')

def nav_subtitle(self):
# TODO l10n: use ngettext
return "%d request%s" % (
self._num_requests,
(self._num_requests == 1) and '' or 's'
)

def title(self):
return _('Ajax Requests')

def url(self):
return ''

def storage(self, request):
if self.session_key not in request.session:
request.session[self.session_key] = []
return request.session[self.session_key]

def record(self, request, ddt_html):
self.storage(request).append({
'id': str(uuid.uuid4()),
'time': datetime.datetime.now(),
'path': request.path,
'html': ddt_html,
})

def get_html(self, request, req_id):
for ajax_request in self.storage(request):
if ajax_request['id'] == req_id:
return ajax_request['html']

def get_context(self, request):
return {
'ajax_requests': self.storage(request),
}

def process_request(self, request):
self._num_requests = len(self.storage(request))

def process_response(self, request, response):
self.record_stats(self.get_context(request))
2 changes: 2 additions & 0 deletions debug_toolbar/templates/debug_toolbar/base.html
@@ -1,4 +1,5 @@
{% load i18n %}
<div id="djDebugWrapper">
<style type="text/css">
@media print { #djDebug {display:none;}}
{{ css }}
Expand Down Expand Up @@ -52,3 +53,4 @@ <h3>{{ panel.title|safe }}</h3>
{% endfor %}
<div id="djDebugWindow" class="panelContent"></div>
</div>
</div>
19 changes: 19 additions & 0 deletions debug_toolbar/templates/debug_toolbar/panels/ajax.html
@@ -0,0 +1,19 @@
{% load i18n %}
<table>
<thead>
<tr>
<th>{% trans "Request Time" %}</th>
<th>{% trans "Request Path" %}</th>
</tr>
</thead>
<tbody>
{% for ajax_req in ajax_requests %}
<tr id='djAjaxReq-{{ ajax_req.id }}' class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
<td>{{ ajax_req.time }}</td>
<td><a class='djAjaxLoad' data-requestid='{{ ajax_req.id }}' href='' title='Load the toolbar for this request'>{{ ajax_req.path }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<br/>
<button id='djAjaxRefreshBtn' type='button'>{% trans 'Refresh' %}</button>
1 change: 1 addition & 0 deletions debug_toolbar/toolbar/loader.py
Expand Up @@ -38,6 +38,7 @@ def __init__(self, request):
#'debug_toolbar.panels.cache.CacheDebugPanel',
'debug_toolbar.panels.signals.SignalDebugPanel',
'debug_toolbar.panels.logger.LoggingPanel',
'debug_toolbar.panels.ajax.AjaxDebugPanel',
)
self.load_panels()
self.stats = {}
Expand Down
2 changes: 2 additions & 0 deletions debug_toolbar/urls.py
Expand Up @@ -14,4 +14,6 @@
url(r'^%s/sql_explain/$' % _PREFIX, 'debug_toolbar.views.sql_explain', name='sql_explain'),
url(r'^%s/sql_profile/$' % _PREFIX, 'debug_toolbar.views.sql_profile', name='sql_profile'),
url(r'^%s/template_source/$' % _PREFIX, 'debug_toolbar.views.template_source', name='template_source'),
url(r'^%s/ajax_request/$' % _PREFIX, 'debug_toolbar.views.ajax_list', name='ajax_list'),
url(r'^%s/ajax_request/(?P<req_id>[\w-]+)/$' % _PREFIX, 'debug_toolbar.views.ajax_request', name='ajax_request'),
)
35 changes: 34 additions & 1 deletion debug_toolbar/views.py
Expand Up @@ -7,7 +7,7 @@
import os
import django.views.static
from django.conf import settings
from django.http import HttpResponseBadRequest
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.utils import simplejson
from django.utils.hashcompat import sha_constructor
Expand Down Expand Up @@ -204,3 +204,36 @@ def template_source(request):
'source': source,
'template_name': template_name
})


def ajax_request(request, req_id):
"""
Returns the Debug Toolbar HTML for the given request ID.
"""
from debug_toolbar.panels.ajax import AjaxDebugPanel
from debug_toolbar.middleware import DebugToolbarMiddleware
toolbar = DebugToolbarMiddleware.get_current()
try:
ajax_panel = toolbar.get_panel(AjaxDebugPanel)
except IndexError:
raise ValueError('AjaxDebugPanel must be enabled to use this view.')
ddt_html = ajax_panel.get_html(request, req_id)
if ddt_html:
return HttpResponse(ddt_html)
else:
return HttpResponseBadRequest('No such request {0}'.format(req_id))


def ajax_list(request):
"""
Returns the latest list of AJAX requests in HTML format.
"""
from debug_toolbar.panels.ajax import AjaxDebugPanel
from debug_toolbar.middleware import DebugToolbarMiddleware
toolbar = DebugToolbarMiddleware.get_current()
try:
ajax_panel = toolbar.get_panel(AjaxDebugPanel)
except IndexError:
raise ValueError('AjaxDebugPanel must be enabled to use this view.')
context = ajax_panel.get_context(request)
return render_to_response('debug_toolbar/panels/ajax.html', context)