django-cfg 1.3.13__py3-none-any.whl → 1.4.3__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/user_admin.py +39 -16
- django_cfg/apps/accounts/serializers/profile.py +1 -1
- django_cfg/apps/accounts/services/otp_service.py +18 -11
- django_cfg/apps/accounts/signals.py +15 -24
- django_cfg/apps/accounts/utils/notifications.py +217 -358
- django_cfg/apps/accounts/views/otp.py +2 -2
- django_cfg/apps/accounts/views/webhook.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +1 -1
- django_cfg/apps/agents/examples/__init__.py +3 -0
- django_cfg/apps/agents/examples/simple_example.py +161 -0
- django_cfg/apps/api/commands/views.py +66 -83
- django_cfg/apps/api/health/drf_views.py +269 -0
- django_cfg/apps/api/health/serializers.py +45 -0
- django_cfg/apps/api/health/urls.py +6 -1
- django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
- django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
- django_cfg/apps/knowbase/admin/document_admin.py +136 -270
- django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
- django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
- django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
- django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
- django_cfg/apps/knowbase/config/constance_fields.py +1 -1
- django_cfg/apps/knowbase/config/settings.py +2 -2
- django_cfg/apps/knowbase/examples/__init__.py +3 -0
- django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
- django_cfg/apps/knowbase/mixins/__init__.py +19 -2
- django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
- django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
- django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
- django_cfg/apps/knowbase/mixins/creator.py +10 -10
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
- django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
- django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
- django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
- django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
- django_cfg/apps/knowbase/mixins/service.py +2 -2
- django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
- django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
- django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
- django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
- django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
- django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
- django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
- django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
- django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
- django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
- django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
- django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
- django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
- django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
- django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
- django_cfg/apps/knowbase/services/embedding/models.py +18 -14
- django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
- django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
- django_cfg/apps/leads/tests.py +1 -1
- django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
- django_cfg/apps/payments/admin/balance_admin.py +1 -1
- django_cfg/apps/payments/admin/currencies_admin.py +1 -1
- django_cfg/apps/payments/admin/payments_admin.py +1 -1
- django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
- django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
- django_cfg/apps/payments/admin_interface/views/base.py +29 -2
- django_cfg/apps/payments/apps.py +1 -1
- django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
- django_cfg/apps/payments/config/helpers.py +3 -2
- django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
- django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
- django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
- django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
- django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
- django_cfg/apps/payments/management/commands/test_providers.py +1 -1
- django_cfg/apps/payments/middleware/api_access.py +1 -1
- django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
- django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
- django_cfg/apps/payments/models/balance.py +2 -2
- django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
- django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
- django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
- django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
- django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
- django_cfg/apps/payments/models/payments.py +2 -2
- django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
- django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
- django_cfg/apps/payments/services/core/base.py +1 -1
- django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
- django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
- django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
- django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
- django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
- django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
- django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
- django_cfg/apps/payments/services/core/payment_service.py +124 -612
- django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
- django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
- django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
- django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
- django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
- django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
- django_cfg/apps/payments/services/providers/base.py +1 -1
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
- django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
- django_cfg/apps/payments/services/providers/registry.py +1 -1
- django_cfg/apps/payments/services/providers/sync_service.py +1 -1
- django_cfg/apps/payments/signals/__init__.py +1 -1
- django_cfg/apps/payments/signals/api_key_signals.py +1 -1
- django_cfg/apps/payments/signals/balance_signals.py +1 -1
- django_cfg/apps/payments/signals/payment_signals.py +1 -1
- django_cfg/apps/payments/signals/subscription_signals.py +1 -1
- django_cfg/apps/payments/views/api/api_keys.py +1 -1
- django_cfg/apps/payments/views/api/balances.py +1 -1
- django_cfg/apps/payments/views/api/base.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +1 -1
- django_cfg/apps/payments/views/api/payments.py +1 -1
- django_cfg/apps/payments/views/api/subscriptions.py +1 -1
- django_cfg/apps/payments/views/api/webhooks.py +1 -1
- django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
- django_cfg/apps/payments/views/serializers/balances.py +1 -1
- django_cfg/apps/payments/views/serializers/currencies.py +1 -1
- django_cfg/apps/payments/views/serializers/payments.py +1 -1
- django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
- django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
- django_cfg/apps/support/admin/support_admin.py +21 -13
- django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
- django_cfg/apps/support/utils/support_email_service.py +1 -1
- django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
- django_cfg/apps/tasks/utils/simulator.py +1 -1
- django_cfg/apps/tasks/views/dashboard.py +33 -3
- django_cfg/apps/urls.py +5 -1
- django_cfg/cli/README.md +57 -471
- django_cfg/cli/commands/create_project.py +140 -529
- django_cfg/cli/main.py +13 -10
- django_cfg/core/__init__.py +63 -6
- django_cfg/core/base/__init__.py +5 -0
- django_cfg/core/base/config_model.py +652 -0
- django_cfg/core/builders/__init__.py +11 -0
- django_cfg/core/builders/apps_builder.py +258 -0
- django_cfg/core/builders/middleware_builder.py +115 -0
- django_cfg/core/builders/security_builder.py +96 -0
- django_cfg/core/config.py +20 -892
- django_cfg/core/constants.py +69 -0
- django_cfg/core/environment/__init__.py +9 -0
- django_cfg/core/exceptions.py +45 -298
- django_cfg/core/generation/__init__.py +51 -0
- django_cfg/core/generation/core_generators/__init__.py +0 -0
- django_cfg/core/generation/core_generators/settings.py +90 -0
- django_cfg/core/generation/core_generators/static.py +82 -0
- django_cfg/core/generation/core_generators/templates.py +141 -0
- django_cfg/core/generation/data_generators/__init__.py +15 -0
- django_cfg/core/generation/data_generators/cache.py +132 -0
- django_cfg/core/generation/data_generators/database.py +117 -0
- django_cfg/core/generation/generation.py +92 -0
- django_cfg/core/generation/integration_generators/__init__.py +21 -0
- django_cfg/core/generation/integration_generators/api.py +237 -0
- django_cfg/core/generation/integration_generators/sessions.py +65 -0
- django_cfg/core/generation/integration_generators/tailwind.py +54 -0
- django_cfg/core/generation/integration_generators/tasks.py +92 -0
- django_cfg/core/generation/integration_generators/third_party.py +144 -0
- django_cfg/core/generation/orchestrator.py +285 -0
- django_cfg/core/generation/protocols.py +30 -0
- django_cfg/core/generation/security_generators/__init__.py +0 -0
- django_cfg/core/generation/utility_generators/__init__.py +24 -0
- django_cfg/core/generation/utility_generators/email.py +58 -0
- django_cfg/core/generation/utility_generators/i18n.py +66 -0
- django_cfg/core/generation/utility_generators/limits.py +58 -0
- django_cfg/core/generation/utility_generators/logging.py +66 -0
- django_cfg/core/generation/utility_generators/security.py +101 -0
- django_cfg/core/generation/utils/__init__.py +0 -0
- django_cfg/core/generation/utils/helpers.py +32 -0
- django_cfg/core/integration/__init__.py +18 -25
- django_cfg/core/integration/display/startup.py +146 -133
- django_cfg/core/integration/url_integration.py +13 -2
- django_cfg/core/services/__init__.py +5 -0
- django_cfg/core/services/config_service.py +121 -0
- django_cfg/core/state/__init__.py +9 -0
- django_cfg/core/state/registry.py +84 -0
- django_cfg/core/types/__init__.py +15 -0
- django_cfg/core/types/aliases.py +15 -0
- django_cfg/core/types/enums.py +49 -0
- django_cfg/dashboard/DEBUG_README.md +105 -0
- django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
- django_cfg/dashboard/__init__.py +24 -0
- django_cfg/dashboard/components.py +308 -0
- django_cfg/dashboard/debug.py +176 -0
- django_cfg/dashboard/management/__init__.py +0 -0
- django_cfg/dashboard/management/commands/__init__.py +0 -0
- django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
- django_cfg/dashboard/sections/__init__.py +1 -0
- django_cfg/dashboard/sections/base.py +128 -0
- django_cfg/dashboard/sections/commands.py +32 -0
- django_cfg/dashboard/sections/overview.py +394 -0
- django_cfg/dashboard/sections/stats.py +48 -0
- django_cfg/dashboard/sections/system.py +73 -0
- django_cfg/management/commands/check_settings.py +6 -2
- django_cfg/management/commands/clear_constance.py +6 -1
- django_cfg/management/commands/create_token.py +5 -4
- django_cfg/management/commands/generate.py +5 -0
- django_cfg/management/commands/list_urls.py +7 -2
- django_cfg/management/commands/migrate_all.py +6 -2
- django_cfg/management/commands/migrator.py +6 -1
- django_cfg/management/commands/rundramatiq.py +6 -1
- django_cfg/management/commands/rundramatiq_simulator.py +11 -4
- django_cfg/management/commands/runserver_ngrok.py +9 -7
- django_cfg/management/commands/script.py +25 -21
- django_cfg/management/commands/show_config.py +6 -1
- django_cfg/management/commands/show_urls.py +8 -3
- django_cfg/management/commands/superuser.py +5 -4
- django_cfg/management/commands/task_clear.py +8 -3
- django_cfg/management/commands/task_status.py +8 -3
- django_cfg/management/commands/test_email.py +6 -1
- django_cfg/management/commands/test_telegram.py +6 -1
- django_cfg/management/commands/test_twilio.py +6 -1
- django_cfg/management/commands/tree.py +7 -4
- django_cfg/models/__init__.py +88 -3
- django_cfg/models/api/__init__.py +27 -0
- django_cfg/models/{api.py → api/config.py} +1 -1
- django_cfg/models/api/drf/__init__.py +21 -0
- django_cfg/models/api/drf/config.py +101 -0
- django_cfg/models/api/drf/redoc.py +31 -0
- django_cfg/models/api/drf/spectacular.py +129 -0
- django_cfg/models/api/drf/swagger.py +59 -0
- django_cfg/models/{api_keys.py → api/keys.py} +16 -6
- django_cfg/models/{limits.py → api/limits.py} +0 -1
- django_cfg/models/base/__init__.py +14 -0
- django_cfg/models/django/__init__.py +16 -0
- django_cfg/models/{constance.py → django/constance.py} +1 -1
- django_cfg/models/{environment.py → django/environment.py} +1 -1
- django_cfg/models/infrastructure/__init__.py +17 -0
- django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
- django_cfg/models/infrastructure/database/__init__.py +22 -0
- django_cfg/models/infrastructure/database/config.py +265 -0
- django_cfg/models/infrastructure/database/converters.py +91 -0
- django_cfg/models/infrastructure/database/parsers.py +96 -0
- django_cfg/models/infrastructure/database/routing.py +85 -0
- django_cfg/models/infrastructure/database/validators.py +170 -0
- django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
- django_cfg/models/{security.py → infrastructure/security.py} +2 -2
- django_cfg/models/ngrok/__init__.py +11 -0
- django_cfg/models/ngrok/auth.py +37 -0
- django_cfg/models/ngrok/config.py +77 -0
- django_cfg/models/ngrok/tunnel.py +35 -0
- django_cfg/models/payments/__init__.py +20 -0
- django_cfg/models/payments/api_keys.py +57 -0
- django_cfg/models/{payments.py → payments/config.py} +56 -154
- django_cfg/models/payments/providers/__init__.py +15 -0
- django_cfg/models/payments/providers/base.py +25 -0
- django_cfg/models/payments/providers/nowpayments.py +48 -0
- django_cfg/models/services/__init__.py +18 -0
- django_cfg/models/services/base.py +65 -0
- django_cfg/models/{email.py → services/email.py} +1 -1
- django_cfg/models/services/telegram.py +172 -0
- django_cfg/models/tasks/__init__.py +51 -0
- django_cfg/models/tasks/backends.py +250 -0
- django_cfg/models/tasks/config.py +314 -0
- django_cfg/models/tasks/utils.py +174 -0
- django_cfg/modules/base.py +18 -3
- django_cfg/modules/django_admin/decorators/actions.py +1 -1
- django_cfg/modules/django_admin/decorators/display.py +1 -1
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
- django_cfg/modules/django_currency/examples/__init__.py +3 -0
- django_cfg/modules/django_currency/examples/example_database_usage.py +144 -0
- django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
- django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
- django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
- django_cfg/modules/django_drf_theme/README.md +207 -0
- django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
- django_cfg/modules/django_drf_theme/__init__.py +23 -0
- django_cfg/modules/django_drf_theme/apps.py +15 -0
- django_cfg/modules/django_drf_theme/renderers.py +58 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
- django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
- django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
- django_cfg/modules/django_email/__init__.py +14 -0
- django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
- django_cfg/modules/django_email/utils.py +40 -0
- django_cfg/modules/django_health/__init__.py +9 -0
- django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
- django_cfg/modules/django_ipc_client/README.md +346 -0
- django_cfg/modules/django_ipc_client/__init__.py +51 -0
- django_cfg/modules/django_ipc_client/client.py +540 -0
- django_cfg/modules/django_ipc_client/config.py +207 -0
- django_cfg/modules/django_ipc_client/dashboard/README.md +517 -0
- django_cfg/modules/django_ipc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
- django_cfg/modules/django_ipc_client/dashboard/__init__.py +11 -0
- django_cfg/modules/django_ipc_client/dashboard/apps.py +22 -0
- django_cfg/modules/django_ipc_client/dashboard/monitor.py +435 -0
- django_cfg/modules/django_ipc_client/dashboard/static/django_ipc_dashboard/js/dashboard.js +373 -0
- django_cfg/modules/django_ipc_client/dashboard/templates/django_ipc_dashboard/base.html +76 -0
- django_cfg/modules/django_ipc_client/dashboard/templates/django_ipc_dashboard/dashboard.html +200 -0
- django_cfg/modules/django_ipc_client/dashboard/urls.py +22 -0
- django_cfg/modules/django_ipc_client/dashboard/urls_admin.py +9 -0
- django_cfg/modules/django_ipc_client/dashboard/views.py +251 -0
- django_cfg/modules/django_ipc_client/exceptions.py +201 -0
- django_cfg/modules/django_llm/llm/client.py +155 -550
- django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
- django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
- django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
- django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
- django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
- django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
- django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
- django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
- django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
- django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
- django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
- django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
- django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
- django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
- django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
- django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
- django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
- django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
- django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
- django_cfg/modules/django_llm/translator/translator.py +150 -603
- django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
- django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
- django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
- django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
- django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
- django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
- django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
- django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
- django_cfg/modules/django_logging/__init__.py +14 -0
- django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
- django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
- django_cfg/modules/django_ngrok/__init__.py +39 -0
- django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
- django_cfg/modules/django_rpc_old/POETRY.md +344 -0
- django_cfg/modules/django_rpc_old/README.md +397 -0
- django_cfg/modules/django_rpc_old/TESTING.md +358 -0
- django_cfg/modules/django_rpc_old/__init__.py +39 -0
- django_cfg/modules/django_rpc_old/client.py +531 -0
- django_cfg/modules/django_rpc_old/config.py +279 -0
- django_cfg/modules/django_rpc_old/exceptions.py +172 -0
- django_cfg/modules/django_tailwind/README.md +478 -0
- django_cfg/modules/django_tailwind/__init__.py +7 -0
- django_cfg/modules/django_tailwind/apps.py +10 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
- django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
- django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
- django_cfg/modules/django_tasks/__init__.py +29 -0
- django_cfg/modules/django_tasks/factory.py +127 -0
- django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
- django_cfg/modules/django_tasks/settings.py +107 -0
- django_cfg/modules/django_telegram/__init__.py +29 -0
- django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
- django_cfg/modules/django_telegram/utils.py +62 -0
- django_cfg/modules/django_twilio/__init__.py +54 -107
- django_cfg/modules/django_twilio/_imports.py +30 -0
- django_cfg/modules/django_twilio/base.py +192 -0
- django_cfg/modules/django_twilio/email_otp.py +227 -0
- django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
- django_cfg/modules/django_twilio/simple_service.py +1 -2
- django_cfg/modules/django_twilio/sms.py +94 -0
- django_cfg/modules/django_twilio/twilio_service.py +2 -3
- django_cfg/modules/django_twilio/unified.py +310 -0
- django_cfg/modules/django_twilio/utils.py +190 -0
- django_cfg/modules/django_twilio/whatsapp.py +137 -0
- django_cfg/modules/django_unfold/callbacks/base.py +198 -7
- django_cfg/modules/django_unfold/callbacks/main.py +102 -10
- django_cfg/modules/django_unfold/dashboard.py +65 -43
- django_cfg/modules/django_unfold/models/config.py +13 -12
- django_cfg/modules/django_unfold/models/navigation.py +8 -3
- django_cfg/modules/django_unfold/models/tabs.py +2 -2
- django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
- django_cfg/registry/core.py +24 -26
- django_cfg/registry/modules.py +5 -2
- django_cfg/registry/services.py +20 -3
- django_cfg/registry/third_party.py +8 -8
- django_cfg/static/admin/css/dashboard.css +260 -0
- django_cfg/static/admin/js/commands.js +171 -0
- django_cfg/static/admin/js/dashboard.js +126 -0
- django_cfg/templates/admin/components/management_commands.js +375 -0
- django_cfg/templates/admin/components/progress_bar.html +18 -23
- django_cfg/templates/admin/examples/component_class_example.html +156 -0
- django_cfg/templates/admin/index.html +48 -20
- django_cfg/templates/admin/index_new.html +106 -0
- django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
- django_cfg/templates/admin/sections/commands_section.html +626 -0
- django_cfg/templates/admin/sections/overview_section.html +112 -0
- django_cfg/templates/admin/sections/stats_section.html +35 -0
- django_cfg/templates/admin/sections/system_section.html +99 -0
- django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
- django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
- django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
- django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
- django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
- django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
- django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
- django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
- django_cfg/templatetags/django_cfg.py +7 -1
- django_cfg/utils/smart_defaults.py +4 -4
- django_cfg-1.4.3.dist-info/METADATA +533 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/RECORD +432 -195
- django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
- django_cfg/core/generation.py +0 -621
- django_cfg/management/commands/validate_config.py +0 -189
- django_cfg/models/database.py +0 -480
- django_cfg/models/drf.py +0 -272
- django_cfg/models/ngrok.py +0 -122
- django_cfg/models/services.py +0 -440
- django_cfg/models/tasks.py +0 -550
- django_cfg/modules/django_twilio/service.py +0 -942
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/rest_framework/api.html +0 -12
- django_cfg/utils/toolkit.py +0 -703
- django_cfg-1.3.13.dist-info/METADATA +0 -1029
- /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
- /django_cfg/core/{environment.py → environment/detector.py} +0 -0
- /django_cfg/models/{cors.py → api/cors.py} +0 -0
- /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
- /django_cfg/models/{base.py → base/config.py} +0 -0
- /django_cfg/models/{cfg.py → base/module.py} +0 -0
- /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
- /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/licenses/LICENSE +0 -0
@@ -42,7 +42,7 @@ class OTPViewSet(viewsets.GenericViewSet):
|
|
42
42
|
500: OTPErrorResponseSerializer,
|
43
43
|
},
|
44
44
|
)
|
45
|
-
@action(detail=False, methods=["post"], url_path="request")
|
45
|
+
@action(detail=False, methods=["post"], url_path="request", url_name="request")
|
46
46
|
def request_otp(self, request):
|
47
47
|
"""Request OTP code to email or phone."""
|
48
48
|
serializer = OTPRequestSerializer(data=request.data)
|
@@ -111,7 +111,7 @@ class OTPViewSet(viewsets.GenericViewSet):
|
|
111
111
|
410: OTPErrorResponseSerializer,
|
112
112
|
},
|
113
113
|
)
|
114
|
-
@action(detail=False, methods=["post"], url_path="verify")
|
114
|
+
@action(detail=False, methods=["post"], url_path="verify", url_name="verify")
|
115
115
|
def verify_otp(self, request):
|
116
116
|
"""Verify OTP code and return JWT tokens."""
|
117
117
|
serializer = OTPVerifySerializer(data=request.data)
|
@@ -20,7 +20,7 @@ from drf_spectacular.utils import extend_schema, OpenApiParameter
|
|
20
20
|
from drf_spectacular.types import OpenApiTypes
|
21
21
|
from twilio.request_validator import RequestValidator
|
22
22
|
|
23
|
-
from django_cfg.core.
|
23
|
+
from django_cfg.core.state import get_current_config
|
24
24
|
from ..models import TwilioResponse, OTPSecret
|
25
25
|
from ..serializers.webhook import TwilioWebhookSerializer
|
26
26
|
|
@@ -14,7 +14,7 @@ from pydantic_ai.models.openai import OpenAIChatModel
|
|
14
14
|
from pydantic_ai.providers.openrouter import OpenRouterProvider
|
15
15
|
from pydantic_ai.providers.openai import OpenAIProvider
|
16
16
|
|
17
|
-
from django_cfg.modules.
|
17
|
+
from django_cfg.modules.django_logging import get_logger
|
18
18
|
from django_cfg.modules.base import BaseCfgModule
|
19
19
|
from .exceptions import ExecutionError, ConfigurationError
|
20
20
|
|
@@ -0,0 +1,161 @@
|
|
1
|
+
"""
|
2
|
+
Simple example demonstrating Django Orchestrator usage.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import asyncio
|
6
|
+
from dataclasses import dataclass
|
7
|
+
from pydantic import BaseModel
|
8
|
+
from django.contrib.auth.models import User
|
9
|
+
|
10
|
+
from django_cfg.modules.django_orchestrator import DjangoAgent, SimpleOrchestrator, DjangoDeps, RunContext
|
11
|
+
|
12
|
+
|
13
|
+
# Define output model
|
14
|
+
class GreetingResult(BaseModel):
|
15
|
+
"""Result from greeting agent."""
|
16
|
+
greeting: str
|
17
|
+
personalized: bool
|
18
|
+
user_info: str
|
19
|
+
|
20
|
+
|
21
|
+
# Create agent
|
22
|
+
greeting_agent = DjangoAgent[DjangoDeps, GreetingResult](
|
23
|
+
name="greeting_agent",
|
24
|
+
deps_type=DjangoDeps,
|
25
|
+
output_type=GreetingResult,
|
26
|
+
instructions="Generate personalized greetings for users"
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
# Add tools to agent
|
31
|
+
@greeting_agent.tool
|
32
|
+
async def get_user_info(ctx: RunContext[DjangoDeps]) -> str:
|
33
|
+
"""Get user information for personalization."""
|
34
|
+
user = ctx.deps.user
|
35
|
+
return f"User: {user.username}, Email: {user.email}, Joined: {user.date_joined}"
|
36
|
+
|
37
|
+
|
38
|
+
@greeting_agent.tool
|
39
|
+
async def get_time_of_day(ctx: RunContext[DjangoDeps]) -> str:
|
40
|
+
"""Get current time of day for greeting."""
|
41
|
+
from datetime import datetime
|
42
|
+
hour = datetime.now().hour
|
43
|
+
|
44
|
+
if hour < 12:
|
45
|
+
return "morning"
|
46
|
+
elif hour < 17:
|
47
|
+
return "afternoon"
|
48
|
+
else:
|
49
|
+
return "evening"
|
50
|
+
|
51
|
+
|
52
|
+
async def simple_example():
|
53
|
+
"""Run simple orchestrator example."""
|
54
|
+
print("🤖 Django Orchestrator Simple Example")
|
55
|
+
print("=" * 50)
|
56
|
+
|
57
|
+
# Create test user (in real app, get from request)
|
58
|
+
try:
|
59
|
+
user = await User.objects.aget(username='testuser')
|
60
|
+
except User.DoesNotExist:
|
61
|
+
user = await User.objects.acreate_user(
|
62
|
+
username='testuser',
|
63
|
+
email='test@example.com',
|
64
|
+
first_name='Test',
|
65
|
+
last_name='User'
|
66
|
+
)
|
67
|
+
|
68
|
+
# Create dependencies
|
69
|
+
deps = DjangoDeps(user=user)
|
70
|
+
|
71
|
+
# Create orchestrator
|
72
|
+
orchestrator = SimpleOrchestrator()
|
73
|
+
orchestrator.register_agent(greeting_agent)
|
74
|
+
|
75
|
+
# Execute single agent
|
76
|
+
print("\n1. Single Agent Execution:")
|
77
|
+
results = await orchestrator.execute(
|
78
|
+
pattern="sequential",
|
79
|
+
agents=["greeting_agent"],
|
80
|
+
prompt="Create a personalized greeting",
|
81
|
+
deps=deps
|
82
|
+
)
|
83
|
+
|
84
|
+
result = results[0]
|
85
|
+
print(f"✅ Agent: {result.agent_name}")
|
86
|
+
print(f"✅ Output: {result.output}")
|
87
|
+
print(f"✅ Execution Time: {result.execution_time:.2f}s")
|
88
|
+
print(f"✅ Tokens Used: {result.tokens_used}")
|
89
|
+
|
90
|
+
# Get metrics
|
91
|
+
print("\n2. Agent Metrics:")
|
92
|
+
metrics = greeting_agent.get_metrics()
|
93
|
+
for key, value in metrics.items():
|
94
|
+
print(f"📊 {key}: {value}")
|
95
|
+
|
96
|
+
# Get orchestrator metrics
|
97
|
+
print("\n3. Orchestrator Metrics:")
|
98
|
+
orch_metrics = orchestrator.get_metrics()
|
99
|
+
for key, value in orch_metrics.items():
|
100
|
+
print(f"📈 {key}: {value}")
|
101
|
+
|
102
|
+
|
103
|
+
async def multi_agent_example():
|
104
|
+
"""Run multi-agent orchestrator example."""
|
105
|
+
print("\n🤖 Multi-Agent Example")
|
106
|
+
print("=" * 30)
|
107
|
+
|
108
|
+
# Create additional agents
|
109
|
+
analyzer_agent = DjangoAgent[DjangoDeps, BaseModel](
|
110
|
+
name="content_analyzer",
|
111
|
+
deps_type=DjangoDeps,
|
112
|
+
output_type=BaseModel,
|
113
|
+
instructions="Analyze content sentiment and topics"
|
114
|
+
)
|
115
|
+
|
116
|
+
formatter_agent = DjangoAgent[DjangoDeps, BaseModel](
|
117
|
+
name="content_formatter",
|
118
|
+
deps_type=DjangoDeps,
|
119
|
+
output_type=BaseModel,
|
120
|
+
instructions="Format content for display"
|
121
|
+
)
|
122
|
+
|
123
|
+
# Create orchestrator
|
124
|
+
orchestrator = SimpleOrchestrator()
|
125
|
+
orchestrator.register_agent(analyzer_agent)
|
126
|
+
orchestrator.register_agent(formatter_agent)
|
127
|
+
|
128
|
+
# Get user
|
129
|
+
user = await User.objects.aget(username='testuser')
|
130
|
+
deps = DjangoDeps(user=user)
|
131
|
+
|
132
|
+
# Execute sequential workflow
|
133
|
+
print("\n1. Sequential Workflow:")
|
134
|
+
results = await orchestrator.execute(
|
135
|
+
pattern="sequential",
|
136
|
+
agents=["content_analyzer", "content_formatter"],
|
137
|
+
prompt="Analyze and format this content: Hello world!",
|
138
|
+
deps=deps
|
139
|
+
)
|
140
|
+
|
141
|
+
for i, result in enumerate(results, 1):
|
142
|
+
print(f"Step {i} - {result.agent_name}: {result.success}")
|
143
|
+
|
144
|
+
# Execute parallel workflow
|
145
|
+
print("\n2. Parallel Workflow:")
|
146
|
+
results = await orchestrator.execute(
|
147
|
+
pattern="parallel",
|
148
|
+
agents=["content_analyzer", "content_formatter"],
|
149
|
+
prompt="Process content in parallel",
|
150
|
+
deps=deps,
|
151
|
+
max_concurrent=2
|
152
|
+
)
|
153
|
+
|
154
|
+
successful = sum(1 for r in results if r.success)
|
155
|
+
print(f"✅ {successful}/{len(results)} agents completed successfully")
|
156
|
+
|
157
|
+
|
158
|
+
if __name__ == "__main__":
|
159
|
+
# Run examples
|
160
|
+
asyncio.run(simple_example())
|
161
|
+
asyncio.run(multi_agent_example())
|
@@ -21,13 +21,13 @@ from django.utils import timezone
|
|
21
21
|
logger = logging.getLogger(__name__)
|
22
22
|
|
23
23
|
|
24
|
-
def
|
25
|
-
"""Check if user is
|
26
|
-
return user.is_authenticated and user.
|
24
|
+
def is_staff_user(user):
|
25
|
+
"""Check if user is staff member."""
|
26
|
+
return user.is_authenticated and user.is_staff
|
27
27
|
|
28
28
|
|
29
29
|
@require_http_methods(["GET"])
|
30
|
-
@user_passes_test(
|
30
|
+
@user_passes_test(is_staff_user)
|
31
31
|
def list_commands_view(request):
|
32
32
|
"""
|
33
33
|
List all available Django management commands.
|
@@ -81,7 +81,7 @@ def list_commands_view(request):
|
|
81
81
|
|
82
82
|
@csrf_exempt
|
83
83
|
@require_http_methods(["POST"])
|
84
|
-
@user_passes_test(
|
84
|
+
@user_passes_test(is_staff_user)
|
85
85
|
def execute_command_view(request):
|
86
86
|
"""
|
87
87
|
Execute a Django management command and stream output in real-time.
|
@@ -117,106 +117,89 @@ def execute_command_view(request):
|
|
117
117
|
"error": f"Command '{command_name}' not found",
|
118
118
|
"available_commands": list(available_commands.keys()),
|
119
119
|
}, status=400)
|
120
|
-
|
121
|
-
# Security check -
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
if command_name in dangerous_commands:
|
120
|
+
|
121
|
+
# Security check - use same filtering as dashboard
|
122
|
+
from django_cfg.modules.django_unfold.callbacks.base import is_command_allowed
|
123
|
+
|
124
|
+
app_name = available_commands.get(command_name)
|
125
|
+
if not is_command_allowed(command_name, app_name):
|
128
126
|
return JsonResponse({
|
129
|
-
"status": "error",
|
130
|
-
"error": f"Command '{command_name}' is not allowed via
|
131
|
-
"suggestion": "
|
127
|
+
"status": "error",
|
128
|
+
"error": f"Command '{command_name}' is not allowed via web interface for security reasons",
|
129
|
+
"suggestion": "Only safe django_cfg commands and whitelisted utilities can be executed via web.",
|
132
130
|
}, status=403)
|
133
131
|
|
134
132
|
# Create streaming response generator
|
135
133
|
def stream_command_execution():
|
136
134
|
"""Generator that yields command output in SSE format."""
|
137
135
|
start_time = time.time()
|
138
|
-
|
136
|
+
|
139
137
|
# Send start event
|
140
138
|
yield f"data: {json.dumps({'type': 'start', 'command': command_name, 'args': args})}\n\n"
|
141
|
-
|
139
|
+
|
142
140
|
try:
|
143
|
-
#
|
144
|
-
import
|
141
|
+
# Capture command output using StringIO
|
142
|
+
from io import StringIO
|
145
143
|
import sys
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
if key.startswith('--'):
|
173
|
-
cmd.append(key)
|
174
|
-
if value is not True: # Boolean flags don't need values
|
175
|
-
cmd.append(str(value))
|
176
|
-
else:
|
177
|
-
cmd.append(f"--{key}")
|
178
|
-
if value is not True:
|
179
|
-
cmd.append(str(value))
|
180
|
-
|
181
|
-
# Start process
|
182
|
-
process = subprocess.Popen(
|
183
|
-
cmd,
|
184
|
-
stdout=subprocess.PIPE,
|
185
|
-
stderr=subprocess.STDOUT,
|
186
|
-
universal_newlines=True,
|
187
|
-
bufsize=1,
|
188
|
-
cwd=project_root
|
189
|
-
)
|
190
|
-
|
144
|
+
|
145
|
+
# Create output buffer
|
146
|
+
output_buffer = StringIO()
|
147
|
+
|
148
|
+
# Redirect stdout/stderr to buffer
|
149
|
+
old_stdout = sys.stdout
|
150
|
+
old_stderr = sys.stderr
|
151
|
+
sys.stdout = output_buffer
|
152
|
+
sys.stderr = output_buffer
|
153
|
+
|
154
|
+
try:
|
155
|
+
# Execute Django command directly using call_command
|
156
|
+
call_command(command_name, *args, **options)
|
157
|
+
return_code = 0
|
158
|
+
except Exception as e:
|
159
|
+
# Command execution failed
|
160
|
+
output_buffer.write(f"\nError: {str(e)}\n")
|
161
|
+
return_code = 1
|
162
|
+
finally:
|
163
|
+
# Restore stdout/stderr
|
164
|
+
sys.stdout = old_stdout
|
165
|
+
sys.stderr = old_stderr
|
166
|
+
|
167
|
+
# Get all output
|
168
|
+
output = output_buffer.getvalue()
|
169
|
+
|
191
170
|
# Stream output line by line
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
# Wait for process to complete
|
198
|
-
return_code = process.wait()
|
171
|
+
if output:
|
172
|
+
for line in output.split('\n'):
|
173
|
+
if line.strip():
|
174
|
+
yield f"data: {json.dumps({'type': 'output', 'line': line})}\n\n"
|
175
|
+
|
199
176
|
execution_time = time.time() - start_time
|
200
|
-
|
177
|
+
|
201
178
|
# Send completion event
|
202
179
|
yield f"data: {json.dumps({'type': 'complete', 'return_code': return_code, 'execution_time': round(execution_time, 2)})}\n\n"
|
203
|
-
|
180
|
+
|
204
181
|
# Log command execution
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
182
|
+
if return_code == 0:
|
183
|
+
logger.info(
|
184
|
+
f"Command executed: {command_name} {' '.join(args)} "
|
185
|
+
f"by user {request.user.username} in {execution_time:.2f}s"
|
186
|
+
)
|
187
|
+
else:
|
188
|
+
logger.error(
|
189
|
+
f"Command failed: {command_name} {' '.join(args)} "
|
190
|
+
f"by user {request.user.username} in {execution_time:.2f}s"
|
191
|
+
)
|
192
|
+
|
210
193
|
except Exception as cmd_error:
|
211
194
|
execution_time = time.time() - start_time
|
212
|
-
|
195
|
+
|
213
196
|
logger.error(
|
214
197
|
f"Command failed: {command_name} {' '.join(args)} "
|
215
198
|
f"by user {request.user.username}: {cmd_error}"
|
216
199
|
)
|
217
|
-
|
200
|
+
|
218
201
|
# Send error event
|
219
|
-
yield f"data: {json.dumps({'type': 'error', '
|
202
|
+
yield f"data: {json.dumps({'type': 'error', 'message': str(cmd_error), 'execution_time': round(execution_time, 2)})}\n\n"
|
220
203
|
|
221
204
|
# Return streaming response
|
222
205
|
response = StreamingHttpResponse(
|
@@ -243,7 +226,7 @@ def execute_command_view(request):
|
|
243
226
|
|
244
227
|
|
245
228
|
@require_http_methods(["GET"])
|
246
|
-
@user_passes_test(
|
229
|
+
@user_passes_test(is_staff_user)
|
247
230
|
def command_help_view(request, command_name):
|
248
231
|
"""
|
249
232
|
Get help information for a specific command.
|
@@ -0,0 +1,269 @@
|
|
1
|
+
"""
|
2
|
+
Django CFG Health Check DRF Views
|
3
|
+
|
4
|
+
DRF browsable API views with Tailwind theme support.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import time
|
9
|
+
import psutil
|
10
|
+
from datetime import datetime
|
11
|
+
from typing import Dict, Any
|
12
|
+
|
13
|
+
from django.conf import settings
|
14
|
+
from django.db import connections
|
15
|
+
from django.core.cache import cache
|
16
|
+
from django.utils import timezone
|
17
|
+
|
18
|
+
from rest_framework.views import APIView
|
19
|
+
from rest_framework.response import Response
|
20
|
+
from rest_framework import status
|
21
|
+
from rest_framework.permissions import AllowAny
|
22
|
+
|
23
|
+
from django_cfg.core.integration import get_current_version
|
24
|
+
from .serializers import HealthCheckSerializer, QuickHealthSerializer
|
25
|
+
|
26
|
+
|
27
|
+
class DRFHealthCheckView(APIView):
|
28
|
+
"""
|
29
|
+
Django CFG comprehensive health check endpoint with DRF Browsable API.
|
30
|
+
|
31
|
+
Auto-discovers configuration from DjangoConfig and checks:
|
32
|
+
- All configured databases
|
33
|
+
- Cache connectivity
|
34
|
+
- System resources
|
35
|
+
- Configuration status
|
36
|
+
|
37
|
+
This endpoint uses DRF Browsable API with Tailwind CSS theme! 🎨
|
38
|
+
"""
|
39
|
+
|
40
|
+
permission_classes = [AllowAny] # Public endpoint
|
41
|
+
serializer_class = HealthCheckSerializer # For schema generation
|
42
|
+
|
43
|
+
def get(self, request):
|
44
|
+
"""Return comprehensive health check data."""
|
45
|
+
# Get DjangoConfig instance
|
46
|
+
try:
|
47
|
+
config = getattr(settings, 'config', None)
|
48
|
+
|
49
|
+
health_data = {
|
50
|
+
"status": "healthy",
|
51
|
+
"timestamp": timezone.now(),
|
52
|
+
"service": config.project_name if config else "Django CFG",
|
53
|
+
"version": get_current_version(),
|
54
|
+
"checks": {}
|
55
|
+
}
|
56
|
+
except Exception:
|
57
|
+
health_data = {
|
58
|
+
"status": "healthy",
|
59
|
+
"timestamp": timezone.now(),
|
60
|
+
"service": "Django CFG",
|
61
|
+
"version": get_current_version(),
|
62
|
+
"checks": {}
|
63
|
+
}
|
64
|
+
|
65
|
+
# Database connectivity check
|
66
|
+
try:
|
67
|
+
db_status = self._check_databases()
|
68
|
+
health_data["checks"]["databases"] = db_status
|
69
|
+
except Exception as e:
|
70
|
+
health_data["checks"]["databases"] = {
|
71
|
+
"status": "error",
|
72
|
+
"error": str(e)
|
73
|
+
}
|
74
|
+
health_data["status"] = "unhealthy"
|
75
|
+
|
76
|
+
# Cache availability check
|
77
|
+
try:
|
78
|
+
cache_status = self._check_cache()
|
79
|
+
health_data["checks"]["cache"] = cache_status
|
80
|
+
except Exception as e:
|
81
|
+
health_data["checks"]["cache"] = {
|
82
|
+
"status": "error",
|
83
|
+
"error": str(e)
|
84
|
+
}
|
85
|
+
if health_data["status"] != "unhealthy":
|
86
|
+
health_data["status"] = "degraded"
|
87
|
+
|
88
|
+
# System resources check
|
89
|
+
try:
|
90
|
+
system_status = self._check_system_resources()
|
91
|
+
health_data["checks"]["system"] = system_status
|
92
|
+
except Exception as e:
|
93
|
+
health_data["checks"]["system"] = {
|
94
|
+
"status": "error",
|
95
|
+
"error": str(e)
|
96
|
+
}
|
97
|
+
|
98
|
+
# Environment info
|
99
|
+
health_data["environment"] = {
|
100
|
+
"debug": getattr(settings, 'DEBUG', False),
|
101
|
+
"django_env": os.getenv("DJANGO_ENV", "development"),
|
102
|
+
"python_version": f"{os.sys.version_info.major}.{os.sys.version_info.minor}.{os.sys.version_info.micro}",
|
103
|
+
}
|
104
|
+
|
105
|
+
# Return appropriate HTTP status
|
106
|
+
http_status = status.HTTP_200_OK
|
107
|
+
if health_data["status"] == "unhealthy":
|
108
|
+
http_status = status.HTTP_503_SERVICE_UNAVAILABLE
|
109
|
+
elif health_data["status"] == "degraded":
|
110
|
+
http_status = status.HTTP_200_OK # Still operational
|
111
|
+
|
112
|
+
return Response(health_data, status=http_status)
|
113
|
+
|
114
|
+
def _check_databases(self) -> Dict[str, Any]:
|
115
|
+
"""Check all configured database connections."""
|
116
|
+
db_results = {}
|
117
|
+
|
118
|
+
# Get all configured databases
|
119
|
+
database_names = list(getattr(settings, 'DATABASES', {}).keys())
|
120
|
+
|
121
|
+
for db_name in database_names:
|
122
|
+
try:
|
123
|
+
start_time = time.time()
|
124
|
+
conn = connections[db_name]
|
125
|
+
|
126
|
+
with conn.cursor() as cursor:
|
127
|
+
cursor.execute("SELECT 1")
|
128
|
+
cursor.fetchone()
|
129
|
+
|
130
|
+
response_time = (time.time() - start_time) * 1000 # ms
|
131
|
+
|
132
|
+
db_results[db_name] = {
|
133
|
+
"status": "healthy",
|
134
|
+
"response_time_ms": round(response_time, 2),
|
135
|
+
"engine": conn.settings_dict.get("ENGINE", "unknown"),
|
136
|
+
}
|
137
|
+
|
138
|
+
except Exception as e:
|
139
|
+
db_results[db_name] = {
|
140
|
+
"status": "error",
|
141
|
+
"error": str(e)[:100], # Truncate long errors
|
142
|
+
}
|
143
|
+
|
144
|
+
# Overall database status
|
145
|
+
all_healthy = all(
|
146
|
+
db["status"] == "healthy"
|
147
|
+
for db in db_results.values()
|
148
|
+
) if db_results else False
|
149
|
+
|
150
|
+
return {
|
151
|
+
"status": "healthy" if all_healthy else "error",
|
152
|
+
"databases": db_results,
|
153
|
+
"total_databases": len(db_results),
|
154
|
+
}
|
155
|
+
|
156
|
+
def _check_cache(self) -> Dict[str, Any]:
|
157
|
+
"""Check cache connectivity and performance."""
|
158
|
+
try:
|
159
|
+
test_key = "django_cfg_health_check"
|
160
|
+
test_value = f"test_{int(time.time())}"
|
161
|
+
|
162
|
+
# Test cache write
|
163
|
+
start_time = time.time()
|
164
|
+
cache.set(test_key, test_value, timeout=60)
|
165
|
+
write_time = (time.time() - start_time) * 1000
|
166
|
+
|
167
|
+
# Test cache read
|
168
|
+
start_time = time.time()
|
169
|
+
cached_value = cache.get(test_key)
|
170
|
+
read_time = (time.time() - start_time) * 1000
|
171
|
+
|
172
|
+
# Cleanup
|
173
|
+
cache.delete(test_key)
|
174
|
+
|
175
|
+
if cached_value == test_value:
|
176
|
+
return {
|
177
|
+
"status": "healthy",
|
178
|
+
"write_time_ms": round(write_time, 2),
|
179
|
+
"read_time_ms": round(read_time, 2),
|
180
|
+
"backend": getattr(settings, "CACHES", {}).get("default", {}).get("BACKEND", "unknown"),
|
181
|
+
}
|
182
|
+
else:
|
183
|
+
return {
|
184
|
+
"status": "error",
|
185
|
+
"error": "Cache read/write mismatch"
|
186
|
+
}
|
187
|
+
|
188
|
+
except Exception as e:
|
189
|
+
return {
|
190
|
+
"status": "error",
|
191
|
+
"error": str(e)
|
192
|
+
}
|
193
|
+
|
194
|
+
def _check_system_resources(self) -> Dict[str, Any]:
|
195
|
+
"""Check system resource usage."""
|
196
|
+
try:
|
197
|
+
# CPU usage
|
198
|
+
cpu_percent = psutil.cpu_percent(interval=1)
|
199
|
+
|
200
|
+
# Memory usage
|
201
|
+
memory = psutil.virtual_memory()
|
202
|
+
|
203
|
+
# Disk usage
|
204
|
+
disk = psutil.disk_usage('/')
|
205
|
+
|
206
|
+
# System uptime
|
207
|
+
boot_time = datetime.fromtimestamp(psutil.boot_time())
|
208
|
+
uptime = datetime.now() - boot_time
|
209
|
+
|
210
|
+
return {
|
211
|
+
"status": "healthy",
|
212
|
+
"cpu": {
|
213
|
+
"percent": cpu_percent,
|
214
|
+
"count": psutil.cpu_count(),
|
215
|
+
},
|
216
|
+
"memory": {
|
217
|
+
"percent": memory.percent,
|
218
|
+
"used_gb": round(memory.used / (1024**3), 2),
|
219
|
+
"total_gb": round(memory.total / (1024**3), 2),
|
220
|
+
},
|
221
|
+
"disk": {
|
222
|
+
"percent": round((disk.used / disk.total) * 100, 2),
|
223
|
+
"used_gb": round(disk.used / (1024**3), 2),
|
224
|
+
"total_gb": round(disk.total / (1024**3), 2),
|
225
|
+
},
|
226
|
+
"uptime": {
|
227
|
+
"days": uptime.days,
|
228
|
+
"hours": uptime.seconds // 3600,
|
229
|
+
"boot_time": boot_time.isoformat(),
|
230
|
+
},
|
231
|
+
"process_count": len(psutil.pids()),
|
232
|
+
}
|
233
|
+
|
234
|
+
except Exception as e:
|
235
|
+
return {
|
236
|
+
"status": "error",
|
237
|
+
"error": str(e)
|
238
|
+
}
|
239
|
+
|
240
|
+
|
241
|
+
class DRFQuickHealthView(APIView):
|
242
|
+
"""
|
243
|
+
Quick health check for load balancers with DRF Browsable API.
|
244
|
+
|
245
|
+
This endpoint uses DRF Browsable API with Tailwind CSS theme! 🎨
|
246
|
+
"""
|
247
|
+
|
248
|
+
permission_classes = [AllowAny] # Public endpoint
|
249
|
+
serializer_class = QuickHealthSerializer # For schema generation
|
250
|
+
|
251
|
+
def get(self, request):
|
252
|
+
"""Return minimal health status."""
|
253
|
+
try:
|
254
|
+
# Just check main database
|
255
|
+
conn = connections["default"]
|
256
|
+
with conn.cursor() as cursor:
|
257
|
+
cursor.execute("SELECT 1")
|
258
|
+
|
259
|
+
return Response({
|
260
|
+
"status": "ok",
|
261
|
+
"timestamp": timezone.now(),
|
262
|
+
})
|
263
|
+
|
264
|
+
except Exception as e:
|
265
|
+
return Response({
|
266
|
+
"status": "error",
|
267
|
+
"error": str(e),
|
268
|
+
"timestamp": timezone.now(),
|
269
|
+
}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
|