django-cfg 1.4.120__py3-none-any.whl → 1.5.2__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.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +8 -4
- django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
- django_cfg/apps/dashboard/TRANSACTION_FIX.md +73 -0
- django_cfg/apps/dashboard/serializers/__init__.py +0 -12
- django_cfg/apps/dashboard/serializers/activity.py +1 -1
- django_cfg/apps/dashboard/services/__init__.py +0 -2
- django_cfg/apps/dashboard/services/charts_service.py +4 -3
- django_cfg/apps/dashboard/services/statistics_service.py +11 -2
- django_cfg/apps/dashboard/services/system_health_service.py +64 -106
- django_cfg/apps/dashboard/urls.py +0 -2
- django_cfg/apps/dashboard/views/__init__.py +0 -2
- django_cfg/apps/dashboard/views/commands_views.py +3 -6
- django_cfg/apps/dashboard/views/overview_views.py +14 -13
- django_cfg/apps/grpc/__init__.py +9 -0
- django_cfg/apps/grpc/admin/__init__.py +11 -0
- django_cfg/apps/{tasks → grpc}/admin/config.py +32 -41
- django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
- django_cfg/apps/grpc/apps.py +28 -0
- django_cfg/apps/grpc/auth/__init__.py +9 -0
- django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
- django_cfg/apps/grpc/interceptors/__init__.py +19 -0
- django_cfg/apps/grpc/interceptors/errors.py +241 -0
- django_cfg/apps/grpc/interceptors/logging.py +270 -0
- django_cfg/apps/grpc/interceptors/metrics.py +306 -0
- django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
- django_cfg/apps/grpc/management/__init__.py +1 -0
- django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
- django_cfg/apps/grpc/managers/__init__.py +10 -0
- django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
- django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
- django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
- django_cfg/apps/grpc/models/__init__.py +9 -0
- django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
- django_cfg/apps/grpc/serializers/__init__.py +23 -0
- django_cfg/apps/grpc/serializers/health.py +18 -0
- django_cfg/apps/grpc/serializers/requests.py +18 -0
- django_cfg/apps/grpc/serializers/services.py +50 -0
- django_cfg/apps/grpc/serializers/stats.py +22 -0
- django_cfg/apps/grpc/services/__init__.py +16 -0
- django_cfg/apps/grpc/services/base.py +375 -0
- django_cfg/apps/grpc/services/discovery.py +415 -0
- django_cfg/apps/grpc/urls.py +23 -0
- django_cfg/apps/grpc/utils/__init__.py +13 -0
- django_cfg/apps/grpc/utils/proto_gen.py +423 -0
- django_cfg/apps/grpc/views/__init__.py +9 -0
- django_cfg/apps/grpc/views/monitoring.py +497 -0
- django_cfg/apps/knowbase/apps.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +7 -9
- django_cfg/apps/maintenance/admin/site_admin.py +5 -4
- django_cfg/apps/newsletter/admin/newsletter_admin.py +12 -11
- django_cfg/apps/payments/admin/balance_admin.py +26 -36
- django_cfg/apps/payments/admin/payment_admin.py +65 -85
- django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
- django_cfg/apps/rq/__init__.py +9 -0
- django_cfg/apps/rq/apps.py +80 -0
- django_cfg/apps/rq/management/__init__.py +1 -0
- django_cfg/apps/rq/management/commands/__init__.py +1 -0
- django_cfg/apps/rq/management/commands/rqscheduler.py +31 -0
- django_cfg/apps/rq/management/commands/rqstats.py +33 -0
- django_cfg/apps/rq/management/commands/rqworker.py +31 -0
- django_cfg/apps/rq/management/commands/rqworker_pool.py +27 -0
- django_cfg/apps/rq/serializers/__init__.py +40 -0
- django_cfg/apps/rq/serializers/health.py +60 -0
- django_cfg/apps/rq/serializers/job.py +100 -0
- django_cfg/apps/rq/serializers/queue.py +80 -0
- django_cfg/apps/rq/serializers/schedule.py +178 -0
- django_cfg/apps/rq/serializers/testing.py +139 -0
- django_cfg/apps/rq/serializers/worker.py +58 -0
- django_cfg/apps/rq/services/__init__.py +25 -0
- django_cfg/apps/rq/services/config_helper.py +233 -0
- django_cfg/apps/rq/services/models/README.md +417 -0
- django_cfg/apps/rq/services/models/__init__.py +30 -0
- django_cfg/apps/rq/services/models/event.py +123 -0
- django_cfg/apps/rq/services/models/job.py +99 -0
- django_cfg/apps/rq/services/models/queue.py +92 -0
- django_cfg/apps/rq/services/models/worker.py +104 -0
- django_cfg/apps/rq/services/rq_converters.py +183 -0
- django_cfg/apps/rq/tasks/__init__.py +23 -0
- django_cfg/apps/rq/tasks/demo_tasks.py +284 -0
- django_cfg/apps/rq/urls.py +54 -0
- django_cfg/apps/rq/views/__init__.py +19 -0
- django_cfg/apps/rq/views/jobs.py +882 -0
- django_cfg/apps/rq/views/monitoring.py +248 -0
- django_cfg/apps/rq/views/queues.py +261 -0
- django_cfg/apps/rq/views/schedule.py +400 -0
- django_cfg/apps/rq/views/testing.py +761 -0
- django_cfg/apps/rq/views/workers.py +195 -0
- django_cfg/apps/urls.py +13 -8
- django_cfg/config.py +106 -0
- django_cfg/core/base/config_model.py +16 -26
- django_cfg/core/builders/apps_builder.py +7 -11
- django_cfg/core/generation/integration_generators/__init__.py +3 -6
- django_cfg/core/generation/integration_generators/django_rq.py +80 -0
- django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
- django_cfg/core/generation/orchestrator.py +15 -15
- django_cfg/core/integration/display/startup.py +6 -20
- django_cfg/mixins/__init__.py +2 -0
- django_cfg/mixins/superadmin_api.py +59 -0
- django_cfg/models/__init__.py +3 -3
- django_cfg/models/api/grpc/__init__.py +59 -0
- django_cfg/models/api/grpc/config.py +364 -0
- django_cfg/models/django/__init__.py +3 -3
- django_cfg/models/django/django_rq.py +621 -0
- django_cfg/models/django/revolution_legacy.py +1 -1
- django_cfg/modules/base.py +19 -6
- django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
- django_cfg/modules/django_admin/config/background_task_config.py +4 -4
- django_cfg/modules/django_admin/utils/__init__.py +41 -3
- django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
- django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
- django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
- django_cfg/modules/django_admin/utils/html/badges.py +47 -0
- django_cfg/modules/django_admin/utils/html/base.py +167 -0
- django_cfg/modules/django_admin/utils/html/code.py +87 -0
- django_cfg/modules/django_admin/utils/html/composition.py +205 -0
- django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
- django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
- django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
- django_cfg/modules/django_admin/utils/html/progress.py +127 -0
- django_cfg/modules/django_admin/utils/html_builder.py +55 -408
- django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
- django_cfg/modules/django_unfold/navigation.py +21 -18
- django_cfg/pyproject.toml +4 -6
- django_cfg/registry/core.py +4 -7
- django_cfg/registry/modules.py +6 -0
- django_cfg/static/frontend/admin.zip +0 -0
- django_cfg/templates/admin/constance/includes/results_list.html +73 -0
- django_cfg/templates/admin/index.html +187 -62
- django_cfg/templatetags/django_cfg.py +61 -1
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/METADATA +12 -4
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/RECORD +140 -96
- django_cfg/apps/dashboard/permissions.py +0 -48
- django_cfg/apps/dashboard/serializers/django_q2.py +0 -50
- django_cfg/apps/dashboard/services/django_q2_service.py +0 -159
- django_cfg/apps/dashboard/views/django_q2_views.py +0 -79
- django_cfg/apps/tasks/__init__.py +0 -64
- django_cfg/apps/tasks/admin/__init__.py +0 -4
- django_cfg/apps/tasks/admin/task_log.py +0 -265
- django_cfg/apps/tasks/apps.py +0 -15
- django_cfg/apps/tasks/filters/__init__.py +0 -10
- django_cfg/apps/tasks/filters/task_log.py +0 -121
- django_cfg/apps/tasks/migrations/0001_initial.py +0 -196
- django_cfg/apps/tasks/migrations/0002_delete_tasklog.py +0 -16
- django_cfg/apps/tasks/models/__init__.py +0 -4
- django_cfg/apps/tasks/models/task_log.py +0 -246
- django_cfg/apps/tasks/serializers/__init__.py +0 -28
- django_cfg/apps/tasks/serializers/task_log.py +0 -249
- django_cfg/apps/tasks/services/__init__.py +0 -10
- django_cfg/apps/tasks/services/client/__init__.py +0 -7
- django_cfg/apps/tasks/services/client/client.py +0 -234
- django_cfg/apps/tasks/services/config_helper.py +0 -63
- django_cfg/apps/tasks/services/sync.py +0 -204
- django_cfg/apps/tasks/urls.py +0 -16
- django_cfg/apps/tasks/views/__init__.py +0 -10
- django_cfg/apps/tasks/views/task_log.py +0 -41
- django_cfg/apps/tasks/views/task_log_base.py +0 -41
- django_cfg/apps/tasks/views/task_log_overview.py +0 -100
- django_cfg/apps/tasks/views/task_log_related.py +0 -41
- django_cfg/apps/tasks/views/task_log_stats.py +0 -91
- django_cfg/apps/tasks/views/task_log_timeline.py +0 -81
- django_cfg/core/generation/integration_generators/django_q2.py +0 -133
- django_cfg/core/generation/integration_generators/tasks.py +0 -88
- django_cfg/models/django/django_q2.py +0 -514
- django_cfg/models/tasks/__init__.py +0 -49
- django_cfg/models/tasks/backends.py +0 -122
- django_cfg/models/tasks/config.py +0 -209
- django_cfg/models/tasks/utils.py +0 -162
- django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
- django_cfg/modules/django_q2/README.md +0 -140
- django_cfg/modules/django_q2/__init__.py +0 -8
- django_cfg/modules/django_q2/apps.py +0 -107
- django_cfg/modules/django_q2/management/commands/__init__.py +0 -0
- django_cfg/modules/django_q2/management/commands/sync_django_q_schedules.py +0 -74
- /django_cfg/apps/{tasks/migrations → grpc/management/commands}/__init__.py +0 -0
- /django_cfg/{modules/django_q2/management → apps/grpc/migrations}/__init__.py +0 -0
- /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
- /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -209,93 +209,69 @@ class PaymentAdmin(PydanticAdmin):
|
|
|
209
209
|
age = timezone.now() - obj.created_at
|
|
210
210
|
age_text = f"{age.days} days, {age.seconds // 3600} hours"
|
|
211
211
|
|
|
212
|
-
# Build details list
|
|
213
|
-
details = []
|
|
214
|
-
|
|
215
|
-
# Basic info
|
|
216
|
-
details.append(self.html.inline([
|
|
217
|
-
self.html.span("Internal ID:", "font-semibold"),
|
|
218
|
-
self.html.span(obj.internal_payment_id, "")
|
|
219
|
-
], separator=" "))
|
|
220
|
-
|
|
221
|
-
details.append(self.html.inline([
|
|
222
|
-
self.html.span("Age:", "font-semibold"),
|
|
223
|
-
self.html.span(age_text, "")
|
|
224
|
-
], separator=" "))
|
|
225
|
-
|
|
226
|
-
# Provider info
|
|
227
|
-
if obj.provider_payment_id:
|
|
228
|
-
details.append(self.html.inline([
|
|
229
|
-
self.html.span("Provider Payment ID:", "font-semibold"),
|
|
230
|
-
self.html.span(obj.provider_payment_id, "")
|
|
231
|
-
], separator=" "))
|
|
232
|
-
|
|
233
212
|
# Transaction details
|
|
213
|
+
transaction_value = None
|
|
234
214
|
if obj.transaction_hash:
|
|
235
215
|
explorer_link = obj.get_explorer_link()
|
|
236
216
|
if explorer_link:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
217
|
+
transaction_value = self.html.link(
|
|
218
|
+
explorer_link,
|
|
219
|
+
f"{obj.transaction_hash[:16]}...",
|
|
220
|
+
target="_blank"
|
|
221
|
+
)
|
|
241
222
|
else:
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
223
|
+
transaction_value = self.html.code(obj.transaction_hash)
|
|
224
|
+
|
|
225
|
+
return self.html.breakdown(
|
|
226
|
+
self.html.key_value("Internal ID", obj.internal_payment_id),
|
|
227
|
+
self.html.key_value("Age", age_text),
|
|
228
|
+
self.html.key_value(
|
|
229
|
+
"Provider Payment ID",
|
|
230
|
+
obj.provider_payment_id
|
|
231
|
+
) if obj.provider_payment_id else None,
|
|
232
|
+
self.html.key_value(
|
|
233
|
+
"Transaction",
|
|
234
|
+
transaction_value
|
|
235
|
+
) if obj.transaction_hash else None,
|
|
236
|
+
self.html.key_value(
|
|
237
|
+
"Confirmations",
|
|
250
238
|
self.html.badge(str(obj.confirmations_count), variant="info", icon=Icons.CHECK_CIRCLE)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
self.html.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
else
|
|
286
|
-
|
|
287
|
-
self.html.span("Expires At:", "font-semibold"),
|
|
288
|
-
self.html.span(str(obj.expires_at), "")
|
|
289
|
-
], separator=" "))
|
|
290
|
-
|
|
291
|
-
# Description
|
|
292
|
-
if obj.description:
|
|
293
|
-
details.append(self.html.inline([
|
|
294
|
-
self.html.span("Description:", "font-semibold"),
|
|
295
|
-
self.html.span(obj.description, "")
|
|
296
|
-
], separator=" "))
|
|
297
|
-
|
|
298
|
-
return "<br>".join(details)
|
|
239
|
+
) if obj.confirmations_count > 0 else None,
|
|
240
|
+
self.html.key_value(
|
|
241
|
+
"Pay Address",
|
|
242
|
+
self.html.code(obj.pay_address)
|
|
243
|
+
) if obj.pay_address else None,
|
|
244
|
+
self.html.key_value(
|
|
245
|
+
"Pay Amount",
|
|
246
|
+
self.html.inline(
|
|
247
|
+
self.html.number(obj.pay_amount, precision=8),
|
|
248
|
+
obj.currency.token,
|
|
249
|
+
separator=" "
|
|
250
|
+
)
|
|
251
|
+
) if obj.pay_amount else None,
|
|
252
|
+
self.html.key_value(
|
|
253
|
+
"Actual Amount",
|
|
254
|
+
self.html.inline(
|
|
255
|
+
self.html.number(obj.actual_amount, precision=8),
|
|
256
|
+
obj.currency.token,
|
|
257
|
+
separator=" "
|
|
258
|
+
)
|
|
259
|
+
) if obj.actual_amount else None,
|
|
260
|
+
self.html.key_value(
|
|
261
|
+
"Payment URL",
|
|
262
|
+
self.html.link(obj.payment_url, "Open", target="_blank")
|
|
263
|
+
) if obj.payment_url else None,
|
|
264
|
+
self.html.key_value(
|
|
265
|
+
"Expired",
|
|
266
|
+
self.html.badge(f"Yes ({obj.expires_at})", variant="danger", icon=Icons.ERROR)
|
|
267
|
+
) if obj.expires_at and obj.is_expired else (
|
|
268
|
+
self.html.key_value("Expires At", str(obj.expires_at)) if obj.expires_at else None
|
|
269
|
+
),
|
|
270
|
+
self.html.key_value(
|
|
271
|
+
"Description",
|
|
272
|
+
obj.description
|
|
273
|
+
) if obj.description else None
|
|
274
|
+
)
|
|
299
275
|
|
|
300
276
|
payment_details_display.short_description = "Payment Details"
|
|
301
277
|
|
|
@@ -306,8 +282,12 @@ class PaymentAdmin(PydanticAdmin):
|
|
|
306
282
|
|
|
307
283
|
qr_url = obj.get_qr_code_url(size=200)
|
|
308
284
|
if qr_url:
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
285
|
+
from django.utils.html import format_html
|
|
286
|
+
img_html = format_html('<img src="{}" alt="QR Code" style="max-width:200px;">', qr_url)
|
|
287
|
+
caption = self.html.inline(
|
|
288
|
+
self.html.text("Scan to pay:", size="sm"),
|
|
289
|
+
self.html.code(obj.pay_address),
|
|
290
|
+
separator=" "
|
|
312
291
|
)
|
|
313
|
-
|
|
292
|
+
return self.html.breakdown(img_html, caption)
|
|
293
|
+
return self.html.text(f"Address: {obj.pay_address}", size="sm")
|
|
@@ -251,105 +251,70 @@ class WithdrawalRequestAdmin(PydanticAdmin):
|
|
|
251
251
|
if not obj.pk:
|
|
252
252
|
return "Save to see details"
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
self.html.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
self.html.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
self.html.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
self.html.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
self.html.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
self.html.span("Admin Notes:", "font-semibold"),
|
|
320
|
-
self.html.span(obj.admin_notes, "")
|
|
321
|
-
], separator=" "))
|
|
322
|
-
|
|
323
|
-
if obj.transaction_hash:
|
|
324
|
-
details.append(self.html.inline([
|
|
325
|
-
self.html.span("Transaction Hash:", "font-semibold"),
|
|
326
|
-
self.html.span(f"<code>{obj.transaction_hash}</code>", "")
|
|
327
|
-
], separator=" "))
|
|
328
|
-
|
|
329
|
-
if obj.crypto_amount:
|
|
330
|
-
details.append(self.html.inline([
|
|
331
|
-
self.html.span("Crypto Amount:", "font-semibold"),
|
|
332
|
-
self.html.span(f"{obj.crypto_amount:.8f} {obj.currency.token}", "")
|
|
333
|
-
], separator=" "))
|
|
334
|
-
|
|
335
|
-
if obj.approved_at:
|
|
336
|
-
details.append(self.html.inline([
|
|
337
|
-
self.html.span("Approved At:", "font-semibold"),
|
|
338
|
-
self.html.span(str(obj.approved_at), "")
|
|
339
|
-
], separator=" "))
|
|
340
|
-
|
|
341
|
-
if obj.completed_at:
|
|
342
|
-
details.append(self.html.inline([
|
|
343
|
-
self.html.span("Completed At:", "font-semibold"),
|
|
344
|
-
self.html.span(str(obj.completed_at), "")
|
|
345
|
-
], separator=" "))
|
|
346
|
-
|
|
347
|
-
if obj.rejected_at:
|
|
348
|
-
details.append(self.html.inline([
|
|
349
|
-
self.html.span("Rejected At:", "font-semibold"),
|
|
350
|
-
self.html.span(str(obj.rejected_at), "")
|
|
351
|
-
], separator=" "))
|
|
352
|
-
|
|
353
|
-
return "<br>".join(details)
|
|
254
|
+
return self.html.breakdown(
|
|
255
|
+
self.html.key_value("Withdrawal ID", str(obj.id)),
|
|
256
|
+
self.html.key_value(
|
|
257
|
+
"User",
|
|
258
|
+
f"{obj.user.username} ({obj.user.email})"
|
|
259
|
+
),
|
|
260
|
+
self.html.key_value(
|
|
261
|
+
"Amount",
|
|
262
|
+
self.html.number(obj.amount_usd, precision=2, prefix="$", suffix=" USD")
|
|
263
|
+
),
|
|
264
|
+
self.html.key_value("Currency", obj.currency.code),
|
|
265
|
+
self.html.key_value(
|
|
266
|
+
"Wallet Address",
|
|
267
|
+
self.html.code(obj.wallet_address)
|
|
268
|
+
),
|
|
269
|
+
self.html.key_value("Status", obj.get_status_display()),
|
|
270
|
+
self.html.key_value(
|
|
271
|
+
"Network Fee",
|
|
272
|
+
self.html.number(obj.network_fee_usd, precision=2, prefix="$", suffix=" USD")
|
|
273
|
+
) if obj.network_fee_usd else None,
|
|
274
|
+
self.html.key_value(
|
|
275
|
+
"Service Fee",
|
|
276
|
+
self.html.number(obj.service_fee_usd, precision=2, prefix="$", suffix=" USD")
|
|
277
|
+
) if obj.service_fee_usd else None,
|
|
278
|
+
self.html.key_value(
|
|
279
|
+
"Total Fee",
|
|
280
|
+
self.html.number(obj.total_fee_usd, precision=2, prefix="$", suffix=" USD")
|
|
281
|
+
) if obj.total_fee_usd else None,
|
|
282
|
+
self.html.key_value(
|
|
283
|
+
"Final Amount",
|
|
284
|
+
self.html.number(obj.final_amount_usd, precision=2, prefix="$", suffix=" USD")
|
|
285
|
+
) if obj.final_amount_usd else None,
|
|
286
|
+
self.html.key_value(
|
|
287
|
+
"Approved By",
|
|
288
|
+
obj.admin_user.username
|
|
289
|
+
) if obj.admin_user else None,
|
|
290
|
+
self.html.key_value(
|
|
291
|
+
"Admin Notes",
|
|
292
|
+
obj.admin_notes
|
|
293
|
+
) if obj.admin_notes else None,
|
|
294
|
+
self.html.key_value(
|
|
295
|
+
"Transaction Hash",
|
|
296
|
+
self.html.code(obj.transaction_hash)
|
|
297
|
+
) if obj.transaction_hash else None,
|
|
298
|
+
self.html.key_value(
|
|
299
|
+
"Crypto Amount",
|
|
300
|
+
self.html.inline(
|
|
301
|
+
self.html.number(obj.crypto_amount, precision=8),
|
|
302
|
+
obj.currency.token,
|
|
303
|
+
separator=" "
|
|
304
|
+
)
|
|
305
|
+
) if obj.crypto_amount else None,
|
|
306
|
+
self.html.key_value(
|
|
307
|
+
"Approved At",
|
|
308
|
+
str(obj.approved_at)
|
|
309
|
+
) if obj.approved_at else None,
|
|
310
|
+
self.html.key_value(
|
|
311
|
+
"Completed At",
|
|
312
|
+
str(obj.completed_at)
|
|
313
|
+
) if obj.completed_at else None,
|
|
314
|
+
self.html.key_value(
|
|
315
|
+
"Rejected At",
|
|
316
|
+
str(obj.rejected_at)
|
|
317
|
+
) if obj.rejected_at else None
|
|
318
|
+
)
|
|
354
319
|
|
|
355
320
|
withdrawal_details_display.short_description = "Withdrawal Details"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AppConfig for Django-RQ integration with monitoring and API capabilities.
|
|
3
|
+
|
|
4
|
+
This app provides REST API endpoints for Django-RQ task queue monitoring,
|
|
5
|
+
management, and statistics. It wraps django-rq's functionality with modern
|
|
6
|
+
DRF ViewSets and unified django-cfg patterns.
|
|
7
|
+
|
|
8
|
+
Features:
|
|
9
|
+
- REST API for monitoring queues, workers, and jobs
|
|
10
|
+
- Prometheus metrics integration
|
|
11
|
+
- Enhanced monitoring interfaces
|
|
12
|
+
- Job management (view, requeue, delete)
|
|
13
|
+
- Integration with django-cfg ecosystem (Centrifugo, auth)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from django.apps import AppConfig
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RQAppConfig(AppConfig):
|
|
20
|
+
"""
|
|
21
|
+
AppConfig for Django-RQ monitoring and management application.
|
|
22
|
+
|
|
23
|
+
Provides:
|
|
24
|
+
- REST API endpoints for monitoring
|
|
25
|
+
- Prometheus metrics export
|
|
26
|
+
- Job and queue management
|
|
27
|
+
- Worker statistics
|
|
28
|
+
- Integration with django-cfg authentication
|
|
29
|
+
|
|
30
|
+
Usage:
|
|
31
|
+
Add to INSTALLED_APPS:
|
|
32
|
+
INSTALLED_APPS = [
|
|
33
|
+
...
|
|
34
|
+
'django_rq', # Required: django-rq core
|
|
35
|
+
'django_cfg.apps.rq', # Django-CFG RQ monitoring
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
Configure in django-cfg config:
|
|
39
|
+
class MyConfig(BaseConfig):
|
|
40
|
+
django_rq: DjangoRQConfig = DjangoRQConfig(
|
|
41
|
+
enabled=True,
|
|
42
|
+
queues={
|
|
43
|
+
'default': {
|
|
44
|
+
'host': 'localhost',
|
|
45
|
+
'port': 6379,
|
|
46
|
+
'db': 0,
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
prometheus_enabled=True,
|
|
50
|
+
)
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
54
|
+
name = 'django_cfg.apps.rq'
|
|
55
|
+
verbose_name = 'Django-CFG RQ Monitoring'
|
|
56
|
+
label = 'django_cfg_rq'
|
|
57
|
+
|
|
58
|
+
def ready(self):
|
|
59
|
+
"""
|
|
60
|
+
Initialize the app when Django starts.
|
|
61
|
+
|
|
62
|
+
Registers:
|
|
63
|
+
- Admin interfaces (if not already registered)
|
|
64
|
+
- Signal handlers for monitoring
|
|
65
|
+
- Scheduled jobs from config
|
|
66
|
+
"""
|
|
67
|
+
# Import admin to register custom admin classes
|
|
68
|
+
try:
|
|
69
|
+
from . import admin # noqa: F401
|
|
70
|
+
except ImportError:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
# Register scheduled jobs from config (runs once on startup)
|
|
74
|
+
try:
|
|
75
|
+
from .services import register_schedules_from_config
|
|
76
|
+
register_schedules_from_config()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
from django_cfg.modules.django_logging import get_logger
|
|
79
|
+
logger = get_logger("rq.apps")
|
|
80
|
+
logger.warning(f"Failed to register schedules: {e}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Management commands for Django-RQ integration."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Django-RQ management commands for django-cfg."""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django-CFG wrapper for django-rq rqscheduler command.
|
|
3
|
+
|
|
4
|
+
Runs the RQ scheduler daemon for scheduled/periodic jobs.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
python manage.py rqscheduler
|
|
8
|
+
python manage.py rqscheduler --queue default
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from django_rq.management.commands.rqscheduler import Command as DjangoRQSchedulerCommand
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Command(DjangoRQSchedulerCommand):
|
|
15
|
+
"""
|
|
16
|
+
Runs the RQ scheduler daemon.
|
|
17
|
+
|
|
18
|
+
The scheduler handles:
|
|
19
|
+
- Scheduled jobs (enqueue at specific time)
|
|
20
|
+
- Periodic jobs (cron-like scheduling)
|
|
21
|
+
- Delayed job execution
|
|
22
|
+
|
|
23
|
+
Inherits all functionality from django-rq's rqscheduler command.
|
|
24
|
+
|
|
25
|
+
Common options:
|
|
26
|
+
--queue QUEUE Queue to schedule jobs on (default: 'default')
|
|
27
|
+
--interval SECONDS Polling interval (default: 1)
|
|
28
|
+
--pid FILE Write PID to file
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
help = 'Runs RQ scheduler daemon for django-cfg (wrapper for django-rq rqscheduler)'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django-CFG wrapper for django-rq rqstats command.
|
|
3
|
+
|
|
4
|
+
Displays real-time statistics about RQ queues and workers.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
python manage.py rqstats
|
|
8
|
+
python manage.py rqstats --interval 5
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from django_rq.management.commands.rqstats import Command as DjangoRQStatsCommand
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Command(DjangoRQStatsCommand):
|
|
15
|
+
"""
|
|
16
|
+
Displays real-time RQ statistics in terminal.
|
|
17
|
+
|
|
18
|
+
Shows:
|
|
19
|
+
- Queue sizes (queued, started, finished, failed)
|
|
20
|
+
- Worker count and status
|
|
21
|
+
- Job processing rates
|
|
22
|
+
- Updates in real-time
|
|
23
|
+
|
|
24
|
+
Inherits all functionality from django-rq's rqstats command.
|
|
25
|
+
|
|
26
|
+
Common options:
|
|
27
|
+
--interval SECONDS Update interval (default: 1)
|
|
28
|
+
--raw Show raw numbers (no colors)
|
|
29
|
+
--only-queues Show only queue statistics
|
|
30
|
+
--only-workers Show only worker statistics
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
help = 'Shows real-time RQ statistics for django-cfg (wrapper for django-rq rqstats)'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django-CFG wrapper for django-rq rqworker command.
|
|
3
|
+
|
|
4
|
+
This is a simple proxy that inherits all functionality from django-rq's rqworker.
|
|
5
|
+
Allows running: python manage.py rqworker [queues]
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
python manage.py rqworker default
|
|
9
|
+
python manage.py rqworker high default low
|
|
10
|
+
python manage.py rqworker default --with-scheduler
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from django_rq.management.commands.rqworker import Command as DjangoRQWorkerCommand
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Command(DjangoRQWorkerCommand):
|
|
17
|
+
"""
|
|
18
|
+
Runs RQ workers on specified queues.
|
|
19
|
+
|
|
20
|
+
Inherits all functionality from django-rq's rqworker command.
|
|
21
|
+
See django-rq documentation for available options.
|
|
22
|
+
|
|
23
|
+
Common options:
|
|
24
|
+
--burst Run in burst mode (exit when queue is empty)
|
|
25
|
+
--with-scheduler Run worker with embedded scheduler
|
|
26
|
+
--name NAME Custom worker name
|
|
27
|
+
--worker-ttl SEC Worker timeout (default: 420)
|
|
28
|
+
--sentry-dsn DSN Report exceptions to Sentry
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
help = 'Runs RQ workers for django-cfg (wrapper for django-rq rqworker)'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django-CFG wrapper for django-rq rqworker-pool command.
|
|
3
|
+
|
|
4
|
+
Runs multiple RQ workers in a pool for better performance.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
python manage.py rqworker_pool default --num-workers 4
|
|
8
|
+
python manage.py rqworker_pool high default --num-workers 8
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from django_rq.management.commands.rqworker_pool import Command as DjangoRQWorkerPoolCommand
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Command(DjangoRQWorkerPoolCommand):
|
|
15
|
+
"""
|
|
16
|
+
Runs a pool of RQ workers for improved throughput.
|
|
17
|
+
|
|
18
|
+
Inherits all functionality from django-rq's rqworker-pool command.
|
|
19
|
+
Creates multiple worker processes to handle jobs in parallel.
|
|
20
|
+
|
|
21
|
+
Common options:
|
|
22
|
+
--num-workers N Number of worker processes (default: CPU count)
|
|
23
|
+
--burst Run in burst mode
|
|
24
|
+
--name NAME Worker name prefix
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
help = 'Runs a pool of RQ workers for django-cfg (wrapper for django-rq rqworker-pool)'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DRF serializers for Django-RQ monitoring API.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .health import HealthCheckSerializer, RQConfigSerializer
|
|
6
|
+
from .queue import QueueStatsSerializer, QueueDetailSerializer
|
|
7
|
+
from .worker import WorkerSerializer, WorkerStatsSerializer
|
|
8
|
+
from .job import JobListSerializer, JobDetailSerializer, JobActionResponseSerializer
|
|
9
|
+
from .schedule import (
|
|
10
|
+
ScheduleCreateSerializer,
|
|
11
|
+
ScheduledJobSerializer,
|
|
12
|
+
ScheduleActionResponseSerializer,
|
|
13
|
+
)
|
|
14
|
+
from .testing import (
|
|
15
|
+
TestScenarioSerializer,
|
|
16
|
+
RunDemoRequestSerializer,
|
|
17
|
+
StressTestRequestSerializer,
|
|
18
|
+
TestingActionResponseSerializer,
|
|
19
|
+
CleanupRequestSerializer,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
'HealthCheckSerializer',
|
|
24
|
+
'RQConfigSerializer',
|
|
25
|
+
'QueueStatsSerializer',
|
|
26
|
+
'QueueDetailSerializer',
|
|
27
|
+
'WorkerSerializer',
|
|
28
|
+
'WorkerStatsSerializer',
|
|
29
|
+
'JobListSerializer',
|
|
30
|
+
'JobDetailSerializer',
|
|
31
|
+
'JobActionResponseSerializer',
|
|
32
|
+
'ScheduleCreateSerializer',
|
|
33
|
+
'ScheduledJobSerializer',
|
|
34
|
+
'ScheduleActionResponseSerializer',
|
|
35
|
+
'TestScenarioSerializer',
|
|
36
|
+
'RunDemoRequestSerializer',
|
|
37
|
+
'StressTestRequestSerializer',
|
|
38
|
+
'TestingActionResponseSerializer',
|
|
39
|
+
'CleanupRequestSerializer',
|
|
40
|
+
]
|