exceptbot-drf 1.0.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.
exceptbot/urls.py ADDED
@@ -0,0 +1,85 @@
1
+ """
2
+ URL configuration for ExceptBot DRF API.
3
+
4
+ Endpoints:
5
+ GET /exceptbot/api/unresolved/
6
+ GET /exceptbot/api/resolved/
7
+ GET /exceptbot/api/settings/
8
+ PUT /exceptbot/api/settings/
9
+ GET /exceptbot/api/<log_id>/
10
+ GET /exceptbot/api/<log_id>/error/
11
+ GET /exceptbot/api/<log_id>/file/
12
+ POST /exceptbot/api/<log_id>/resolve/
13
+ POST /exceptbot/api/<log_id>/unresolve/
14
+ POST /exceptbot/api/<log_id>/ai/
15
+ """
16
+ from django.urls import path
17
+
18
+ from .views import (
19
+ UnresolvedExceptionListView,
20
+ ResolvedExceptionListView,
21
+ ExceptionDetailView,
22
+ ExceptionErrorMessageView,
23
+ ExceptionFileContentView,
24
+ MarkResolvedView,
25
+ MarkUnresolvedView,
26
+ AIRecommendationView,
27
+ AppSettingsView,
28
+ )
29
+
30
+ app_name = 'exceptbot'
31
+
32
+ urlpatterns = [
33
+ # ---------- لیست‌ها ----------
34
+ path(
35
+ 'api/unresolved/',
36
+ UnresolvedExceptionListView.as_view(),
37
+ name='unresolved-list',
38
+ ),
39
+ path(
40
+ 'api/resolved/',
41
+ ResolvedExceptionListView.as_view(),
42
+ name='resolved-list',
43
+ ),
44
+
45
+ # ---------- تنظیمات ----------
46
+ path(
47
+ 'api/settings/',
48
+ AppSettingsView.as_view(),
49
+ name='app-settings',
50
+ ),
51
+
52
+ # ---------- جزئیات ----------
53
+ path(
54
+ 'api/<int:log_id>/',
55
+ ExceptionDetailView.as_view(),
56
+ name='exception-detail',
57
+ ),
58
+ path(
59
+ 'api/<int:log_id>/error/',
60
+ ExceptionErrorMessageView.as_view(),
61
+ name='exception-error',
62
+ ),
63
+ path(
64
+ 'api/<int:log_id>/file/',
65
+ ExceptionFileContentView.as_view(),
66
+ name='exception-file',
67
+ ),
68
+
69
+ # ---------- عملیات ----------
70
+ path(
71
+ 'api/<int:log_id>/resolve/',
72
+ MarkResolvedView.as_view(),
73
+ name='mark-resolved',
74
+ ),
75
+ path(
76
+ 'api/<int:log_id>/unresolve/',
77
+ MarkUnresolvedView.as_view(),
78
+ name='mark-unresolved',
79
+ ),
80
+ path(
81
+ 'api/<int:log_id>/ai/',
82
+ AIRecommendationView.as_view(),
83
+ name='ai-recommendation',
84
+ ),
85
+ ]
exceptbot/views.py ADDED
@@ -0,0 +1,272 @@
1
+ from rest_framework import status
2
+ from rest_framework.views import APIView
3
+ from rest_framework.response import Response
4
+ from rest_framework.generics import ListAPIView, RetrieveAPIView
5
+
6
+ from openai import OpenAI
7
+
8
+ from .models import ExceptionLog, AppSettings
9
+ from .permissions import IsSuperUser
10
+ from .serializers import (
11
+ ExceptionLogListSerializer,
12
+ ExceptionLogDetailSerializer,
13
+ ExceptionLogResolveSerializer,
14
+ AppSettingsSerializer,
15
+ )
16
+
17
+
18
+ # ============================================================
19
+ # GET /exceptbot/api/unresolved/
20
+ # ============================================================
21
+ class UnresolvedExceptionListView(ListAPIView):
22
+ serializer_class = ExceptionLogListSerializer
23
+ permission_classes = [IsSuperUser]
24
+
25
+ def get_queryset(self):
26
+ return ExceptionLog.objects.filter(
27
+ is_resolved=False
28
+ ).order_by('-timestamp')
29
+
30
+
31
+ # ============================================================
32
+ # GET /exceptbot/api/resolved/
33
+ # ============================================================
34
+ class ResolvedExceptionListView(ListAPIView):
35
+ serializer_class = ExceptionLogListSerializer
36
+ permission_classes = [IsSuperUser]
37
+
38
+ def get_queryset(self):
39
+ return ExceptionLog.objects.filter(
40
+ is_resolved=True
41
+ ).order_by('-timestamp')
42
+
43
+
44
+ # ============================================================
45
+ # GET /exceptbot/api/<log_id>/
46
+ # ============================================================
47
+ class ExceptionDetailView(RetrieveAPIView):
48
+ queryset = ExceptionLog.objects.all()
49
+ serializer_class = ExceptionLogDetailSerializer
50
+ permission_classes = [IsSuperUser]
51
+ lookup_url_kwarg = 'log_id'
52
+
53
+
54
+ # ============================================================
55
+ # GET /exceptbot/api/<log_id>/error/
56
+ # ============================================================
57
+ class ExceptionErrorMessageView(APIView):
58
+ permission_classes = [IsSuperUser]
59
+
60
+ def get(self, request, log_id):
61
+ try:
62
+ log = ExceptionLog.objects.get(id=log_id)
63
+ except ExceptionLog.DoesNotExist:
64
+ return Response(
65
+ {'detail': 'Exception log not found.'},
66
+ status=status.HTTP_404_NOT_FOUND,
67
+ )
68
+
69
+ return Response({
70
+ 'id': log.id,
71
+ 'exception_type': log.exception_type,
72
+ 'full_error_message': log.full_error_message,
73
+ })
74
+
75
+
76
+ # ============================================================
77
+ # GET /exceptbot/api/<log_id>/file/
78
+ # ============================================================
79
+ class ExceptionFileContentView(APIView):
80
+ permission_classes = [IsSuperUser]
81
+
82
+ def get(self, request, log_id):
83
+ try:
84
+ log = ExceptionLog.objects.get(id=log_id)
85
+ except ExceptionLog.DoesNotExist:
86
+ return Response(
87
+ {'detail': 'Exception log not found.'},
88
+ status=status.HTTP_404_NOT_FOUND,
89
+ )
90
+
91
+ return Response({
92
+ 'id': log.id,
93
+ 'file_name': log.file_name,
94
+ 'line_number': log.line_number,
95
+ 'error_line_content': log.error_line_content,
96
+ 'file_content': log.file_content,
97
+ })
98
+
99
+
100
+ # ============================================================
101
+ # POST /exceptbot/api/<log_id>/resolve/
102
+ # ============================================================
103
+ class MarkResolvedView(APIView):
104
+ permission_classes = [IsSuperUser]
105
+
106
+ def post(self, request, log_id):
107
+ try:
108
+ log = ExceptionLog.objects.get(id=log_id)
109
+ except ExceptionLog.DoesNotExist:
110
+ return Response(
111
+ {'detail': 'Exception log not found.'},
112
+ status=status.HTTP_404_NOT_FOUND,
113
+ )
114
+
115
+ serializer = ExceptionLogResolveSerializer(data=request.data)
116
+ serializer.is_valid(raise_exception=True)
117
+
118
+ if log.is_resolved:
119
+ return Response(
120
+ {'detail': 'این خطا قبلاً رفع شده است.'},
121
+ status=status.HTTP_400_BAD_REQUEST,
122
+ )
123
+
124
+ log.mark_resolved(
125
+ user=request.user,
126
+ note=serializer.validated_data.get('resolution_note', ''),
127
+ )
128
+
129
+ return Response(
130
+ ExceptionLogDetailSerializer(log).data,
131
+ status=status.HTTP_200_OK,
132
+ )
133
+
134
+
135
+ # ============================================================
136
+ # POST /exceptbot/api/<log_id>/unresolve/
137
+ # ============================================================
138
+ class MarkUnresolvedView(APIView):
139
+ permission_classes = [IsSuperUser]
140
+
141
+ def post(self, request, log_id):
142
+ try:
143
+ log = ExceptionLog.objects.get(id=log_id)
144
+ except ExceptionLog.DoesNotExist:
145
+ return Response(
146
+ {'detail': 'Exception log not found.'},
147
+ status=status.HTTP_404_NOT_FOUND,
148
+ )
149
+
150
+ log.is_resolved = False
151
+ log.resolved_by = None
152
+ log.resolved_at = None
153
+ log.resolution_note = ''
154
+ log.save()
155
+
156
+ return Response(
157
+ ExceptionLogDetailSerializer(log).data,
158
+ status=status.HTTP_200_OK,
159
+ )
160
+
161
+
162
+ # ============================================================
163
+ # POST /exceptbot/api/<log_id>/ai/
164
+ # ============================================================
165
+ class AIRecommendationView(APIView):
166
+ permission_classes = [IsSuperUser]
167
+
168
+ def post(self, request, log_id):
169
+ try:
170
+ log = ExceptionLog.objects.get(id=log_id)
171
+ except ExceptionLog.DoesNotExist:
172
+ return Response(
173
+ {'detail': 'Exception log not found.'},
174
+ status=status.HTTP_404_NOT_FOUND,
175
+ )
176
+
177
+ app_settings = AppSettings.get_solo()
178
+ if not app_settings.openai_api_key:
179
+ return Response(
180
+ {'detail': 'OpenAI API key is not configured.'},
181
+ status=status.HTTP_400_BAD_REQUEST,
182
+ )
183
+ if log.ai_suggestion:
184
+ return Response({
185
+ 'id': log.id,
186
+ 'ai_suggestion': log.ai_suggestion,
187
+ 'cached': True,
188
+ })
189
+ exception_information = f"""My Django application has thrown an exception.
190
+ An exception happened in line {log.line_number} of file {log.file_name}.
191
+ The exception type is: {log.exception_type}
192
+
193
+ The content of the exception message is:
194
+ {log.full_error_message}
195
+
196
+ text
197
+
198
+ The content of the file that produced the exception is:
199
+ {log.file_content}
200
+
201
+ text
202
+
203
+ The specific line from the above that threw the error is:
204
+ {log.error_line_content}
205
+
206
+ text
207
+
208
+ Please provide a suggestion for how to address the issue.
209
+ """
210
+
211
+ messages = [
212
+ {
213
+ "role": "system",
214
+ "content": (
215
+ "You are ExceptBot. Users send you information about exceptions "
216
+ "in their Django applications and you respond with useful instructions "
217
+ "and code on how to fix the issue. Please answer efficiently and use "
218
+ "an economy of words."
219
+ ),
220
+ },
221
+ {
222
+ "role": "user",
223
+ "content": exception_information,
224
+ },
225
+ ]
226
+
227
+ try:
228
+ client = OpenAI(api_key=app_settings.openai_api_key)
229
+
230
+ response = client.chat.completions.create(
231
+ model="gpt-4-1106-preview",
232
+ messages=messages,
233
+ )
234
+
235
+ log.ai_suggestion = response.choices[0].message.content
236
+ log.save(update_fields=['ai_suggestion'])
237
+
238
+ except Exception as exc:
239
+ return Response(
240
+ {'detail': f'AI service error: {str(exc)}'},
241
+ status=status.HTTP_502_BAD_GATEWAY,
242
+ )
243
+
244
+ return Response({
245
+ 'id': log.id,
246
+ 'ai_suggestion': log.ai_suggestion,
247
+ 'cached': False,
248
+ })
249
+
250
+
251
+ # ============================================================
252
+ # GET /exceptbot/api/settings/
253
+ # PUT /exceptbot/api/settings/
254
+ # ============================================================
255
+ class AppSettingsView(APIView):
256
+ permission_classes = [IsSuperUser]
257
+
258
+ def get(self, request):
259
+ app_settings = AppSettings.get_solo()
260
+ serializer = AppSettingsSerializer(app_settings)
261
+ return Response(serializer.data)
262
+
263
+ def put(self, request):
264
+ app_settings = AppSettings.get_solo()
265
+ serializer = AppSettingsSerializer(
266
+ app_settings,
267
+ data=request.data,
268
+ partial=True,
269
+ )
270
+ serializer.is_valid(raise_exception=True)
271
+ serializer.save()
272
+ return Response(serializer.data)
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.4
2
+ Name: exceptbot-drf
3
+ Version: 1.0.0
4
+ Summary: Exception Logger with AI Suggestions for Django REST Framework
5
+ Author-email: Taha Zarei <tahazarei777@gmail.com>
6
+ Maintainer-email: Taha Zarei <tahazarei777@gmail.com>
7
+ License-Expression: BSD-3-Clause
8
+ Project-URL: Homepage, https://github.com/tahazarei777/exceptbot-drf
9
+ Project-URL: Repository, https://github.com/tahazarei777/exceptbot-drf.git
10
+ Project-URL: Bug Tracker, https://github.com/tahazarei777/exceptbot-drf/issues
11
+ Project-URL: Changelog, https://github.com/tahazarei777/exceptbot-drf/blob/main/CHANGELOG.md
12
+ Keywords: django,django-rest-framework,drf,exception,logger,error-tracking,middleware,openai,chatgpt,api
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: Django
15
+ Classifier: Framework :: Django :: 3.2
16
+ Classifier: Framework :: Django :: 4.0
17
+ Classifier: Framework :: Django :: 4.1
18
+ Classifier: Framework :: Django :: 4.2
19
+ Classifier: Framework :: Django :: 5.0
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3.8
24
+ Classifier: Programming Language :: Python :: 3.9
25
+ Classifier: Programming Language :: Python :: 3.10
26
+ Classifier: Programming Language :: Python :: 3.11
27
+ Classifier: Programming Language :: Python :: 3.12
28
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
+ Classifier: Topic :: Software Development :: Debuggers
30
+ Classifier: Topic :: System :: Logging
31
+ Requires-Python: >=3.8
32
+ Description-Content-Type: text/markdown
33
+ License-File: LICENSE.md
34
+ Requires-Dist: Django>=3.2
35
+ Requires-Dist: djangorestframework>=3.12
36
+ Requires-Dist: openai>=1.3.7
37
+ Provides-Extra: dev
38
+ Requires-Dist: build>=1.0.0; extra == "dev"
39
+ Requires-Dist: twine>=4.0.0; extra == "dev"
40
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
41
+ Requires-Dist: pytest-django>=4.5.0; extra == "dev"
42
+ Dynamic: license-file
43
+
44
+ ```
45
+ # ExceptBot DRF
46
+
47
+ Exception Logger with AI Suggestions for Django REST Framework.
48
+
49
+ [![PyPI version](https://badge.fury.io/py/exceptbot-drf.svg)](https://pypi.org/project/exceptbot-drf/)
50
+ [![Python](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/)
51
+ [![Django](https://img.shields.io/badge/django-3.2%2B-green)](https://www.djangoproject.com/)
52
+ [![DRF](https://img.shields.io/badge/djangorestframework-3.12%2B-red)](https://www.django-rest-framework.org/)
53
+ [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE.md)
54
+
55
+ ## Overview
56
+
57
+ ExceptBot DRF is a Django middleware and REST API application that captures, logs, and helps resolve exceptions in Django/DRF projects. Superusers can view exceptions through REST endpoints, request AI-powered fix suggestions from OpenAI, and track resolution status.
58
+
59
+ This project is a fork of [ExceptBot](https://github.com/geneffects/exceptbot) by Brian Risk, modified to support Django REST Framework APIs, custom user models, and enhanced request metadata.
60
+
61
+ ## Features
62
+
63
+ - Automatic exception capture via Django middleware
64
+ - Full stack trace with code snapshot of the offending file
65
+ - File name, line number, and exact line that raised the exception
66
+ - Request context: URL path, HTTP method, status code, IP address, user agent, and masked request body
67
+ - Source detection (`backend`, `frontend`, `unknown`) via the `X-Client-Type` header
68
+ - Smart deduplication: identical exceptions increment a `count` field
69
+ - AI-powered fix suggestions via OpenAI ChatGPT
70
+ - Resolution tracking with `is_resolved`, `resolved_by`, `resolved_at`, and `resolution_note`
71
+ - REST API with superuser-only access
72
+ - Singleton `AppSettings` model
73
+
74
+ ## Requirements
75
+
76
+ - Python >= 3.8
77
+ - Django >= 3.2
78
+ - djangorestframework >= 3.12
79
+ - openai >= 1.3.7
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ pip install exceptbot-drf
85
+ ```
86
+
87
+ ## Setup
88
+
89
+ ### 1. Add to `INSTALLED_APPS`
90
+
91
+ ```python
92
+ INSTALLED_APPS = [
93
+ # ...
94
+ 'rest_framework',
95
+ 'exceptbot',
96
+ # ...
97
+ ]
98
+ ```
99
+
100
+ ### 2. Add the middleware
101
+
102
+ ```python
103
+ MIDDLEWARE = [
104
+ # ...
105
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
106
+ # ...
107
+ 'exceptbot.middleware.ExceptBotMiddleware',
108
+ ]
109
+ ```
110
+
111
+ `AuthenticationMiddleware` must appear before `ExceptBotMiddleware` so that `request.user` is available.
112
+
113
+ ### 3. Configure DRF authentication
114
+
115
+ ```python
116
+ REST_FRAMEWORK = {
117
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
118
+ 'rest_framework.authentication.SessionAuthentication',
119
+ 'rest_framework.authentication.TokenAuthentication',
120
+ ],
121
+ }
122
+ ```
123
+
124
+ ### 4. Include the URLs
125
+
126
+ ```python
127
+ from django.urls import path, include
128
+
129
+ urlpatterns = [
130
+ # ...
131
+ path('exceptbot/', include('exceptbot.urls', namespace='exceptbot')),
132
+ # ...
133
+ ]
134
+ ```
135
+
136
+ ### 5. Run migrations
137
+
138
+ ```bash
139
+ python manage.py migrate exceptbot
140
+ ```
141
+
142
+ ### 6. Configure settings
143
+
144
+ Navigate to `/admin/exceptbot/appsettings/` and set the following fields:
145
+
146
+ | Field | Description |
147
+ | :--- | :--- |
148
+ | `project_name` | The directory name of your project (used to locate the correct file in the traceback) |
149
+ | `base_url` | Base URL of your site (e.g., `https://myapp.com`) |
150
+ | `openai_api_key` | OpenAI API key |
151
+
152
+ ## API Endpoints
153
+
154
+ | Method | Endpoint | Description |
155
+ | :--- | :--- | :--- |
156
+ | GET | `/exceptbot/api/unresolved/` | List unresolved exceptions |
157
+ | GET | `/exceptbot/api/resolved/` | List resolved exceptions |
158
+ | GET | `/exceptbot/api/<id>/` | Full exception detail |
159
+ | GET | `/exceptbot/api/<id>/error/` | Error message and traceback |
160
+ | GET | `/exceptbot/api/<id>/file/` | Content of the offending file |
161
+ | POST | `/exceptbot/api/<id>/resolve/` | Mark as resolved |
162
+ | POST | `/exceptbot/api/<id>/unresolve/` | Revert to unresolved |
163
+ | POST | `/exceptbot/api/<id>/ai/` | Get or generate AI suggestion |
164
+ | GET | `/exceptbot/api/settings/` | View settings |
165
+ | PUT | `/exceptbot/api/settings/` | Update settings |
166
+
167
+ All endpoints require superuser authentication.
168
+
169
+ ## Examples
170
+
171
+ Fetch unresolved exceptions:
172
+
173
+ ```bash
174
+ curl -H "Authorization: Bearer YOUR_TOKEN" \
175
+ http://localhost:8000/exceptbot/api/unresolved/
176
+ ```
177
+
178
+ Mark an exception as resolved:
179
+
180
+ ```bash
181
+ curl -X POST \
182
+ -H "Authorization: Bearer YOUR_TOKEN" \
183
+ -H "Content-Type: application/json" \
184
+ -d '{"resolution_note": "Fixed in commit abc123"}' \
185
+ http://localhost:8000/exceptbot/api/1/resolve/
186
+ ```
187
+
188
+ Request an AI suggestion:
189
+
190
+ ```bash
191
+ curl -X POST \
192
+ -H "Authorization: Bearer YOUR_TOKEN" \
193
+ http://localhost:8000/exceptbot/api/1/ai/
194
+ ```
195
+
196
+ ## Source Detection
197
+
198
+ To distinguish frontend errors from backend errors, include the following header in requests:
199
+
200
+ ```
201
+ X-Client-Type: frontend
202
+ ```
203
+
204
+ If omitted, the default value is `backend`.
205
+
206
+ ## How It Works
207
+
208
+ 1. The middleware catches exceptions in `process_exception` and inspects the traceback.
209
+ 2. It walks the traceback in reverse to find the first frame inside the project, matched by `project_name`.
210
+ 3. It reads the offending file and stores the full source as a code snapshot.
211
+ 4. It extracts HTTP method, status code, IP address, user agent, and masked request body.
212
+ 5. Identical exceptions increment `count` instead of creating new records.
213
+ 6. Superusers query the REST API to view, analyze, and resolve exceptions.
214
+
215
+ ## Security
216
+
217
+ - Sensitive request fields (`password`, `token`, `api_key`, `authorization`, and similar) are replaced with `***MASKED***` before being stored.
218
+ - All endpoints are protected by the `IsSuperUser` permission class.
219
+ - The OpenAI API key is hidden in the admin list view and only shown as a boolean.
220
+ - Only one `AppSettings` record can exist.
221
+
222
+ ## Project Structure
223
+
224
+ ```
225
+ exceptbot/
226
+ ├── migrations/
227
+ ├── admin.py
228
+ ├── apps.py
229
+ ├── middleware.py
230
+ ├── models.py
231
+ ├── permissions.py
232
+ ├── serializers.py
233
+ ├── urls.py
234
+ └── views.py
235
+ ```
236
+
237
+ ## Contributing
238
+
239
+ Issues and pull requests are welcome at:
240
+ https://github.com/tahazarei777/exceptbot-drf
241
+
242
+ ## Reporting Issues
243
+
244
+ Report bugs at:
245
+ https://github.com/tahazarei777/exceptbot-drf/issues
246
+
247
+ ## License
248
+
249
+ This project is a fork of [ExceptBot](https://github.com/geneffects/exceptbot) by Brian Risk.
250
+
251
+ Original work: Copyright © 2023-present, [D.AT Analytics, LLC](https://d.at/). All rights reserved.
252
+
253
+ Modifications: Copyright © 2025, Taha Zarei.
254
+
255
+ Licensed under the BSD 3-Clause License. See [LICENSE.md](LICENSE.md) for details.
256
+ Exception Logger with AI Suggestions for Django REST Framework
257
+ ```
258
+
259
+ **Website:**
260
+ ```
261
+ https://pypi.org/project/exceptbot-drf/
262
+ ```
263
+
264
+ **Topics:**
265
+ ```
266
+ django
267
+ django-rest-framework
268
+ drf
269
+ exception-logger
270
+ error-tracking
271
+ openai
272
+ chatgpt
273
+ python
274
+ middleware
275
+ api
276
+ ```
277
+
@@ -0,0 +1,17 @@
1
+ exceptbot/__init__.py,sha256=qpinTa2JOwKaCmaZUtKaIYyL7I-mmma7s6Aw-zn2yWU,53
2
+ exceptbot/admin.py,sha256=Wl6ZHlHECwQG8BqdGGKTS_2BxB0BNSWouGycZpW5SeE,7657
3
+ exceptbot/apps.py,sha256=dm-qZBaqJf9KUtque9jPOFTdTojLLwXWaAVD9w7Z2wc,224
4
+ exceptbot/middleware.py,sha256=XmPDVBtzKj0CwRMHp8F6CmG-4HxZ3PqHj618u0HkLnY,4759
5
+ exceptbot/models.py,sha256=F3qxNdxMNXqDVTiIu5kf_S04AsQYvb2f-rcrR6dMiBU,4588
6
+ exceptbot/permissions.py,sha256=ifz3DSAQ3Btv23N8v5liOv_wrhqSvKFpwvB2NFyLC0k,353
7
+ exceptbot/serializers.py,sha256=7HwGNWpaP0AvWDRvFgB7PcXRNMvj6zLUAhabvaSIrM4,1812
8
+ exceptbot/tests.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ exceptbot/urls.py,sha256=6NMAd96zoNezSvGZbV3bXQY_MIVSUBr5raBQNBm3OCs,2117
10
+ exceptbot/views.py,sha256=dAeEXN5dOvAzQG3k0WKdwu8WMwqVyioKWEnByzjcMD8,8895
11
+ exceptbot/migrations/0001_initial.py,sha256=vGj62lOh87tIA5E6dXyapcGUhv3qu56-yv_nMsFilCU,3916
12
+ exceptbot/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ exceptbot_drf-1.0.0.dist-info/licenses/LICENSE.md,sha256=jCLL2JKM45eawXN10hanKHEo1VBGbMcWMH6hCpmE_QU,2388
14
+ exceptbot_drf-1.0.0.dist-info/METADATA,sha256=-EuyI0wfelURRf9ADaP8eEleac9RQR4Rdbb0s7zGOkQ,8775
15
+ exceptbot_drf-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
16
+ exceptbot_drf-1.0.0.dist-info/top_level.txt,sha256=KMKHsV3dV3nn3xSzh8DeAasL87GKqVPubPFvCMiedQI,10
17
+ exceptbot_drf-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+