plain.admin 0.14.1__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.
Files changed (80) hide show
  1. plain/admin/README.md +260 -0
  2. plain/admin/__init__.py +5 -0
  3. plain/admin/assets/admin/admin.css +108 -0
  4. plain/admin/assets/admin/admin.js +79 -0
  5. plain/admin/assets/admin/chart.js +19 -0
  6. plain/admin/assets/admin/jquery-3.6.1.slim.min.js +2 -0
  7. plain/admin/assets/admin/list.js +57 -0
  8. plain/admin/assets/admin/popper.min.js +5 -0
  9. plain/admin/assets/admin/tippy-bundle.umd.min.js +1 -0
  10. plain/admin/assets/toolbar/toolbar.js +51 -0
  11. plain/admin/cards/__init__.py +10 -0
  12. plain/admin/cards/base.py +86 -0
  13. plain/admin/cards/charts.py +153 -0
  14. plain/admin/cards/tables.py +26 -0
  15. plain/admin/config.py +21 -0
  16. plain/admin/dates.py +254 -0
  17. plain/admin/default_settings.py +4 -0
  18. plain/admin/impersonate/README.md +44 -0
  19. plain/admin/impersonate/__init__.py +3 -0
  20. plain/admin/impersonate/middleware.py +38 -0
  21. plain/admin/impersonate/models.py +0 -0
  22. plain/admin/impersonate/permissions.py +16 -0
  23. plain/admin/impersonate/settings.py +8 -0
  24. plain/admin/impersonate/urls.py +10 -0
  25. plain/admin/impersonate/views.py +23 -0
  26. plain/admin/middleware.py +12 -0
  27. plain/admin/querystats/README.md +191 -0
  28. plain/admin/querystats/__init__.py +3 -0
  29. plain/admin/querystats/core.py +153 -0
  30. plain/admin/querystats/middleware.py +99 -0
  31. plain/admin/querystats/urls.py +9 -0
  32. plain/admin/querystats/views.py +27 -0
  33. plain/admin/templates/admin/base.html +160 -0
  34. plain/admin/templates/admin/cards/base.html +30 -0
  35. plain/admin/templates/admin/cards/card.html +17 -0
  36. plain/admin/templates/admin/cards/chart.html +25 -0
  37. plain/admin/templates/admin/cards/table.html +35 -0
  38. plain/admin/templates/admin/delete.html +17 -0
  39. plain/admin/templates/admin/detail.html +24 -0
  40. plain/admin/templates/admin/form.html +13 -0
  41. plain/admin/templates/admin/index.html +5 -0
  42. plain/admin/templates/admin/list.html +194 -0
  43. plain/admin/templates/admin/page.html +3 -0
  44. plain/admin/templates/admin/search.html +27 -0
  45. plain/admin/templates/admin/values/UUID.html +1 -0
  46. plain/admin/templates/admin/values/bool.html +9 -0
  47. plain/admin/templates/admin/values/datetime.html +1 -0
  48. plain/admin/templates/admin/values/default.html +5 -0
  49. plain/admin/templates/admin/values/dict.html +1 -0
  50. plain/admin/templates/admin/values/get_display.html +1 -0
  51. plain/admin/templates/admin/values/img.html +4 -0
  52. plain/admin/templates/admin/values/list.html +1 -0
  53. plain/admin/templates/admin/values/model.html +15 -0
  54. plain/admin/templates/admin/values/queryset.html +7 -0
  55. plain/admin/templates/elements/admin/Checkbox.html +8 -0
  56. plain/admin/templates/elements/admin/CheckboxField.html +7 -0
  57. plain/admin/templates/elements/admin/FieldErrors.html +5 -0
  58. plain/admin/templates/elements/admin/Input.html +9 -0
  59. plain/admin/templates/elements/admin/InputField.html +5 -0
  60. plain/admin/templates/elements/admin/Label.html +3 -0
  61. plain/admin/templates/elements/admin/Select.html +11 -0
  62. plain/admin/templates/elements/admin/SelectField.html +5 -0
  63. plain/admin/templates/elements/admin/Submit.html +6 -0
  64. plain/admin/templates/querystats/querystats.html +78 -0
  65. plain/admin/templates/querystats/toolbar.html +79 -0
  66. plain/admin/templates/toolbar/toolbar.html +91 -0
  67. plain/admin/templates.py +25 -0
  68. plain/admin/toolbar.py +36 -0
  69. plain/admin/urls.py +45 -0
  70. plain/admin/views/__init__.py +41 -0
  71. plain/admin/views/base.py +140 -0
  72. plain/admin/views/models.py +254 -0
  73. plain/admin/views/objects.py +399 -0
  74. plain/admin/views/registry.py +117 -0
  75. plain/admin/views/types.py +6 -0
  76. plain/admin/views/viewsets.py +54 -0
  77. plain_admin-0.14.1.dist-info/METADATA +275 -0
  78. plain_admin-0.14.1.dist-info/RECORD +80 -0
  79. plain_admin-0.14.1.dist-info/WHEEL +4 -0
  80. plain_admin-0.14.1.dist-info/licenses/LICENSE +28 -0
@@ -0,0 +1,117 @@
1
+ from plain.urls import path, reverse_lazy
2
+
3
+
4
+ class AdminViewRegistry:
5
+ def __init__(self):
6
+ # View classes that will be added to the admin automatically
7
+ self.registered_views = set()
8
+
9
+ def register_view(self, view=None):
10
+ def inner(view):
11
+ self.registered_views.add(view)
12
+ # TODO do this somewhere else...
13
+ # self.registered_views = set(self.registered_views, key=lambda v: v.title)
14
+ return view
15
+
16
+ if callable(view):
17
+ return inner(view)
18
+ else:
19
+ return inner
20
+
21
+ def register_viewset(self, viewset=None):
22
+ def inner(viewset):
23
+ for view in viewset.get_views():
24
+ self.register_view(view)
25
+ return viewset
26
+
27
+ if callable(viewset):
28
+ return inner(viewset)
29
+ else:
30
+ return inner
31
+
32
+ def get_nav_sections(self):
33
+ # class NavItem:
34
+ # def __init__(self, view, children):
35
+ # self.view = view
36
+ # self.children = children
37
+
38
+ sections = {}
39
+
40
+ for view in self.registered_views:
41
+ section = view.get_nav_section()
42
+ if not section:
43
+ continue
44
+ if section not in sections:
45
+ sections[section] = []
46
+ sections[section].append(view)
47
+
48
+ # Sort each section by nav_title
49
+ for section in sections.values():
50
+ section.sort(key=lambda v: v.get_nav_title())
51
+
52
+ # Sort sections dictionary by key
53
+ sections = dict(sorted(sections.items()))
54
+
55
+ return sections
56
+
57
+ # root_nav_items = []
58
+
59
+ # for view in sorted_views:
60
+ # if view.parent_view_class:
61
+ # continue
62
+ # children = [x for x in sorted_views if x.parent_view_class == view]
63
+ # root_nav_items.append(NavItem(view, children))
64
+
65
+ # return root_nav_items
66
+
67
+ def get_urls(self):
68
+ urlpatterns = []
69
+
70
+ paths_seen = set()
71
+
72
+ def add_view_path(view, _path):
73
+ if _path in paths_seen:
74
+ raise ValueError(f"Path {_path} already registered")
75
+ paths_seen.add(_path)
76
+ if not _path.endswith("/"):
77
+ _path += "/"
78
+ urlpatterns.append(path(_path, view, name=view.view_name()))
79
+
80
+ for view in self.registered_views:
81
+ add_view_path(view, f"p/{view.get_path()}")
82
+
83
+ return urlpatterns
84
+
85
+ def get_searchable_views(self):
86
+ views = [
87
+ view
88
+ for view in self.registered_views
89
+ if getattr(view, "allow_global_search", False)
90
+ ]
91
+ # Sort by slug since title isn't required by all views
92
+ views.sort(key=lambda v: v.get_slug())
93
+ return views
94
+
95
+ def get_model_detail_url(self, instance):
96
+ from plain.admin.views.base import URL_NAMESPACE
97
+ from plain.admin.views.models import AdminModelDetailView
98
+
99
+ if not getattr(instance, "pk", None):
100
+ # Has to actually be in the db
101
+ return
102
+
103
+ for view in self.registered_views:
104
+ if not issubclass(view, AdminModelDetailView):
105
+ continue
106
+
107
+ if view.model == instance.__class__:
108
+ return reverse_lazy(
109
+ f"{URL_NAMESPACE}:{view.view_name()}",
110
+ kwargs={"pk": instance.pk},
111
+ )
112
+
113
+
114
+ registry = AdminViewRegistry()
115
+ register_view = registry.register_view
116
+ register_viewset = registry.register_viewset
117
+ get_model_detail_url = registry.get_model_detail_url
@@ -0,0 +1,6 @@
1
+ class Img:
2
+ def __init__(self, src, *, alt="", width=None, height=None):
3
+ self.src = src
4
+ self.alt = alt
5
+ self.width = width
6
+ self.height = height
@@ -0,0 +1,54 @@
1
+ from plain.views import View
2
+
3
+
4
+ class AdminViewset:
5
+ @classmethod
6
+ def get_views(cls) -> list[View]:
7
+ """Views are defined as inner classes on the viewset class."""
8
+
9
+ # Primary views that we can interlink automatically
10
+ ListView = getattr(cls, "ListView", None)
11
+ CreateView = getattr(cls, "CreateView", None)
12
+ UpdateView = getattr(cls, "UpdateView", None)
13
+ DetailView = getattr(cls, "DetailView", None)
14
+ DeleteView = getattr(cls, "DeleteView", None)
15
+
16
+ # Set parent-child view class relationships
17
+ if ListView and CreateView:
18
+ CreateView.parent_view_class = ListView
19
+
20
+ if ListView and DetailView:
21
+ DetailView.parent_view_class = ListView
22
+
23
+ if DetailView and UpdateView:
24
+ UpdateView.parent_view_class = DetailView
25
+
26
+ if DetailView and DeleteView:
27
+ DeleteView.parent_view_class = DetailView
28
+
29
+ # Now iterate all inner view classes
30
+ views = []
31
+
32
+ for attr in cls.__dict__.values():
33
+ if isinstance(attr, type) and issubclass(attr, View):
34
+ views.append(attr)
35
+
36
+ for view in views:
37
+ view.viewset = cls
38
+
39
+ if ListView:
40
+ view.get_list_url = ListView.get_view_url
41
+
42
+ if CreateView:
43
+ view.get_create_url = CreateView.get_view_url
44
+
45
+ if DetailView:
46
+ view.get_detail_url = DetailView.get_view_url
47
+
48
+ if UpdateView:
49
+ view.get_update_url = UpdateView.get_view_url
50
+
51
+ if DeleteView:
52
+ view.get_delete_url = DeleteView.get_view_url
53
+
54
+ return views
@@ -0,0 +1,275 @@
1
+ Metadata-Version: 2.4
2
+ Name: plain.admin
3
+ Version: 0.14.1
4
+ Summary: Admin dashboard and tools for Plain.
5
+ Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
+ License-Expression: BSD-3-Clause
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: plain-auth<1.0.0
10
+ Requires-Dist: plain-htmx<1.0.0
11
+ Requires-Dist: plain-tailwind<1.0.0
12
+ Requires-Dist: plain<1.0.0
13
+ Requires-Dist: sqlparse>=0.2.2
14
+ Description-Content-Type: text/markdown
15
+
16
+ # Admin
17
+
18
+ An admin interface for admin users.
19
+
20
+ The Plain Admin is a new package built from the ground up.
21
+ It leverages class-based views and standard URLs and templates to provide a flexible admin where
22
+ you can quickly create your own pages and cards,
23
+ in addition to models.
24
+
25
+ - cards
26
+ - dashboards
27
+ - diy forms
28
+ - detached from login (do your own login (oauth, passkeys, etc))
29
+
30
+ ## Installation
31
+
32
+ - install plain.admin and plain.htmx, add plain.admin.admin and plain.htmx to installed packages
33
+ - add url
34
+
35
+ ## Models in the admin
36
+
37
+ ## Dashboards
38
+
39
+ <!-- # plain.querystats
40
+
41
+ On-page database query stats in development and production.
42
+
43
+ On each page, the query stats will display how many database queries were performed and how long they took.
44
+
45
+ [Watch on YouTube](https://www.youtube.com/watch?v=NX8VXxVJm08)
46
+
47
+ Clicking the stats in the toolbar will show the full SQL query log with tracebacks and timings.
48
+ This is even designed to work in production,
49
+ making it much easier to discover and debug performance issues on production data!
50
+
51
+ ![Django query stats](https://user-images.githubusercontent.com/649496/213781593-54197bb6-36a8-4c9d-8294-5b43bd86a4c9.png)
52
+
53
+ It will also point out duplicate queries,
54
+ which can typically be removed by using `select_related`,
55
+ `prefetch_related`, or otherwise refactoring your code.
56
+
57
+ ## Installation
58
+
59
+ ```python
60
+ # settings.py
61
+ INSTALLED_PACKAGES = [
62
+ # ...
63
+ "plain.admin.querystats",
64
+ ]
65
+
66
+ MIDDLEWARE = [
67
+ "plain.sessions.middleware.SessionMiddleware",
68
+ "plain.auth.middleware.AuthenticationMiddleware",
69
+
70
+ "plain.admin.querystats.QueryStatsMiddleware",
71
+ # Put additional middleware below querystats
72
+ # ...
73
+ ]
74
+ ```
75
+
76
+ We strongly recommend using the plain-toolbar along with this,
77
+ but if you aren't,
78
+ you can add the querystats to your frontend templates with this include:
79
+
80
+ ```html
81
+ {% include "querystats/button.html" %}
82
+ ```
83
+
84
+ *Note that you will likely want to surround this with an if `DEBUG` or `is_admin` check.*
85
+
86
+ To view querystats you need to send a POST request to `?querystats=store` (i.e. via a `<form>`),
87
+ and the template include is the easiest way to do that.
88
+
89
+ ## Tailwind CSS
90
+
91
+ This package is styled with [Tailwind CSS](https://tailwindcss.com/),
92
+ and pairs well with [`plain-tailwind`](https://github.com/plainpackages/plain-tailwind).
93
+
94
+ If you are using your own Tailwind implementation,
95
+ you can modify the "content" in your Tailwind config to include any Plain packages:
96
+
97
+ ```js
98
+ // tailwind.config.js
99
+ module.exports = {
100
+ content: [
101
+ // ...
102
+ ".venv/lib/python*/site-packages/plain*/**/*.{html,js}",
103
+ ],
104
+ // ...
105
+ }
106
+ ```
107
+
108
+ If you aren't using Tailwind, and don't intend to, open an issue to discuss other options.
109
+
110
+
111
+ # plain.toolbar
112
+
113
+ The admin toolbar is enabled for every user who `is_admin`.
114
+
115
+ ![Plain Admin toolbar](https://user-images.githubusercontent.com/649496/213781915-a2094f54-99b8-4a05-a36e-dee107405229.png)
116
+
117
+ ## Installation
118
+
119
+ Add `plaintoolbar` to your `INSTALLED_PACKAGES`,
120
+ and the `{% toolbar %}` to your base template:
121
+
122
+ ```python
123
+ # settings.py
124
+ INSTALLED_PACKAGES += [
125
+ "plaintoolbar",
126
+ ]
127
+ ```
128
+
129
+ ```html
130
+ <!-- base.template.html -->
131
+ {% load toolbar %}
132
+ <!doctype html>
133
+ <html lang="en">
134
+ <head>
135
+ ...
136
+ </head>
137
+ <body>
138
+ {% toolbar %}
139
+ ...
140
+ </body>
141
+ ```
142
+
143
+ More specific settings can be found below.
144
+
145
+ ## Tailwind CSS
146
+
147
+ This package is styled with [Tailwind CSS](https://tailwindcss.com/),
148
+ and pairs well with [`plain-tailwind`](https://github.com/plainpackages/plain-tailwind).
149
+
150
+ If you are using your own Tailwind implementation,
151
+ you can modify the "content" in your Tailwind config to include any Plain packages:
152
+
153
+ ```js
154
+ // tailwind.config.js
155
+ module.exports = {
156
+ content: [
157
+ // ...
158
+ ".venv/lib/python*/site-packages/plain*/**/*.{html,js}",
159
+ ],
160
+ // ...
161
+ }
162
+ ```
163
+
164
+ If you aren't using Tailwind, and don't intend to, open an issue to discuss other options.
165
+
166
+
167
+ # plain.requestlog
168
+
169
+ The request log stores a local history of HTTP requests and responses during `plain work` (Django runserver).
170
+
171
+ The request history will make it easy to see redirects,
172
+ 400 and 500 level errors,
173
+ form submissions,
174
+ API calls,
175
+ webhooks,
176
+ and more.
177
+
178
+ [Watch on YouTube](https://www.youtube.com/watch?v=AwI7Pt5oZnM)
179
+
180
+ Requests can be re-submitted by clicking the "replay" button.
181
+
182
+ [![Django request log](https://user-images.githubusercontent.com/649496/213781414-417ad043-de67-4836-9ef1-2b91404336c3.png)](https://user-images.githubusercontent.com/649496/213781414-417ad043-de67-4836-9ef1-2b91404336c3.png)
183
+
184
+ ## Installation
185
+
186
+ ```python
187
+ # settings.py
188
+ INSTALLED_PACKAGES += [
189
+ "plainrequestlog",
190
+ ]
191
+
192
+ MIDDLEWARE = MIDDLEWARE + [
193
+ # ...
194
+ "plainrequestlog.RequestLogMiddleware",
195
+ ]
196
+ ```
197
+
198
+ The default settings can be customized if needed:
199
+
200
+ ```python
201
+ # settings.py
202
+ DEV_REQUESTS_IGNORE_PATHS = [
203
+ "/sw.js",
204
+ "/favicon.ico",
205
+ "/admin/jsi18n/",
206
+ ]
207
+ DEV_REQUESTS_MAX = 50
208
+ ```
209
+
210
+ ## Tailwind CSS
211
+
212
+ This package is styled with [Tailwind CSS](https://tailwindcss.com/),
213
+ and pairs well with [`plain-tailwind`](https://github.com/plainpackages/plain-tailwind).
214
+
215
+ If you are using your own Tailwind implementation,
216
+ you can modify the "content" in your Tailwind config to include any Plain packages:
217
+
218
+ ```js
219
+ // tailwind.config.js
220
+ module.exports = {
221
+ content: [
222
+ // ...
223
+ ".venv/lib/python*/site-packages/plain*/**/*.{html,js}",
224
+ ],
225
+ // ...
226
+ }
227
+ ```
228
+
229
+ If you aren't using Tailwind, and don't intend to, open an issue to discuss other options.
230
+
231
+
232
+ # plain.impersonate
233
+
234
+ See what your users see.
235
+
236
+ A key feature for providing customer support is to be able to view the site through their account.
237
+ With `impersonate` installed, you can impersonate a user by finding them in the Django admin and clicking the "Impersonate" button.
238
+
239
+ ![](/docs/img/impersonate-admin.png)
240
+
241
+ Then with the [admin toolbar](/docs/plain-toolbar/) enabled, you'll get a notice of the impersonation and a button to exit:
242
+
243
+ ![](/docs/img/impersonate-bar.png)
244
+
245
+ ## Installation
246
+
247
+ To impersonate users, you need the app, middleware, and URLs:
248
+
249
+ ```python
250
+ # settings.py
251
+ INSTALLED_PACKAGES = INSTALLED_PACKAGES + [
252
+ "plain.admin.impersonate",
253
+ ]
254
+
255
+ MIDDLEWARE = MIDDLEWARE + [
256
+ "plain.admin.impersonate.ImpersonateMiddleware",
257
+ ]
258
+ ```
259
+
260
+ ```python
261
+ # urls.py
262
+ urlpatterns = [
263
+ # ...
264
+ path("impersonate/", include("plain.admin.impersonate.urls")),
265
+ ]
266
+ ```
267
+
268
+ ## Settings
269
+
270
+ By default, all admin users can impersonate other users.
271
+
272
+ ```python
273
+ # settings.py
274
+ IMPERSONATE_ALLOWED = lambda user: user.is_admin
275
+ ``` -->
@@ -0,0 +1,80 @@
1
+ plain/admin/README.md,sha256=ojs9HMwjwV4MwIxuD-6TOb8_W0uolzijqFm_beJ6WSY,6384
2
+ plain/admin/__init__.py,sha256=bPv9iftT8aLqBH6dDy-HTVXW66dQUhfIiEZ-LIUMC0Y,78
3
+ plain/admin/config.py,sha256=paj5BjySp2RPJ74sios4lUjLOzEZJDmoop-CnkjHXTE,641
4
+ plain/admin/dates.py,sha256=EEhcQhHt3-k6kE9yvPdH5X6EecmUQ259xywbDBec3Dg,10253
5
+ plain/admin/default_settings.py,sha256=j7RdgGqksCmCgPO7zCcFiVV9f8yW-EULvqDcFOhQap8,127
6
+ plain/admin/middleware.py,sha256=k3yP1o3CzvLiZZSoxqq-DvAZlp4sICRauaT-kD3FJKM,398
7
+ plain/admin/templates.py,sha256=jLhJkuvqnPMBQTP-kzojFaqmFi50GZHvrVzuZCLc3rk,836
8
+ plain/admin/toolbar.py,sha256=dsZa_I-tTbaeOluCbvHGEqy4_Suw6Q_JSrKl8Eu08qY,973
9
+ plain/admin/urls.py,sha256=f3WD6Za7jsm2jgUpyi-U9UB1ZWhQYGs8q-Qju0BsUKg,1296
10
+ plain/admin/assets/admin/admin.css,sha256=K8XfU6XMSe2-e-T9XvD-g2t1cgYz9GDMne-ugVeNXcA,2207
11
+ plain/admin/assets/admin/admin.js,sha256=AWD6UqPxGqJFaUhYTDWe4niTgk0thzU4gRl7qK41KNc,2759
12
+ plain/admin/assets/admin/chart.js,sha256=GZiCYXjL6SmyuSCGE0Df80QvOUkw6H2YD-zsVID05lo,205089
13
+ plain/admin/assets/admin/jquery-3.6.1.slim.min.js,sha256=W2eb4M1jdKpuZ_-_KnDgqI9X9SwGLrXtO0dknpNPJyE,72534
14
+ plain/admin/assets/admin/list.js,sha256=_DPneRvk3VSzjVzfEaxyif4vLD75sCWz7bkHYp89uL8,1826
15
+ plain/admin/assets/admin/popper.min.js,sha256=SgCxkjQZdrt2puqn62YUu9hknpCBGBEAy9uhQ9PPZaI,20083
16
+ plain/admin/assets/admin/tippy-bundle.umd.min.js,sha256=oVWBpeGTKMG_iBWGkQF02JnGIMFPYuFqTjUWeJY3pZ0,25668
17
+ plain/admin/assets/toolbar/toolbar.js,sha256=kRCQ37iQNklzBjjBeHSeBU39mLpQ4Q0pnC3cdbQAy28,1636
18
+ plain/admin/cards/__init__.py,sha256=8NfWrguyJRriJFUc3_QeGaDILhgeU3d1aXktzIuAR1E,172
19
+ plain/admin/cards/base.py,sha256=IUqHOWB0t7sM1vVUrbHT76alP-7Jwga3FfagNgKTJww,2250
20
+ plain/admin/cards/charts.py,sha256=fbCypn4_2uhFnNgj7z1T7bhSjQVtlxODnctynI6yrqI,5017
21
+ plain/admin/cards/tables.py,sha256=lGUBeSaBsNVuzINVH8qU-1XF0PfPY03gcUKtN-462zE,599
22
+ plain/admin/impersonate/README.md,sha256=GT7ubMxyB2RhUh-gDg_yYqWSm7oMp0hy1LepXyDRMo8,1012
23
+ plain/admin/impersonate/__init__.py,sha256=houAFRscvEx8ajejZl9Im8Iu1aJFTTloHMXpgSwViVs,83
24
+ plain/admin/impersonate/middleware.py,sha256=gFDLyEBqslJC908gKk1bFKmLFpimiaQdeaaXCBjUvaM,1261
25
+ plain/admin/impersonate/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ plain/admin/impersonate/permissions.py,sha256=N0EFshs0pgwFIAsK2MUgfnyhdb2rYheY_l47cYdGurE,332
27
+ plain/admin/impersonate/settings.py,sha256=4wbWBN9eZIzei4fwkFLfw-_T5pvP_GG4l1lDdVpL_Co,193
28
+ plain/admin/impersonate/urls.py,sha256=v4yBKyuwbJv8TwrCz7D_MMr7HPg7m-q7g_UdnJKV_js,258
29
+ plain/admin/impersonate/views.py,sha256=p8kEGC2ZNntAaLJRgwCaGSJABjLWoarpya9IuBpNW5A,789
30
+ plain/admin/querystats/README.md,sha256=dCKMmZQIgbjNLRDrgIaq6yxeON81JVADXzkLoGTPoy8,4823
31
+ plain/admin/querystats/__init__.py,sha256=VmP1aQ5Pviq4Z3izCB8G9g0Weq-2SYR88UFNtwqAPpo,81
32
+ plain/admin/querystats/core.py,sha256=ekleABddKOUPwBXVO1Ybw_MpIg6bJVoRpFpPatAqFhQ,4312
33
+ plain/admin/querystats/middleware.py,sha256=M1EVdX11H545IdZlppbSIL_h8hzBIrMELrYrcAb4aq0,3192
34
+ plain/admin/querystats/urls.py,sha256=gIA280o52H2pUIML__X-1t9hR-386kDRJmjYjNNctTc,157
35
+ plain/admin/querystats/views.py,sha256=58UpxaBp_H80Tf7azi4QcphgHbXgP5iqLDf-qZJfzRI,788
36
+ plain/admin/templates/admin/base.html,sha256=nhgBk69i8HaafIe0aa2xuj-YmEKyc9UZAju-k3G0nUs,8514
37
+ plain/admin/templates/admin/delete.html,sha256=lNuU2G-BR6TH6NUmh7VcvjnEuFeI84rwwk_oO1jkUq0,431
38
+ plain/admin/templates/admin/detail.html,sha256=hwRSjPunxLnJKPAyP2HYVKV3cJmWw-OBgCLZ1WQ8zaE,743
39
+ plain/admin/templates/admin/form.html,sha256=Cc9zKjYbs_W3TaoO5teLhg35eb47JYYXEJH55wA760E,289
40
+ plain/admin/templates/admin/index.html,sha256=b65tcZhv9QfvmjePySU7MmzUlpMECIXP8dBH-a3Eyxw,69
41
+ plain/admin/templates/admin/list.html,sha256=RFtGZf_g_IDiPc8udchgf63mMpfEsvzS4hUKqkvDjoM,8781
42
+ plain/admin/templates/admin/page.html,sha256=wzRR-JLs8CgCOoB3BMoYWqTMpYM0z4X2qlqdwAe0YjM,67
43
+ plain/admin/templates/admin/search.html,sha256=c04SZ5MbF1iKBYSGospQwB1UtU2_JAi_npaaPUtuJIk,620
44
+ plain/admin/templates/admin/cards/base.html,sha256=2HRIxvt5Kf0MPVv7XLQZcc7vfz3YR_WLsrVgbQtyN5I,933
45
+ plain/admin/templates/admin/cards/card.html,sha256=OWR1kF4vKtr06x_Q34Z01UmKEv_Jdq2Ws3v3RARaxCY,263
46
+ plain/admin/templates/admin/cards/chart.html,sha256=boQRaWXiZvwKkMudT3IDsRvaofv5LHgbSeWr_HEGghg,642
47
+ plain/admin/templates/admin/cards/table.html,sha256=zFTdzmKUU2gS7ni-qjft5mxhcPK2rPogPXsE7208QYg,651
48
+ plain/admin/templates/admin/values/UUID.html,sha256=ZoIp0u7WVKbJfEBdHyJI7IMCYHQ9c12NOlNoFcsqaps,66
49
+ plain/admin/templates/admin/values/bool.html,sha256=UEvtDDdQ_kwiGBk8gq47o3mVTsC6XVAd8Iv5R4648Xw,797
50
+ plain/admin/templates/admin/values/datetime.html,sha256=VD2McvirOxRpriNT5y3O02NcEIQ4OEb-o8wTqKBZyQI,122
51
+ plain/admin/templates/admin/values/default.html,sha256=xpoKqUbvZjn9e_ZBsaQReaDXYpSoBeYV9iNZ1BtWIW4,105
52
+ plain/admin/templates/admin/values/dict.html,sha256=vqUOuJglFDbOBuKCA11VoIZHWVt_FjMe166TKEstDa4,53
53
+ plain/admin/templates/admin/values/get_display.html,sha256=nBg3CSA9xYTSrDluOwPBqT8R1ESA8zMBAGtCFRfPeWQ,77
54
+ plain/admin/templates/admin/values/img.html,sha256=d1FegNtLhjSjliHKi9hAkJ6klhm8wdtPXbvEVSyHWjQ,63
55
+ plain/admin/templates/admin/values/list.html,sha256=vqUOuJglFDbOBuKCA11VoIZHWVt_FjMe166TKEstDa4,53
56
+ plain/admin/templates/admin/values/model.html,sha256=_aX_t2VQYt_bN_jQBky5IDi49cbq7dzBLuisb3BY_is,410
57
+ plain/admin/templates/admin/values/queryset.html,sha256=YU-mDxHzinWWLUBE-oX3dOMOMyHymzrdeZyxXGMF5Ss,138
58
+ plain/admin/templates/elements/admin/Checkbox.html,sha256=2hUSWCbazaJKyZdsk2shF0qN6kSeV20HVLdRitC_KfQ,213
59
+ plain/admin/templates/elements/admin/CheckboxField.html,sha256=B6s4jXK96MfMJY8X9YgMfR4UKWkHogK7Dfn81jbW0fk,206
60
+ plain/admin/templates/elements/admin/FieldErrors.html,sha256=YO150DwGG8tf8Q4d1Cf59gpchXzF-n8FSse2GqOX3cA,108
61
+ plain/admin/templates/elements/admin/Input.html,sha256=7rziKkGDgg-fQ4Yfb_hjR9pOt0DFs8UeXicN6MCoM4s,371
62
+ plain/admin/templates/elements/admin/InputField.html,sha256=J3pvMs3ajYJ1G1iE0vFaS9kjSppe2HyLIbEl3WbqkuY,156
63
+ plain/admin/templates/elements/admin/Label.html,sha256=OsSkdbeREkDwdLSxXTLjgnycvFf69j4FPTKciDFf-Z4,96
64
+ plain/admin/templates/elements/admin/Select.html,sha256=CUJD4cHno_bc0g_SQN0IV0a5sZL7Gx4fFB44xBse-Ic,458
65
+ plain/admin/templates/elements/admin/SelectField.html,sha256=thvPmQzvi65BnGsErVtLwZUKYvsTpz4onrMwKXMuXA4,157
66
+ plain/admin/templates/elements/admin/Submit.html,sha256=1Lgn3Du9rXplbM3V12z2JckSaiWPlPGLP48xIZ887AA,150
67
+ plain/admin/templates/querystats/querystats.html,sha256=CMH3TDBXXxoxrICMIxiLfo4cN7ae9DMCg3WNmZR8M_o,3504
68
+ plain/admin/templates/querystats/toolbar.html,sha256=dePs614akVWUD8IlgzvQ0TREThv1ttKPj-yOPzJxmXM,3574
69
+ plain/admin/templates/toolbar/toolbar.html,sha256=k76DXO8NrnFKGZpDA0DASdesFvw6qt8vv_T9CqCLE6U,6504
70
+ plain/admin/views/__init__.py,sha256=nF6AENZ3Xxyi08OTRrF6e-HYBkZSFj7XBK2mVzMYqN4,846
71
+ plain/admin/views/base.py,sha256=uBhZs7vh1mrHb3fVTEYpRpIRY9SJ7JCgQVksGQQdGIg,3743
72
+ plain/admin/views/models.py,sha256=0zSrxvZa1ARmqUeSzKqXNS5fT0bVwydsNdSOK5AUhk8,7287
73
+ plain/admin/views/objects.py,sha256=8fx1Fp1rzYbw9ARNzLYYWzaTrMbNY-5LshY1mvqtf8c,11315
74
+ plain/admin/views/registry.py,sha256=kqWGiC5Nscu_wjUiEfh6OX28jG7Y5J0XV9UsZGSQMQs,3537
75
+ plain/admin/views/types.py,sha256=ONMMdUoapgMoUVYgSIe-4YCdfvaVMQ4jgPWYiMo0pDk,178
76
+ plain/admin/views/viewsets.py,sha256=dqMlQ6kLn9iqd9BwBWAZT1S271wH1FdfM5HXbOgBMEw,1655
77
+ plain_admin-0.14.1.dist-info/METADATA,sha256=CkD5qsFoLv_Ee7erQcx7zxItBoHfAPK3ZmpbvI-Tlj4,6821
78
+ plain_admin-0.14.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
79
+ plain_admin-0.14.1.dist-info/licenses/LICENSE,sha256=cvKM3OlqHx3ijD6e34zsSUkPvzl-ya3Dd63A6EHL94U,1500
80
+ plain_admin-0.14.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, Dropseed, LLC
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.