plain.admin 0.27.1__py3-none-any.whl → 0.28.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/admin/assets/toolbar/toolbar.js +117 -6
- plain/admin/querystats/core.py +0 -1
- plain/admin/querystats/middleware.py +7 -8
- plain/admin/querystats/views.py +26 -3
- plain/admin/templates/querystats/querystats.html +111 -79
- plain/admin/templates/querystats/toolbar.html +4 -4
- plain/admin/templates/toolbar/toolbar.html +213 -54
- plain/admin/toolbar.py +1 -3
- {plain_admin-0.27.1.dist-info → plain_admin-0.28.0.dist-info}/METADATA +1 -1
- {plain_admin-0.27.1.dist-info → plain_admin-0.28.0.dist-info}/RECORD +12 -12
- {plain_admin-0.27.1.dist-info → plain_admin-0.28.0.dist-info}/WHEEL +0 -0
- {plain_admin-0.27.1.dist-info → plain_admin-0.28.0.dist-info}/licenses/LICENSE +0 -0
@@ -32,6 +32,42 @@ var plainToolbar = {
|
|
32
32
|
toggleExpand: function () {
|
33
33
|
this.expanded = !this.expanded;
|
34
34
|
document.querySelector("#plaintoolbar-details").classList.toggle("hidden");
|
35
|
+
localStorage.setItem('plaintoolbar.expanded', this.expanded ? '1' : '0');
|
36
|
+
},
|
37
|
+
expand: function () {
|
38
|
+
this.expanded = true;
|
39
|
+
document.querySelector("#plaintoolbar-details").classList.remove("hidden");
|
40
|
+
localStorage.setItem('plaintoolbar.expanded', '1');
|
41
|
+
},
|
42
|
+
collapse: function () {
|
43
|
+
this.expanded = false;
|
44
|
+
document.querySelector("#plaintoolbar-details").classList.add("hidden");
|
45
|
+
localStorage.setItem('plaintoolbar.expanded', '0');
|
46
|
+
},
|
47
|
+
showTab: function (tabName) {
|
48
|
+
this.expand();
|
49
|
+
|
50
|
+
var toolbar = document.querySelector("#plaintoolbar");
|
51
|
+
var tab = toolbar.querySelector("div[data-toolbar-tab=" + tabName + "]");
|
52
|
+
|
53
|
+
// Hide all children in the tab parent
|
54
|
+
for (var i = 0; i < tab.parentNode.children.length; i++) {
|
55
|
+
var child = tab.parentNode.children[i];
|
56
|
+
if (child !== tab) {
|
57
|
+
child.style.display = "none";
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
tab.style.display = "block";
|
62
|
+
|
63
|
+
toolbar.querySelectorAll("button[data-toolbar-tab]").forEach(function (tab) {
|
64
|
+
if (tab.dataset.toolbarTab === tabName) {
|
65
|
+
tab.setAttribute("data-active", true);
|
66
|
+
} else {
|
67
|
+
tab.removeAttribute("data-active");
|
68
|
+
}
|
69
|
+
});
|
70
|
+
localStorage.setItem('plaintoolbar.tab', tabName);
|
35
71
|
},
|
36
72
|
};
|
37
73
|
|
@@ -40,12 +76,87 @@ if (plainToolbar.shouldHide()) {
|
|
40
76
|
plainToolbar.hide();
|
41
77
|
}
|
42
78
|
|
43
|
-
window.addEventListener("load", function
|
44
|
-
|
45
|
-
|
46
|
-
|
79
|
+
window.addEventListener("load", function() {
|
80
|
+
// Restore expanded/collapsed state
|
81
|
+
var state = localStorage.getItem('plaintoolbar.expanded');
|
82
|
+
if (state === '1') {
|
83
|
+
plainToolbar.expand();
|
84
|
+
// Restore last active tab
|
85
|
+
var lastTab = localStorage.getItem('plaintoolbar.tab');
|
86
|
+
if (lastTab) {
|
87
|
+
plainToolbar.showTab(lastTab);
|
88
|
+
}
|
89
|
+
} else if (state === '0') {
|
90
|
+
plainToolbar.collapse();
|
91
|
+
}
|
92
|
+
var toolbar = document.querySelector("#plaintoolbar");
|
93
|
+
|
94
|
+
toolbar.querySelectorAll("button[data-toolbar-tab]").forEach(function (tab) {
|
95
|
+
tab.addEventListener("click", function () {
|
96
|
+
plainToolbar.showTab(tab.dataset.toolbarTab);
|
97
|
+
});
|
98
|
+
});
|
99
|
+
|
100
|
+
toolbar.querySelectorAll('[data-plaintoolbar-hide]').forEach(function(btn) {
|
101
|
+
btn.addEventListener('click', function() {
|
102
|
+
plainToolbar.hide();
|
47
103
|
});
|
48
|
-
|
49
|
-
|
104
|
+
});
|
105
|
+
|
106
|
+
toolbar.querySelectorAll('[data-plaintoolbar-hideuntil]').forEach(function(btn) {
|
107
|
+
btn.addEventListener('click', function() {
|
108
|
+
console.log("Hiding admin toolbar for 1 hour");
|
109
|
+
plainToolbar.hideUntil(Date.now() + 3600000);
|
110
|
+
});
|
111
|
+
});
|
112
|
+
|
113
|
+
toolbar.querySelectorAll('[data-plaintoolbar-expand]').forEach(function(btn) {
|
114
|
+
btn.addEventListener('click', function() {
|
115
|
+
plainToolbar.toggleExpand();
|
50
116
|
});
|
117
|
+
});
|
118
|
+
|
119
|
+
// Enable manual resize of the expanded toolbar via drag handle
|
120
|
+
var details = document.getElementById('plaintoolbar-details');
|
121
|
+
if (details) {
|
122
|
+
var handle = details.querySelector('[data-resizer]');
|
123
|
+
var content = handle.nextElementSibling;
|
124
|
+
var isDragging = false;
|
125
|
+
var startY = 0;
|
126
|
+
var startHeight = 0;
|
127
|
+
if (handle && content) {
|
128
|
+
// Initial cursor
|
129
|
+
handle.style.cursor = 'grab';
|
130
|
+
// Start dragging
|
131
|
+
handle.addEventListener('mousedown', function(e) {
|
132
|
+
isDragging = true;
|
133
|
+
startY = e.clientY;
|
134
|
+
startHeight = content.offsetHeight;
|
135
|
+
handle.style.cursor = 'grabbing';
|
136
|
+
// Prevent text selection while dragging
|
137
|
+
document.body.style.userSelect = 'none';
|
138
|
+
e.preventDefault();
|
139
|
+
});
|
140
|
+
// Handle dragging
|
141
|
+
document.addEventListener('mousemove', function(e) {
|
142
|
+
if (!isDragging) return;
|
143
|
+
var delta = e.clientY - startY;
|
144
|
+
// Calculate new height: dragging up increases height
|
145
|
+
var newHeight = startHeight - delta;
|
146
|
+
// Clamp between reasonable bounds
|
147
|
+
var minHeight = 50;
|
148
|
+
var maxHeight = window.innerHeight - 100;
|
149
|
+
newHeight = Math.max(minHeight, Math.min(maxHeight, newHeight));
|
150
|
+
content.style.height = newHeight + 'px';
|
151
|
+
});
|
152
|
+
// End dragging
|
153
|
+
document.addEventListener('mouseup', function() {
|
154
|
+
if (isDragging) {
|
155
|
+
isDragging = false;
|
156
|
+
handle.style.cursor = 'grab';
|
157
|
+
document.body.style.userSelect = '';
|
158
|
+
}
|
159
|
+
});
|
160
|
+
}
|
161
|
+
}
|
51
162
|
});
|
plain/admin/querystats/core.py
CHANGED
@@ -55,15 +55,14 @@ class QueryStatsMiddleware:
|
|
55
55
|
if self.should_ignore_request(request):
|
56
56
|
return self.get_response(request)
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
# Have to wrap this first call so it is included in the querystats,
|
63
|
-
# but we don't have to wrap everything else unless we are admin or debug
|
64
|
-
is_admin = self.is_admin_request(request)
|
58
|
+
# Previously we wrapped this in execute_wrapper too,
|
59
|
+
# but now there's a chicken and the egg issue with "querystats" being by the view,
|
60
|
+
# which hasn't been processed yet (on the initial request)
|
61
|
+
is_admin = self.is_admin_request(request)
|
65
62
|
|
66
63
|
if settings.DEBUG or is_admin:
|
64
|
+
tracking = "querystats" in request.session
|
65
|
+
querystats = QueryStats(include_tracebacks=tracking)
|
67
66
|
with connection.execute_wrapper(querystats):
|
68
67
|
response = self.get_response(request)
|
69
68
|
|
@@ -75,7 +74,7 @@ class QueryStatsMiddleware:
|
|
75
74
|
# by using the server timing API which can be parsed client-side
|
76
75
|
response.headers["Server-Timing"] = querystats.as_server_timing()
|
77
76
|
|
78
|
-
if
|
77
|
+
if tracking and querystats.num_queries > 0:
|
79
78
|
request.session["querystats"][request.unique_id] = json.dumps(
|
80
79
|
querystats.as_context_dict(request), cls=QueryStatsJSONEncoder
|
81
80
|
)
|
plain/admin/querystats/views.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import datetime
|
1
2
|
import json
|
2
3
|
|
3
4
|
from plain.auth.views import AuthViewMixin
|
@@ -17,15 +18,32 @@ class QuerystatsView(AuthViewMixin, TemplateView):
|
|
17
18
|
|
18
19
|
super().check_auth()
|
19
20
|
|
21
|
+
def get_response(self):
|
22
|
+
response = super().get_response()
|
23
|
+
# So we can load it in the toolbar
|
24
|
+
response.headers["X-Frame-Options"] = "SAMEORIGIN"
|
25
|
+
return response
|
26
|
+
|
27
|
+
def get(self):
|
28
|
+
# Give an easy out if things get messed up
|
29
|
+
if (
|
30
|
+
"clear" in self.request.query_params
|
31
|
+
and "querystats" in self.request.session
|
32
|
+
):
|
33
|
+
del self.request.session["querystats"]
|
34
|
+
self.request.session.modified = True
|
35
|
+
|
36
|
+
return super().get()
|
37
|
+
|
20
38
|
def get_template_context(self):
|
21
39
|
context = super().get_template_context()
|
22
40
|
|
23
41
|
querystats = self.request.session.get("querystats", {})
|
24
42
|
|
25
|
-
for request_id
|
43
|
+
for request_id in list(querystats.keys()):
|
26
44
|
try:
|
27
|
-
querystats[request_id] = json.loads(
|
28
|
-
except json.JSONDecodeError:
|
45
|
+
querystats[request_id] = json.loads(querystats[request_id])
|
46
|
+
except (json.JSONDecodeError, TypeError):
|
29
47
|
# If decoding fails, remove the entry from the dictionary
|
30
48
|
del querystats[request_id]
|
31
49
|
|
@@ -38,7 +56,12 @@ class QuerystatsView(AuthViewMixin, TemplateView):
|
|
38
56
|
)
|
39
57
|
)
|
40
58
|
|
59
|
+
# Convert the timestamps back to a python datetime object
|
60
|
+
for data in querystats.values():
|
61
|
+
data["timestamp"] = datetime.datetime.fromtimestamp(data["timestamp"])
|
62
|
+
|
41
63
|
context["querystats"] = querystats
|
64
|
+
context["querystats_enabled"] = "querystats" in self.request.session
|
42
65
|
|
43
66
|
return context
|
44
67
|
|
@@ -6,11 +6,17 @@
|
|
6
6
|
<title>Querystats</title>
|
7
7
|
{% tailwind_css %}
|
8
8
|
</head>
|
9
|
-
<body class="
|
9
|
+
<body class="text-stone-300">
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
{% if querystats_enabled %}
|
12
|
+
<div class="flex items-center justify-between border-b border-white/5 px-6 h-14 fixed top-0 left-0 right-0 bg-stone-950 z-10">
|
13
|
+
<!-- <h1 class="text-lg font-semibold">Querystats</h1> -->
|
14
|
+
<div></div>
|
13
15
|
<div class="flex items-center space-x-2">
|
16
|
+
<form method="get" action=".">
|
17
|
+
{{ csrf_input }}
|
18
|
+
<button type="submit" class="px-2 py-px text-sm rounded-sm bg-stone-700 text-stone-300 hover:bg-stone-600 cursor-pointer whitespace-nowrap">Reload</button>
|
19
|
+
</form>
|
14
20
|
<form method="post" action=".">
|
15
21
|
{{ csrf_input }}
|
16
22
|
<input type="hidden" name="querystats_action" value="clear">
|
@@ -23,88 +29,114 @@
|
|
23
29
|
</form>
|
24
30
|
</div>
|
25
31
|
</div>
|
32
|
+
{% endif %}
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
<
|
30
|
-
<
|
31
|
-
|
32
|
-
|
33
|
-
<
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
<pre><code>{{ qs.request.get("headers", {})|pprint }}</code></pre>
|
41
|
-
</details>
|
42
|
-
</div>
|
43
|
-
</div>
|
44
|
-
|
45
|
-
<div class="flex w-full mt-5 overflow-auto rounded-sm">
|
46
|
-
{% for query in qs.queries %}
|
47
|
-
<a href="#query-{{ loop.index }}"
|
48
|
-
{{ loop.cycle('class=\"h-4 bg-amber-300\"', 'class=\"h-4 bg-amber-400\"', 'class="h-4 bg-amber-500"', 'class="h-4 bg-amber-600"')|safe }}
|
49
|
-
title="[{{ query.duration_display }}] {{ query.sql_display }}"
|
50
|
-
style="width: {{ query.duration / qs.total_time * 100 }}%">
|
51
|
-
</a>
|
34
|
+
{% if querystats %}
|
35
|
+
<div class="flex mt-2 h-full">
|
36
|
+
<aside id="sidebar" class="fixed left-0 top-14 bottom-0 w-82 overflow-auto p-4">
|
37
|
+
<ul class="space-y-2">
|
38
|
+
{% for request_id, qs in querystats.items() %}
|
39
|
+
<li>
|
40
|
+
<button data-request-id="{{ request_id }}" class="w-full text-left px-2 py-1 rounded hover:bg-stone-700 cursor-pointer">
|
41
|
+
<span class="text-sm">{{ qs.request.path }}</span>
|
42
|
+
<span class="font-semibold bg-white/5 rounded-sm px-1 py-0.5 text-xs">{{ qs.request.method }}</span>
|
43
|
+
<div class="text-xs text-stone-400">{{ qs.summary }}</div>
|
44
|
+
<div class="text-xs text-stone-500">{{ qs.timestamp|timesince }} ago</div>
|
45
|
+
</button>
|
46
|
+
</li>
|
52
47
|
{% endfor %}
|
53
|
-
</
|
48
|
+
</ul>
|
49
|
+
</aside>
|
54
50
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
<span class="text-red-500"> duplicated {{ query.duplicate_count }} times</span>
|
63
|
-
{% endif %}
|
64
|
-
|
65
|
-
{#
|
66
|
-
<div>many {{ query.many }}</div>
|
67
|
-
<div>result {{ query.result }}</div>
|
68
|
-
#}
|
69
|
-
</div>
|
70
|
-
<code class="font-mono">{{ query.sql }}</code>
|
71
|
-
</summary>
|
72
|
-
<div class="space-y-3 mt-3">
|
73
|
-
<div>
|
74
|
-
<pre><code class="font-mono whitespace-pre-wrap text-zinc-100">{{ query.sql_display }}</code></pre>
|
75
|
-
</div>
|
76
|
-
<div class="text-zinc-400">
|
77
|
-
<span class="font-medium">Parameters</span>
|
78
|
-
<pre><code class="font-mono">{{ query.params|pprint }}</code></pre>
|
79
|
-
</div>
|
80
|
-
<details>
|
81
|
-
<summary>Traceback</summary>
|
82
|
-
<pre><code class="block overflow-x-auto font-mono text-xs">{{ query.tb }}</code></pre>
|
83
|
-
</details>
|
51
|
+
<main id="content" class="flex-1 p-6 overflow-auto ml-82 mt-14">
|
52
|
+
{% for request_id, qs in querystats.items() %}
|
53
|
+
<div class="request-detail" data-request-id="{{ request_id }}" style="display: none;">
|
54
|
+
<div class="flex justify-between">
|
55
|
+
<div>
|
56
|
+
<h2 class="font-medium text-sm"><span class="font-semibold">{{ qs.request.method }}</span> {{ qs.request.path }}</h2>
|
57
|
+
<p class="text-sm text-white/70">{{ qs.summary }}</p>
|
84
58
|
</div>
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
</div>
|
91
|
-
|
92
|
-
{% else %}
|
59
|
+
<div class="text-right">
|
60
|
+
<div class="text-xs text-white/60">Request ID <code>{{ qs.request.unique_id }}</code></div>
|
61
|
+
<div class="text-xs text-white/60"><code>{{ qs.timestamp }}</code></div>
|
62
|
+
</div>
|
63
|
+
</div>
|
93
64
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
{% endif %}
|
104
|
-
</div>
|
65
|
+
<div class="flex w-full mt-3 overflow-auto rounded-sm">
|
66
|
+
{% for query in qs.queries %}
|
67
|
+
<a href="#query-{{ loop.index }}"
|
68
|
+
{{ loop.cycle('class=\"h-2 bg-amber-400\"', 'class=\"h-2 bg-orange-400\"', 'class=\"h-2 bg-yellow-400\"', 'class=\"h-2 bg-amber-600\"')|safe }}
|
69
|
+
title="[{{ query.duration_display }}] {{ query.sql_display }}"
|
70
|
+
style="width: {{ query.duration / qs.total_time * 100 }}%">
|
71
|
+
</a>
|
72
|
+
{% endfor %}
|
73
|
+
</div>
|
105
74
|
|
106
|
-
|
75
|
+
<div class="mt-4 space-y-3 text-xs">
|
76
|
+
{% for query in qs.queries %}
|
77
|
+
<details id="query-{{ loop.index }}" class="p-2 rounded bg-white/5">
|
78
|
+
<summary class="truncate">
|
79
|
+
<div class="float-right px-2 py-px mb-px ml-2 text-xs rounded-full bg-zinc-700">
|
80
|
+
<span>{{ query.duration_display }}</span>
|
81
|
+
{% if query.duplicate_count is defined %}
|
82
|
+
<span class="text-red-500"> duplicated {{ query.duplicate_count }} times</span>
|
83
|
+
{% endif %}
|
84
|
+
</div>
|
85
|
+
<code class="font-mono">{{ query.sql }}</code>
|
86
|
+
</summary>
|
87
|
+
<div class="space-y-3 mt-3">
|
88
|
+
<div>
|
89
|
+
<pre><code class="font-mono whitespace-pre-wrap text-zinc-100">{{ query.sql_display }}</code></pre>
|
90
|
+
</div>
|
91
|
+
<div class="text-zinc-400">
|
92
|
+
<span class="font-medium">Parameters</span>
|
93
|
+
<pre><code class="font-mono">{{ query.params|pprint }}</code></pre>
|
94
|
+
</div>
|
95
|
+
<details>
|
96
|
+
<summary>Traceback</summary>
|
97
|
+
<pre><code class="block overflow-x-auto font-mono text-xs">{{ query.tb }}</code></pre>
|
98
|
+
</details>
|
99
|
+
</div>
|
100
|
+
</details>
|
101
|
+
{% else %}
|
102
|
+
<div>No queries...</div>
|
103
|
+
{% endfor %}
|
104
|
+
</div>
|
105
|
+
</div>
|
106
|
+
{% endfor %}
|
107
|
+
</main>
|
108
|
+
</div>
|
109
|
+
{% elif querystats_enabled %}
|
110
|
+
<div class="text-center text-white/30 py-8">Querystats are enabled but nothing has been recorded yet.</div>
|
111
|
+
{% else %}
|
112
|
+
<div class="text-center py-8">
|
113
|
+
<div class="text-white/30">Querystats are disabled.</div>
|
114
|
+
<form method="post" action=".">
|
115
|
+
{{ csrf_input }}
|
116
|
+
<input type="hidden" name="querystats_action" value="enable">
|
117
|
+
<button type="submit" class="mt-2 px-2 py-px text-sm rounded-sm bg-stone-700 text-stone-300 hover:bg-stone-600 cursor-pointer whitespace-nowrap">Enable</button>
|
118
|
+
</form>
|
107
119
|
</div>
|
120
|
+
{% endif %}
|
121
|
+
|
122
|
+
<script>
|
123
|
+
document.addEventListener('DOMContentLoaded', function() {
|
124
|
+
const buttons = document.querySelectorAll('#sidebar [data-request-id]');
|
125
|
+
const details = document.querySelectorAll('#content .request-detail');
|
126
|
+
buttons.forEach(function(btn) {
|
127
|
+
btn.addEventListener('click', function(e) {
|
128
|
+
e.preventDefault();
|
129
|
+
const id = this.getAttribute('data-request-id');
|
130
|
+
details.forEach(div => div.style.display = 'none');
|
131
|
+
const sel = document.querySelector('#content .request-detail[data-request-id="' + id + '"]');
|
132
|
+
if (sel) sel.style.display = 'block';
|
133
|
+
buttons.forEach(b => b.classList.remove('bg-stone-700', 'text-white'));
|
134
|
+
this.classList.add('bg-stone-700', 'text-white');
|
135
|
+
});
|
136
|
+
});
|
137
|
+
if (buttons.length > 0) buttons[0].click();
|
138
|
+
});
|
139
|
+
</script>
|
108
140
|
|
109
|
-
</body>
|
141
|
+
</body>
|
110
142
|
</html>
|
@@ -1,25 +1,25 @@
|
|
1
1
|
<div data-querystats class="relative group/querystats" style="display: none;">
|
2
2
|
{% if "querystats" in request.session %}
|
3
|
-
<
|
3
|
+
<button data-toolbar-tab="querystats" class="inline-flex items-center cursor-pointer text-xs rounded-full px-2 py-px bg-white/20 text-white/80 whitespace-nowrap">
|
4
4
|
<span class="relative inline-flex size-2 mr-2">
|
5
5
|
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
|
6
6
|
<span class="relative inline-flex size-2 rounded-full bg-green-500"></span>
|
7
7
|
</span>
|
8
8
|
<span data-querystats-summary></span>
|
9
|
-
</
|
9
|
+
</button>
|
10
10
|
{% else %}
|
11
11
|
<form action="{{ url('admin:querystats:querystats') }}" method="post">
|
12
12
|
{{ csrf_input }}
|
13
13
|
<input type="hidden" name="redirect_url" value="{{ request.get_full_path() }}">
|
14
14
|
<input type="hidden" name="querystats_action" value="enable">
|
15
|
-
<button type="submit" class="cursor-pointer text-xs rounded-full px-2 py-px bg-
|
15
|
+
<button type="submit" class="cursor-pointer text-xs rounded-full px-2 py-px bg-white/20 text-white/80 whitespace-nowrap">
|
16
16
|
<span class="rounded-full bg-zinc-500 w-2 h-2 inline-block mr-1"></span>
|
17
17
|
<span data-querystats-summary></span>
|
18
18
|
</button>
|
19
19
|
</form>
|
20
20
|
{% endif %}
|
21
21
|
|
22
|
-
<div data-querystats-list style="display: none;" class="absolute z-50
|
22
|
+
<div data-querystats-list style="display: none;" class="absolute z-50 hidden -translate-y-full right-0 -top-1 group-hover/querystats:block">
|
23
23
|
<div class="p-2 text-xs border rounded shadow-md bg-zinc-900 border-zinc-700"><table><tbody></tbody></table></div>
|
24
24
|
</div>
|
25
25
|
<script async defer>
|
@@ -1,10 +1,199 @@
|
|
1
1
|
{% if toolbar.should_render() %}
|
2
2
|
{% set exception=toolbar.request_exception() %}
|
3
|
-
<script src="{{ asset('toolbar/toolbar.js') }}"></script>
|
4
|
-
<div id="plaintoolbar" class="print:hidden text-
|
5
|
-
|
3
|
+
<script defer src="{{ asset('toolbar/toolbar.js') }}"></script>
|
4
|
+
<div id="plaintoolbar" class="print:hidden text-stone-300 fixed bottom-0 w-full z-30 hidden sm:flex sm:flex-col">
|
5
|
+
|
6
|
+
<div id="plaintoolbar-details" class="{% if not exception %}hidden{% endif %} relative text-sm border-white/5 shadow-xl border-t inset-shadow-xs inset-shadow-stone-800 rounded-t-xl bg-stone-950/95 backdrop-blur-sm">
|
7
|
+
|
8
|
+
<div class="flex items-center border-b border-white/5 px-2 justify-between">
|
9
|
+
<div class="flex items-center">
|
10
|
+
<button data-active data-toolbar-tab="request" class="data-active:border-yellow-500 px-4 py-2.5 -mb-px cursor-pointer border-b border-transparent hover:border-yellow-600" type="button">Request</button>
|
11
|
+
{% if request.session is defined %}
|
12
|
+
<button data-toolbar-tab="session" class="data-active:border-yellow-500 px-4 py-2.5 -mb-px cursor-pointer border-b border-transparent hover:border-yellow-600" type="button">Session</button>
|
13
|
+
{% endif %}
|
14
|
+
<button data-toolbar-tab="querystats" class="data-active:border-yellow-500 px-4 py-2.5 -mb-px cursor-pointer border-b border-transparent hover:border-yellow-600" type="button">Querystats</button>
|
15
|
+
{% if toolbar.metadata %}
|
16
|
+
<button data-toolbar-tab="metadata" class="data-active:border-yellow-500 px-4 py-2.5 -mb-px cursor-pointer border-b border-transparent hover:border-yellow-600" type="button">Metadata</button>
|
17
|
+
{% endif %}
|
18
|
+
</div>
|
19
|
+
<div class="px-4 flex items-center space-x-4">
|
20
|
+
<button title="Hide toolbar for 1 hour" class="cursor-pointer hover:text-white text-white/50" type="button" data-plaintoolbar-hideuntil>
|
21
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-4 h-4" viewBox="0 0 16 16">
|
22
|
+
<path d="M8.515 1.019A7 7 0 0 0 8 1V0a8 8 0 0 1 .589.022zm2.004.45a7 7 0 0 0-.985-.299l.219-.976q.576.129 1.126.342zm1.37.71a7 7 0 0 0-.439-.27l.493-.87a8 8 0 0 1 .979.654l-.615.789a7 7 0 0 0-.418-.302zm1.834 1.79a7 7 0 0 0-.653-.796l.724-.69q.406.429.747.91zm.744 1.352a7 7 0 0 0-.214-.468l.893-.45a8 8 0 0 1 .45 1.088l-.95.313a7 7 0 0 0-.179-.483m.53 2.507a7 7 0 0 0-.1-1.025l.985-.17q.1.58.116 1.17zm-.131 1.538q.05-.254.081-.51l.993.123a8 8 0 0 1-.23 1.155l-.964-.267q.069-.247.12-.501m-.952 2.379q.276-.436.486-.908l.914.405q-.24.54-.555 1.038zm-.964 1.205q.183-.183.35-.378l.758.653a8 8 0 0 1-.401.432z"/>
|
23
|
+
<path d="M8 1a7 7 0 1 0 4.95 11.95l.707.707A8.001 8.001 0 1 1 8 0z"/>
|
24
|
+
<path d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5"/>
|
25
|
+
</svg>
|
26
|
+
</button>
|
27
|
+
<button class="cursor-pointer hover:text-white text-white/50" type="button" data-plaintoolbar-expand>
|
28
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-4 h-4" viewBox="0 0 16 16">
|
29
|
+
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708"/>
|
30
|
+
</svg>
|
31
|
+
</button>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div data-resizer class="cursor-grab w-20 h-1.5 top-1 bg-white/15 rounded-full absolute top-0 left-1/2 -translate-x-1/2"></div>
|
36
|
+
|
37
|
+
<div class="overflow-auto h-[30vh] flex flex-col">
|
38
|
+
<div data-toolbar-tab="request" class="px-6 py-4">
|
39
|
+
|
40
|
+
{% if exception %}
|
41
|
+
<div class="p-2 mb-5 border-amber-500 border rounded">
|
42
|
+
<div class="text-amber-500 text-lg flex justify-between items-center">
|
43
|
+
<div>
|
44
|
+
<span class="font-bold">Exception</span>
|
45
|
+
{{ exception }}
|
46
|
+
</div>
|
47
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-5 h-5 bi bi-exclamation-triangle-fill" viewBox="0 0 16 16">
|
48
|
+
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
|
49
|
+
</svg>
|
50
|
+
</div>
|
51
|
+
<div class="text-amber-400 text-xs mt-3 bg-white/5 p-2 rounded overflow-auto">
|
52
|
+
<pre><code>{{ exception._traceback_string }}</code></pre>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
{% endif %}
|
56
|
+
|
57
|
+
<dl class="text-sm grid grid-cols-1 sm:grid-cols-[max-content_1fr] sm:gap-y-2 gap-x-8">
|
58
|
+
<dt>Request ID</dt>
|
59
|
+
<dd class="text-sm text-white/50">{{ request.unique_id }}</dd>
|
60
|
+
|
61
|
+
<dt>Query params</dt>
|
62
|
+
<dd class="text-sm text-white/50">{{ request.query_params }}</dd>
|
63
|
+
|
64
|
+
<dt>Method</dt>
|
65
|
+
<dd class="text-sm text-white/50">{{ request.method }}</dd>
|
66
|
+
|
67
|
+
{% if request.resolver_match %}
|
68
|
+
<dt>View</dt>
|
69
|
+
<dd class="text-sm text-white/50">{{ request.resolver_match.view.view_class|pprint }}</dd>
|
70
|
+
|
71
|
+
<dt>URL pattern</dt>
|
72
|
+
<dd class="text-sm text-white/50">
|
73
|
+
<pre><code>{{ request.resolver_match.route }}</code></pre>
|
74
|
+
</dd>
|
75
|
+
|
76
|
+
<dt>URL name</dt>
|
77
|
+
<dd class="text-sm text-white/50">
|
78
|
+
<pre><code>{{ request.resolver_match.namespaced_url_name }}</code></pre>
|
79
|
+
</dd>
|
80
|
+
|
81
|
+
<dt>URL args</dt>
|
82
|
+
<dd class="text-sm text-white/50">
|
83
|
+
<pre><code>{{ request.resolver_match.args }}</code></pre>
|
84
|
+
</dd>
|
85
|
+
|
86
|
+
<dt>URL kwargs</dt>
|
87
|
+
<dd class="text-sm text-white/50">
|
88
|
+
<pre><code>{{ request.resolver_match.kwargs }}</code></pre>
|
89
|
+
</dd>
|
90
|
+
{% endif %}
|
91
|
+
|
92
|
+
{% if template_names is defined %}
|
93
|
+
<dt>Template names</dt>
|
94
|
+
<dd class="text-sm text-white/50">
|
95
|
+
<pre><code>{{ template_names }}</code></pre>
|
96
|
+
</dd>
|
97
|
+
{% endif %}
|
98
|
+
|
99
|
+
{% if object|default(false) %}
|
100
|
+
<dt>Primary object</dt>
|
101
|
+
<dd class="text-sm text-white/50 inline-flex items-center" title="PK: {{ object.pk|default('unknown') }}">
|
102
|
+
<pre><code>{{ object.__repr__() }}</code></pre>
|
103
|
+
{% if object|get_admin_model_detail_url %}
|
104
|
+
<a class="ml-2 inline-flex items-center p-1 text-blue-500 hover:text-blue-400" href="{{ object|get_admin_model_detail_url }}">
|
105
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-3 h-3 bi bi-database-fill" viewBox="0 0 16 16">
|
106
|
+
<path d="M3.904 1.777C4.978 1.289 6.427 1 8 1s3.022.289 4.096.777C13.125 2.245 14 2.993 14 4s-.875 1.755-1.904 2.223C11.022 6.711 9.573 7 8 7s-3.022-.289-4.096-.777C2.875 5.755 2 5.007 2 4s.875-1.755 1.904-2.223"/>
|
107
|
+
<path d="M2 6.161V7c0 1.007.875 1.755 1.904 2.223C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777C13.125 8.755 14 8.007 14 7v-.839c-.457.432-1.004.751-1.49.972C11.278 7.693 9.682 8 8 8s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"/>
|
108
|
+
<path d="M2 9.161V10c0 1.007.875 1.755 1.904 2.223C4.978 12.711 6.427 13 8 13s3.022-.289 4.096-.777C13.125 11.755 14 11.007 14 10v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"/>
|
109
|
+
<path d="M2 12.161V13c0 1.007.875 1.755 1.904 2.223C4.978 15.711 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"/>
|
110
|
+
</svg>
|
111
|
+
</a>
|
112
|
+
{% endif %}
|
113
|
+
</dd>
|
114
|
+
{% endif %}
|
115
|
+
|
116
|
+
</dl>
|
117
|
+
</div>
|
118
|
+
|
119
|
+
{% if request.session is defined %}
|
120
|
+
<div data-toolbar-tab="session" style="display: none;">
|
121
|
+
<div class="px-6 py-4">
|
122
|
+
<dl class="text-sm grid grid-cols-1 sm:grid-cols-[max-content_1fr] sm:gap-y-2 gap-x-8">
|
123
|
+
<dt>Session ID</dt>
|
124
|
+
<dd class="text-sm text-white/50">{{ request.session.session_key }}</dd>
|
125
|
+
|
126
|
+
{% for k, v in request.session.items() %}
|
127
|
+
<dt>session["{{ k }}"]</dt>
|
128
|
+
<dd class="text-sm text-white/50 max-h-32 overflow-auto">
|
129
|
+
{% if v is iterable and not v|string %}
|
130
|
+
<pre><code>{{ v }}</code></pre>
|
131
|
+
{% else %}
|
132
|
+
{{ v }}
|
133
|
+
{% endif %}
|
134
|
+
</dd>
|
135
|
+
{% endfor %}
|
136
|
+
</dl>
|
137
|
+
</div>
|
138
|
+
</div>
|
139
|
+
{% endif %}
|
140
|
+
|
141
|
+
<div data-toolbar-tab="querystats" style="display: none;" class="h-full">
|
142
|
+
<div id="querystats-container" class="h-full">
|
143
|
+
<div class="px-6 py-4 text-center">
|
144
|
+
<p>Loading querystats...</p>
|
145
|
+
</div>
|
146
|
+
</div>
|
147
|
+
<script>
|
148
|
+
(function() {
|
149
|
+
var container = document.getElementById('querystats-container');
|
150
|
+
var loaded = false;
|
151
|
+
var parent = container.parentNode;
|
152
|
+
var observer = new IntersectionObserver(function(entries) {
|
153
|
+
entries.forEach(function(entry) {
|
154
|
+
if (entry.isIntersecting && !loaded) {
|
155
|
+
loaded = true;
|
156
|
+
var iframe = document.createElement('iframe');
|
157
|
+
iframe.src = "{{ url('admin:querystats:querystats') }}";
|
158
|
+
iframe.frameBorder = "0";
|
159
|
+
iframe.style.width = "100%";
|
160
|
+
iframe.style.height = "100%";
|
161
|
+
container.innerHTML = '';
|
162
|
+
container.appendChild(iframe);
|
163
|
+
observer.disconnect();
|
164
|
+
}
|
165
|
+
});
|
166
|
+
}, { root: parent, threshold: 0 });
|
167
|
+
observer.observe(container);
|
168
|
+
})();
|
169
|
+
</script>
|
170
|
+
</div>
|
171
|
+
|
172
|
+
{% if toolbar.metadata %}
|
173
|
+
<div data-toolbar-tab="metadata" style="display: none;">
|
174
|
+
<div class="px-6 py-4">
|
175
|
+
<dl class="text-sm grid grid-cols-1 sm:grid-cols-[max-content_1fr] sm:gap-y-2 gap-x-8">
|
176
|
+
{% for k, v in toolbar.metadata.items() %}
|
177
|
+
<dt>{{ k }}</dt>
|
178
|
+
<dd class="text-sm text-white/50 max-h-32 overflow-auto">
|
179
|
+
{% if v is iterable and not v|string %}
|
180
|
+
<pre><code>{{ v }}</code></pre>
|
181
|
+
{% else %}
|
182
|
+
{{ v }}
|
183
|
+
{% endif %}
|
184
|
+
</dd>
|
185
|
+
{% endfor %}
|
186
|
+
</dl>
|
187
|
+
</div>
|
188
|
+
</div>
|
189
|
+
{% endif %}
|
190
|
+
</div>
|
191
|
+
|
192
|
+
</div>
|
193
|
+
|
194
|
+
<div class="flex px-3 text-xs border-t border-white/5 space-x-4 py-2 bg-stone-950 shadow-xl">
|
6
195
|
<div class="flex items-center">
|
7
|
-
<code class="ml-1.5
|
196
|
+
<code class="ml-1.5 bg-white/10 px-1.5 rounded-sm whitespace-nowrap text-mono">{{ toolbar.version }}</code>
|
8
197
|
|
9
198
|
{% if request.impersonator is defined %}
|
10
199
|
<div class="flex items-center ml-1 font-light">
|
@@ -18,69 +207,39 @@
|
|
18
207
|
</div>
|
19
208
|
{% endif %}
|
20
209
|
</div>
|
210
|
+
<button type="button" data-plaintoolbar-expand class="flex-grow cursor-pointer"></button>
|
21
211
|
<div class="flex items-center space-x-4">
|
22
212
|
{% include "querystats/toolbar.html" %}
|
23
213
|
|
24
214
|
<div class="flex items-center space-x-3 transition-all">
|
25
|
-
|
26
|
-
{% if
|
27
|
-
<
|
28
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-
|
29
|
-
<path d="
|
30
|
-
<path d="M2 6.161V7c0 1.007.875 1.755 1.904 2.223C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777C13.125 8.755 14 8.007 14 7v-.839c-.457.432-1.004.751-1.49.972C11.278 7.693 9.682 8 8 8s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"/>
|
31
|
-
<path d="M2 9.161V10c0 1.007.875 1.755 1.904 2.223C4.978 12.711 6.427 13 8 13s3.022-.289 4.096-.777C13.125 11.755 14 11.007 14 10v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"/>
|
32
|
-
<path d="M2 12.161V13c0 1.007.875 1.755 1.904 2.223C4.978 15.711 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972"/>
|
215
|
+
|
216
|
+
{% if exception %}
|
217
|
+
<button class="cursor-pointer text-amber-500 hover:text-amber-400" type="button" data-toolbar-tab="request">
|
218
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-4 h-4 bi bi-exclamation-triangle-fill" viewBox="0 0 16 16">
|
219
|
+
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
|
33
220
|
</svg>
|
34
|
-
</
|
221
|
+
</button>
|
35
222
|
{% endif %}
|
223
|
+
|
224
|
+
<a href="{{ url('admin:index') }}" class="hover:underline">Admin</a>
|
225
|
+
|
36
226
|
{% include "toolbar/links.html" ignore missing %}
|
37
|
-
|
227
|
+
|
228
|
+
<button data-plaintoolbar-expand class="hover:text-white cursor-pointer" type="button" title="Expand toolbar">
|
38
229
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-4 h-4" viewBox="0 0 16 16">
|
39
|
-
<path fill-rule="evenodd" d="
|
230
|
+
<path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708z"/>
|
231
|
+
</svg>
|
232
|
+
</button>
|
233
|
+
|
234
|
+
<button data-plaintoolbar-hide class="hover:text-red-500 cursor-pointer" type="button" title="Hide toolbar">
|
235
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x w-4 h-4" viewBox="0 0 16 16">
|
236
|
+
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
40
237
|
</svg>
|
41
238
|
</button>
|
239
|
+
|
42
240
|
</div>
|
43
241
|
</div>
|
44
242
|
</div>
|
45
|
-
<div id="plaintoolbar-details" class="{% if not exception %}hidden{% endif %} p-4 overflow-auto text-sm space-y-2">
|
46
|
-
|
47
|
-
{% if exception %}
|
48
|
-
<div class="p-2 border-amber-500 border rounded">
|
49
|
-
<div class="text-amber-500 text-lg flex justify-between items-center">
|
50
|
-
<div>
|
51
|
-
<span class="font-bold">Exception</span>
|
52
|
-
{{ exception }}
|
53
|
-
</div>
|
54
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="w-5 h-5 bi bi-exclamation-triangle-fill" viewBox="0 0 16 16">
|
55
|
-
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
|
56
|
-
</svg>
|
57
|
-
</div>
|
58
|
-
<div class="text-amber-400 text-xs mt-3 bg-white/5 p-2 rounded overflow-auto">
|
59
|
-
<pre><code>{{ exception._traceback_string }}</code></pre>
|
60
|
-
</div>
|
61
|
-
</div>
|
62
|
-
{% endif %}
|
63
|
-
|
64
|
-
<table>
|
65
|
-
<tbody>
|
66
|
-
{% for k, v in toolbar.metadata.items() %}
|
67
|
-
<tr>
|
68
|
-
<td class="pr-2 font-medium whitespace-nowrap">{{ k }}</td>
|
69
|
-
<td class="whitespace-nowrap">{{ v }}</td>
|
70
|
-
</tr>
|
71
|
-
{% endfor %}
|
72
|
-
</tbody>
|
73
|
-
</table>
|
74
|
-
|
75
|
-
{% if object|default(false) %}
|
76
|
-
<div class="font-mono" title="PK: {{ object.pk|default('unknown') }}">
|
77
|
-
{{ object.__repr__() }}
|
78
|
-
</div>
|
79
|
-
{% endif %}
|
80
243
|
|
81
|
-
<button data-plaintoolbar-hide class="hover:text-red-500" type="button">
|
82
|
-
Hide toolbar for 1 hour
|
83
|
-
</button>
|
84
|
-
</div>
|
85
244
|
</div>
|
86
245
|
{% endif %}
|
plain/admin/toolbar.py
CHANGED
@@ -5,7 +5,7 @@ plain/admin/dates.py,sha256=EEhcQhHt3-k6kE9yvPdH5X6EecmUQ259xywbDBec3Dg,10253
|
|
5
5
|
plain/admin/default_settings.py,sha256=S22r8JtwY-ArlNO4waBOrnRfb2qPbUQ5VSP6niJRzZw,145
|
6
6
|
plain/admin/middleware.py,sha256=k3yP1o3CzvLiZZSoxqq-DvAZlp4sICRauaT-kD3FJKM,398
|
7
7
|
plain/admin/templates.py,sha256=0xgMQmJEbh5U45ZlN2f15Xs42Y2A_lSS-_wdMp1BeD4,854
|
8
|
-
plain/admin/toolbar.py,sha256=
|
8
|
+
plain/admin/toolbar.py,sha256=GrIy_Kdm1KVRkOq4fKmzCfmTcXzoe6vcC3LBG4hEFoM,925
|
9
9
|
plain/admin/urls.py,sha256=sriMi2RCkcrkjCX3CIIP1-Qzs_zDm2pxXeOw28vc7Y4,1301
|
10
10
|
plain/admin/assets/admin/admin.css,sha256=Gu6GpRymJriFitAaKh_P5Sm8ZKrX9jdw6Uflxgypff8,2786
|
11
11
|
plain/admin/assets/admin/admin.js,sha256=2-o4g6EtiiF2HGZIKfnVkC8shXFjY1xFSehDlR9550s,2852
|
@@ -14,7 +14,7 @@ plain/admin/assets/admin/jquery-3.6.1.slim.min.js,sha256=W2eb4M1jdKpuZ_-_KnDgqI9
|
|
14
14
|
plain/admin/assets/admin/list.js,sha256=_DPneRvk3VSzjVzfEaxyif4vLD75sCWz7bkHYp89uL8,1826
|
15
15
|
plain/admin/assets/admin/popper.min.js,sha256=SgCxkjQZdrt2puqn62YUu9hknpCBGBEAy9uhQ9PPZaI,20083
|
16
16
|
plain/admin/assets/admin/tippy-bundle.umd.min.js,sha256=oVWBpeGTKMG_iBWGkQF02JnGIMFPYuFqTjUWeJY3pZ0,25668
|
17
|
-
plain/admin/assets/toolbar/toolbar.js,sha256=
|
17
|
+
plain/admin/assets/toolbar/toolbar.js,sha256=lT63RDzAxHn95IaVl7Wx4ycAaOUjS_IVeCCYiK3iRkc,5265
|
18
18
|
plain/admin/cards/__init__.py,sha256=8NfWrguyJRriJFUc3_QeGaDILhgeU3d1aXktzIuAR1E,172
|
19
19
|
plain/admin/cards/base.py,sha256=ESYY0tX3OossyZi9ubCrLxwxUZ0Z3snZUCVmLvTIg-U,2247
|
20
20
|
plain/admin/cards/charts.py,sha256=uUNO_uN_GVdkwYNSTx1bt1j2L-89rEyIg0vDaxP-9NE,4929
|
@@ -29,10 +29,10 @@ plain/admin/impersonate/urls.py,sha256=s8bwi8qPueKCCYcLW75p-hPFkBKhm2AMi6AQKQcZs
|
|
29
29
|
plain/admin/impersonate/views.py,sha256=PzVmzhlS0LmbHgxdLhc2G27ltvk3zgmO-3QWNAcD-l0,807
|
30
30
|
plain/admin/querystats/README.md,sha256=ONscu4dQOVe20CPHFyI8vR8iL2kvo3cOM8iwVO-lDyM,4821
|
31
31
|
plain/admin/querystats/__init__.py,sha256=VmP1aQ5Pviq4Z3izCB8G9g0Weq-2SYR88UFNtwqAPpo,81
|
32
|
-
plain/admin/querystats/core.py,sha256=
|
33
|
-
plain/admin/querystats/middleware.py,sha256=
|
32
|
+
plain/admin/querystats/core.py,sha256=g7Iz8i4J7CUFL_HqTneoBKpJcVUxsAzLCPlqdX2cKTU,4430
|
33
|
+
plain/admin/querystats/middleware.py,sha256=4Y3AE7CSsNkCgvQ0xtp6KkKxB4yc3eDjOcJBH479hFw,3478
|
34
34
|
plain/admin/querystats/urls.py,sha256=H8wMpqKBnXqA8ZsdwdxTKQguNYJ0JsMRqqMunccBm2I,198
|
35
|
-
plain/admin/querystats/views.py,sha256=
|
35
|
+
plain/admin/querystats/views.py,sha256=noSElnwo3DhRL9a77rwaDDZ16HMLRqeUf4MsW4gHjqU,2582
|
36
36
|
plain/admin/templates/admin/base.html,sha256=npIwsxHa7Gf5FYTmsXfgSor2bbYIWwTSk0-UFmnqmfE,8481
|
37
37
|
plain/admin/templates/admin/delete.html,sha256=lNuU2G-BR6TH6NUmh7VcvjnEuFeI84rwwk_oO1jkUq0,431
|
38
38
|
plain/admin/templates/admin/detail.html,sha256=AizpXs6HguFzwbk7JDbH8poJB5dM2CaVVaQ4FThAHaw,730
|
@@ -66,9 +66,9 @@ plain/admin/templates/elements/admin/SelectField.html,sha256=P2-vXifOs2-ie20AgLy
|
|
66
66
|
plain/admin/templates/elements/admin/Submit.html,sha256=1Lgn3Du9rXplbM3V12z2JckSaiWPlPGLP48xIZ887AA,150
|
67
67
|
plain/admin/templates/elements/admin/Textarea.html,sha256=nCSaGa9t5A5Oj6ZPWW-jSJiGqI1NLPahhXJblq62QME,363
|
68
68
|
plain/admin/templates/elements/admin/TextareaField.html,sha256=4IOJapBNEfhUpMkkLW-gliIefZCEMn5aKyW4QagfcNw,223
|
69
|
-
plain/admin/templates/querystats/querystats.html,sha256=
|
70
|
-
plain/admin/templates/querystats/toolbar.html,sha256=
|
71
|
-
plain/admin/templates/toolbar/toolbar.html,sha256=
|
69
|
+
plain/admin/templates/querystats/querystats.html,sha256=ie6_z5f2Vd0Z6EIwzlu7nE-QzjlcVEdjOeM7hsE5G2A,7385
|
70
|
+
plain/admin/templates/querystats/toolbar.html,sha256=mvERfzU-CdzIBS7aQNosazSeCe9iyDOsswCGFxssdXc,4319
|
71
|
+
plain/admin/templates/toolbar/toolbar.html,sha256=AKISfakxIrjOTEmPsDI58hXpNthQQjSuSe444bSddIA,15561
|
72
72
|
plain/admin/views/__init__.py,sha256=nF6AENZ3Xxyi08OTRrF6e-HYBkZSFj7XBK2mVzMYqN4,846
|
73
73
|
plain/admin/views/base.py,sha256=S1oaMUXnMOwRozbn2K-tk9tL4BMimemfMagZD9QxrJw,3512
|
74
74
|
plain/admin/views/models.py,sha256=DAv7YzeSyQHLLAVdUhSPCkmx2B10g5ksAjHm2jrgQfw,5973
|
@@ -76,7 +76,7 @@ plain/admin/views/objects.py,sha256=eKL8A2B1ZMgTrCbTXnh6vCeju_HObxwetn_xc1vYlfY,
|
|
76
76
|
plain/admin/views/registry.py,sha256=Lxib4YSQCMHb_zACnLKymJakV8jCZPWYll7J8-aV9Xw,3712
|
77
77
|
plain/admin/views/types.py,sha256=ONMMdUoapgMoUVYgSIe-4YCdfvaVMQ4jgPWYiMo0pDk,178
|
78
78
|
plain/admin/views/viewsets.py,sha256=dqMlQ6kLn9iqd9BwBWAZT1S271wH1FdfM5HXbOgBMEw,1655
|
79
|
-
plain_admin-0.
|
80
|
-
plain_admin-0.
|
81
|
-
plain_admin-0.
|
82
|
-
plain_admin-0.
|
79
|
+
plain_admin-0.28.0.dist-info/METADATA,sha256=svNc-LixFxD0pxyDsX5-lQGmwQjYtFvk8c4GwfjHjHo,4237
|
80
|
+
plain_admin-0.28.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
81
|
+
plain_admin-0.28.0.dist-info/licenses/LICENSE,sha256=cvKM3OlqHx3ijD6e34zsSUkPvzl-ya3Dd63A6EHL94U,1500
|
82
|
+
plain_admin-0.28.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|