3
3
from time import time
4
4
5
5
import wrapt
6
- from django .dispatch import Signal
7
- from django .utils .translation import ugettext_lazy as _
8
-
9
6
from debug_toolbar .panels import Panel
10
7
from debug_toolbar .panels .sql .utils import contrasting_color_generator
8
+ from django .dispatch import Signal
9
+ from django .utils .translation import ugettext_lazy as _
11
10
12
-
13
- template_rendered = Signal (providing_args = ['instance' , 'start' , 'end' , 'level' ])
11
+ template_rendered = Signal (providing_args = [
12
+ 'instance' , 'start' , 'end' , 'level' , 'processing_timeline' ,
13
+ ])
14
+
15
+
16
+ node_element_colors = {}
17
+
18
+
19
+ def get_nodelist_timeline (nodelist , level ):
20
+ timeline = []
21
+ for node in nodelist :
22
+ timeline += get_node_timeline (node , level )
23
+ return timeline
24
+
25
+
26
+ def get_node_timeline (node , level ):
27
+ """
28
+ Get timeline for node and it's children
29
+ """
30
+ timeline = []
31
+ child_nodelists = getattr (node , "child_nodelists" , None )
32
+ if child_nodelists :
33
+ for child_nodelist_str in child_nodelists :
34
+ child_nodelist = getattr (node , child_nodelist_str , None )
35
+ if child_nodelist :
36
+ timeline += get_nodelist_timeline (child_nodelist , level + 1 )
37
+
38
+ if hasattr (node , "_node_end" ):
39
+ timeline .append (
40
+ {
41
+ "node" : node ,
42
+ "name" : node ,
43
+ "start" : node ._node_start ,
44
+ "end" : node ._node_end ,
45
+ "level" : level ,
46
+ },
47
+ )
48
+ return timeline
14
49
15
50
16
51
class TemplateProfilerPanel (Panel ):
@@ -19,6 +54,7 @@ class TemplateProfilerPanel(Panel):
19
54
'''
20
55
21
56
template = 'template_profiler_panel/template.html'
57
+ scripts = ["static/js/template_profiler.js" ]
22
58
23
59
def __init__ (self , * args , ** kwargs ):
24
60
self .colors = {}
@@ -49,6 +85,9 @@ def monkey_patch_template_classes(cls):
49
85
else :
50
86
template_classes .append (Jinja2Template )
51
87
88
+ from django .template import Node as DjangoNode
89
+ node_classes = [DjangoNode ]
90
+
52
91
@wrapt .decorator
53
92
def render_wrapper (wrapped , instance , args , kwargs ):
54
93
start = time ()
@@ -63,18 +102,34 @@ def render_wrapper(wrapped, instance, args, kwargs):
63
102
break
64
103
stack_depth += 1
65
104
105
+ timeline = []
106
+ if hasattr (instance , 'nodelist' ):
107
+ timeline = get_nodelist_timeline (instance .nodelist , 0 )
108
+
66
109
template_rendered .send (
67
110
sender = instance .__class__ ,
68
111
instance = instance ,
69
112
start = start ,
70
113
end = end ,
114
+ processing_timeline = timeline ,
71
115
level = stack_depth ,
72
116
)
73
117
return result
74
118
75
119
for template_class in template_classes :
76
120
template_class .render = render_wrapper (template_class .render )
77
121
122
+ @wrapt .decorator
123
+ def render_node_wrapper (wrapped , instance , args , kwargs ):
124
+ instance ._node_start = time ()
125
+ result = wrapped (* args , ** kwargs )
126
+ instance ._node_end = time ()
127
+ return result
128
+
129
+ for node_class in node_classes :
130
+ node_class .render_annotated = render_node_wrapper (
131
+ node_class .render_annotated )
132
+
78
133
cls .have_monkey_patched_template_classes = True
79
134
80
135
@property
@@ -93,7 +148,8 @@ def title(self):
93
148
def _get_color (self , level ):
94
149
return self .colors .setdefault (level , next (self .color_generator ))
95
150
96
- def record (self , instance , start , end , level , ** kwargs ):
151
+ def record (self , instance , start , end , level ,
152
+ processing_timeline , ** kwargs ):
97
153
if not self .enabled :
98
154
return
99
155
@@ -118,6 +174,7 @@ def record(self, instance, start, end, level, **kwargs):
118
174
'end' : end ,
119
175
'time' : (end - start ) * 1000.0 ,
120
176
'level' : level ,
177
+ 'processing_timeline' : processing_timeline ,
121
178
'name' : template_name ,
122
179
'color' : color ,
123
180
})
@@ -132,7 +189,7 @@ def _calc_p(self, part, whole):
132
189
# return the percentage of part or 100% if whole is zero
133
190
return (part / whole ) * 100.0 if whole else 100.0
134
191
135
- def _calc_timeline (self , start , end ):
192
+ def _calc_timeline (self , start , end , processing_timeline ):
136
193
result = {}
137
194
result ['offset_p' ] = self ._calc_p (
138
195
start - self .t_min , self .t_max - self .t_min )
@@ -143,6 +200,43 @@ def _calc_timeline(self, start, end):
143
200
result ['rel_duration_p' ] = self ._calc_p (
144
201
result ['duration_p' ], 100 - result ['offset_p' ])
145
202
203
+ result ['relative_start' ] = (start - self .t_min ) * 1000.0
204
+ result ['relative_end' ] = (end - self .t_min ) * 1000.0
205
+
206
+ result ['processing_timeline' ] = []
207
+ max_level = 0
208
+ for time_item in processing_timeline :
209
+ if 'node' in time_item :
210
+ class_name = time_item ['node' ].__class__ .__name__
211
+ else :
212
+ class_name = time_item ['type' ]
213
+ if class_name not in node_element_colors :
214
+ node_element_colors [class_name ] = next (self .color_generator )
215
+ bg_color = node_element_colors [class_name ]
216
+ if 'node' in time_item :
217
+ position = time_item ['node' ].token .position
218
+ else :
219
+ position = False
220
+ level = time_item ['level' ] if 'level' in time_item else 0
221
+ if level > max_level :
222
+ max_level = level
223
+ result ['processing_timeline' ].append ({
224
+ 'name' : time_item ['name' ],
225
+ 'position' : position ,
226
+ 'relative_start' : (time_item ['start' ] - self .t_min ) * 1000.0 ,
227
+ 'relative_end' : (time_item ['end' ] - self .t_min ) * 1000.0 ,
228
+ 'duration' : (time_item ['end' ] - time_item ['start' ]) * 1000.0 ,
229
+ 'rel_duration_p' : self ._calc_p (
230
+ time_item ['end' ] - time_item ['start' ],
231
+ self .t_max - self .t_min ),
232
+ 'offset_p' : self ._calc_p (
233
+ time_item ['start' ]- self .t_min ,
234
+ self .t_max - self .t_min ),
235
+ 'bg_color' : bg_color ,
236
+ 'level' : level ,
237
+ })
238
+ result ['max_level' ] = max_level
239
+
146
240
return result
147
241
148
242
def process_request (self , request ):
@@ -165,7 +259,9 @@ def process_request(self, request):
165
259
# Calc timelines
166
260
for template in self .templates :
167
261
template .update (
168
- self ._calc_timeline (template ['start' ], template ['end' ]))
262
+ self ._calc_timeline (
263
+ template ['start' ], template ['end' ],
264
+ template ['processing_timeline' ]))
169
265
170
266
self .total = len (self .templates )
171
267
0 commit comments