django-spire 0.23.7__py3-none-any.whl → 0.23.8__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_spire/ai/admin.py +11 -11
- django_spire/ai/chat/apps.py +1 -0
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/dialog_widget.html +1 -1
- django_spire/ai/chat/tests/factories.py +15 -0
- django_spire/ai/chat/tests/test_controller.py +45 -0
- django_spire/ai/chat/tests/test_models.py +301 -0
- django_spire/ai/chat/tests/test_prompts.py +48 -0
- django_spire/ai/chat/tests/test_responses.py +208 -0
- django_spire/ai/chat/tests/test_router/test_base_chat_router.py +66 -6
- django_spire/ai/chat/tests/test_router/test_chat_workflow.py +73 -3
- django_spire/ai/chat/tests/test_router/test_integration.py +86 -6
- django_spire/ai/chat/tests/test_router/test_intent_decoder.py +93 -1
- django_spire/ai/chat/tests/test_router/test_message_intel.py +60 -1
- django_spire/ai/chat/tests/test_router/test_spire_chat_router.py +110 -0
- django_spire/ai/chat/tests/test_urls/test_json_urls.py +202 -1
- django_spire/ai/context/tests/__init__.py +0 -0
- django_spire/ai/context/tests/test_context.py +188 -0
- django_spire/ai/decorators.py +7 -6
- django_spire/ai/prompt/tests/test_bots.py +100 -10
- django_spire/ai/prompt/tests/test_prompt_intel.py +83 -0
- django_spire/ai/prompt/tests/test_prompt_tuning.py +126 -0
- django_spire/ai/sms/decorators.py +8 -2
- django_spire/ai/sms/tests/test_sms.py +240 -16
- django_spire/ai/sms/tests/test_sms_intel.py +42 -0
- django_spire/ai/sms/tests/test_webhook.py +155 -7
- django_spire/ai/sms/views.py +23 -24
- django_spire/ai/tests/test_ai.py +131 -7
- django_spire/auth/apps.py +4 -2
- django_spire/auth/controller/controller.py +36 -23
- django_spire/auth/controller/exceptions.py +9 -0
- django_spire/auth/group/admin.py +1 -0
- django_spire/auth/group/apps.py +2 -0
- django_spire/auth/group/factories.py +17 -8
- django_spire/auth/group/forms.py +7 -0
- django_spire/auth/group/tests/test_factories.py +146 -0
- django_spire/auth/group/tests/test_forms.py +282 -0
- django_spire/auth/group/tests/test_models.py +192 -0
- django_spire/auth/group/tests/test_querysets.py +98 -0
- django_spire/auth/group/tests/test_utils.py +341 -0
- django_spire/auth/group/tests/test_views.py +377 -0
- django_spire/auth/group/urls/__init__.py +3 -1
- django_spire/auth/group/urls/form_urls.py +2 -0
- django_spire/auth/group/urls/json_urls.py +3 -0
- django_spire/auth/group/urls/page_urls.py +2 -0
- django_spire/auth/group/utils.py +6 -2
- django_spire/auth/group/views/form_views.py +6 -3
- django_spire/auth/group/views/json_views.py +6 -2
- django_spire/auth/mfa/admin.py +2 -0
- django_spire/auth/mfa/apps.py +2 -0
- django_spire/auth/mfa/forms.py +1 -0
- django_spire/auth/mfa/querysets.py +9 -2
- django_spire/auth/mfa/tests/test_models.py +233 -0
- django_spire/auth/mfa/tests/test_utils.py +106 -0
- django_spire/auth/mfa/urls/__init__.py +2 -0
- django_spire/auth/mfa/urls/page_urls.py +2 -0
- django_spire/auth/mfa/urls/redirect_urls.py +2 -0
- django_spire/auth/mfa/views/page_views.py +2 -1
- django_spire/auth/permissions/consts.py +2 -2
- django_spire/auth/permissions/decorators.py +8 -8
- django_spire/auth/permissions/permissions.py +28 -35
- django_spire/auth/permissions/tests/test_decorators.py +333 -0
- django_spire/auth/permissions/tests/test_permissions.py +337 -0
- django_spire/auth/permissions/tests/test_tools.py +305 -0
- django_spire/auth/permissions/tools.py +21 -15
- django_spire/auth/seeding/seed.py +3 -0
- django_spire/auth/seeding/seeder.py +2 -0
- django_spire/auth/tests/test_controller.py +323 -0
- django_spire/auth/tests/test_url_endpoints.py +9 -9
- django_spire/auth/tests/test_views.py +406 -0
- django_spire/auth/urls/admin_urls.py +2 -0
- django_spire/auth/urls/redirect_urls.py +2 -0
- django_spire/auth/user/apps.py +2 -0
- django_spire/auth/user/forms.py +9 -0
- django_spire/auth/user/models.py +1 -1
- django_spire/auth/user/services/services.py +1 -0
- django_spire/auth/user/tests/factories.py +14 -13
- django_spire/auth/user/tests/test_factories.py +166 -2
- django_spire/auth/user/tests/test_forms.py +573 -0
- django_spire/auth/user/tests/test_models.py +257 -0
- django_spire/auth/user/tests/test_services.py +200 -0
- django_spire/auth/user/tests/test_tools.py +153 -0
- django_spire/auth/user/tests/test_user_factories.py +139 -0
- django_spire/auth/user/tests/test_views.py +363 -0
- django_spire/auth/user/tools.py +7 -1
- django_spire/auth/user/urls/form_urls.py +3 -0
- django_spire/auth/user/urls/page_urls.py +3 -0
- django_spire/auth/user/views/form_views.py +19 -10
- django_spire/auth/user/views/page_views.py +8 -2
- django_spire/auth/views/redirect_views.py +14 -9
- django_spire/comment/admin.py +2 -0
- django_spire/comment/apps.py +2 -0
- django_spire/comment/templatetags/comment_tags.py +1 -0
- django_spire/comment/tests/test_forms.py +27 -0
- django_spire/comment/tests/test_models.py +215 -0
- django_spire/comment/tests/test_querysets.py +101 -0
- django_spire/comment/tests/test_utils.py +90 -0
- django_spire/comment/urls.py +2 -0
- django_spire/comment/utils.py +22 -13
- django_spire/comment/views.py +1 -1
- django_spire/conf.py +8 -6
- django_spire/consts.py +1 -1
- django_spire/contrib/breadcrumb/apps.py +2 -0
- django_spire/contrib/breadcrumb/breadcrumbs.py +18 -18
- django_spire/contrib/breadcrumb/tests/test_breadcrumbs.py +198 -0
- django_spire/contrib/constructor/__init__.py +3 -3
- django_spire/contrib/constructor/constructor.py +15 -15
- django_spire/contrib/constructor/django_model_constructor.py +5 -4
- django_spire/contrib/constructor/exceptions.py +5 -3
- django_spire/contrib/constructor/tests/__init__.py +0 -0
- django_spire/contrib/constructor/tests/test_constructor.py +193 -0
- django_spire/contrib/form/tests/__init__.py +0 -0
- django_spire/contrib/form/tests/test_forms.py +203 -0
- django_spire/contrib/generic_views/modal_views.py +2 -1
- django_spire/contrib/generic_views/portal_views.py +20 -19
- django_spire/contrib/generic_views/tests/__init__.py +0 -0
- django_spire/contrib/generic_views/tests/test_views.py +459 -0
- django_spire/contrib/help/apps.py +2 -0
- django_spire/contrib/help/templatetags/help.py +1 -0
- django_spire/contrib/help/tests/__init__.py +0 -0
- django_spire/contrib/help/tests/test_templatetags.py +100 -0
- django_spire/contrib/options/mixins.py +6 -5
- django_spire/contrib/options/tests/factories.py +5 -1
- django_spire/contrib/options/tests/test_options.py +234 -0
- django_spire/contrib/ordering/exceptions.py +7 -3
- django_spire/contrib/ordering/mixins.py +2 -0
- django_spire/contrib/ordering/querysets.py +3 -1
- django_spire/contrib/ordering/services/processor_service.py +8 -4
- django_spire/contrib/ordering/services/service.py +1 -2
- django_spire/contrib/ordering/tests/__init__.py +0 -0
- django_spire/contrib/ordering/tests/test_ordering.py +165 -0
- django_spire/contrib/ordering/validators.py +6 -6
- django_spire/contrib/pagination/templatetags/pagination_tags.py +12 -5
- django_spire/contrib/pagination/tests/__init__.py +0 -0
- django_spire/contrib/pagination/tests/test_pagination.py +179 -0
- django_spire/contrib/performance/decorators.py +16 -6
- django_spire/contrib/performance/tests/__init__.py +0 -0
- django_spire/contrib/performance/tests/test_performance.py +107 -0
- django_spire/contrib/queryset/enums.py +3 -1
- django_spire/contrib/queryset/filter_tools.py +10 -5
- django_spire/contrib/queryset/mixins.py +16 -16
- django_spire/contrib/queryset/tests/__init__.py +0 -0
- django_spire/contrib/queryset/tests/test_queryset.py +137 -0
- django_spire/contrib/seeding/field/base.py +13 -7
- django_spire/contrib/seeding/field/callable.py +8 -1
- django_spire/contrib/seeding/field/cleaners.py +5 -5
- django_spire/contrib/seeding/field/custom.py +20 -10
- django_spire/contrib/seeding/field/django/seeder.py +8 -6
- django_spire/contrib/seeding/field/enums.py +7 -5
- django_spire/contrib/seeding/field/override.py +16 -6
- django_spire/contrib/seeding/field/static.py +9 -2
- django_spire/contrib/seeding/field/tests/test_base.py +18 -14
- django_spire/contrib/seeding/field/tests/test_callable.py +13 -9
- django_spire/contrib/seeding/field/tests/test_cleaners.py +51 -38
- django_spire/contrib/seeding/field/tests/test_static.py +13 -9
- django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +2 -0
- django_spire/contrib/seeding/intelligence/intel.py +5 -1
- django_spire/contrib/seeding/intelligence/prompts/factory.py +6 -1
- django_spire/contrib/seeding/intelligence/prompts/foreign_key_selection_prompt.py +6 -1
- django_spire/contrib/seeding/intelligence/prompts/generate_django_model_seeder_prompts.py +2 -0
- django_spire/contrib/seeding/intelligence/prompts/generic_relationship_selection_prompt.py +7 -1
- django_spire/contrib/seeding/intelligence/prompts/hierarchical_selection_prompt.py +6 -2
- django_spire/contrib/seeding/intelligence/prompts/model_field_choices_prompt.py +8 -2
- django_spire/contrib/seeding/intelligence/prompts/negation_prompt.py +2 -0
- django_spire/contrib/seeding/intelligence/prompts/objective_prompt.py +6 -1
- django_spire/contrib/seeding/management/commands/seeding.py +9 -3
- django_spire/contrib/seeding/management/example.py +2 -0
- django_spire/contrib/seeding/model/base.py +16 -7
- django_spire/contrib/seeding/model/config.py +31 -15
- django_spire/contrib/seeding/model/django/config.py +13 -13
- django_spire/contrib/seeding/model/django/seeder.py +4 -4
- django_spire/contrib/seeding/model/django/tests/test_seeder.py +34 -23
- django_spire/contrib/seeding/model/enums.py +2 -0
- django_spire/contrib/seeding/tests/test_config.py +71 -0
- django_spire/contrib/seeding/tests/test_custom.py +35 -0
- django_spire/contrib/seeding/tests/test_enums.py +40 -0
- django_spire/contrib/seeding/tests/test_intel.py +32 -0
- django_spire/contrib/seeding/tests/test_override.py +63 -0
- django_spire/contrib/service/__init__.py +2 -2
- django_spire/contrib/service/django_model_service.py +16 -15
- django_spire/contrib/service/exceptions.py +5 -3
- django_spire/contrib/service/tests/__init__.py +0 -0
- django_spire/contrib/service/tests/test_service.py +153 -0
- django_spire/contrib/session/apps.py +2 -0
- django_spire/contrib/session/controller.py +48 -42
- django_spire/contrib/session/templatetags/session_tags.py +11 -2
- django_spire/contrib/session/tests/test_session_controller.py +117 -53
- django_spire/contrib/tests/__init__.py +0 -0
- django_spire/contrib/tests/test_utils.py +37 -0
- django_spire/contrib/utils.py +4 -1
- django_spire/core/apps.py +2 -0
- django_spire/core/converters/tests/test_to_data.py +353 -0
- django_spire/core/converters/tests/test_to_enums.py +61 -41
- django_spire/core/converters/tests/test_to_pydantic.py +138 -109
- django_spire/core/converters/to_data.py +29 -10
- django_spire/core/converters/to_enums.py +4 -2
- django_spire/core/converters/to_pydantic.py +22 -22
- django_spire/core/decorators.py +19 -6
- django_spire/core/forms/widgets.py +4 -0
- django_spire/core/maps.py +3 -1
- django_spire/core/middleware/maintenance.py +3 -3
- django_spire/core/middleware.py +8 -6
- django_spire/core/redirect/__init__.py +5 -0
- django_spire/core/redirect/generic_redirect.py +1 -2
- django_spire/core/redirect/tests/__init__.py +0 -0
- django_spire/core/redirect/tests/test_generic_redirect.py +34 -0
- django_spire/core/{tests/tests_redirect.py → redirect/tests/test_safe_redirect.py} +55 -81
- django_spire/core/shortcuts.py +3 -3
- django_spire/core/static/django_spire/css/app-layout.css +1 -1
- django_spire/core/static/django_spire/css/app-navigation.css +3 -3
- django_spire/core/static/django_spire/css/bootstrap-override.css +4 -0
- django_spire/core/tag/admin.py +12 -0
- django_spire/core/tag/intelligence/tag_set_bot.py +2 -0
- django_spire/core/tag/mixins.py +2 -0
- django_spire/core/tag/models.py +2 -0
- django_spire/core/tag/querysets.py +2 -0
- django_spire/core/tag/service/tag_service.py +6 -3
- django_spire/core/tag/tests/test_intelligence.py +9 -9
- django_spire/core/tag/tests/test_tags.py +44 -54
- django_spire/core/tag/tests/test_tools.py +191 -0
- django_spire/core/tag/tools.py +3 -0
- django_spire/core/templates/django_spire/card/card.html +5 -2
- django_spire/core/templates/django_spire/card/title_card.html +9 -4
- django_spire/core/templates/django_spire/infinite_scroll/base.html +1 -0
- django_spire/core/templates/django_spire/navigation/side_navigation.html +19 -24
- django_spire/core/templates/django_spire/page/full_page.html +46 -16
- django_spire/core/templates/django_spire/table/base.html +4 -2
- django_spire/core/templatetags/json.py +6 -2
- django_spire/core/templatetags/message.py +13 -8
- django_spire/core/templatetags/string_formating.py +8 -5
- django_spire/core/templatetags/tests/__init__.py +0 -0
- django_spire/core/templatetags/tests/test_templatetags.py +427 -0
- django_spire/core/templatetags/variable_types.py +17 -9
- django_spire/core/tests/test_cases.py +1 -1
- django_spire/core/tests/test_conf.py +43 -0
- django_spire/core/tests/test_consts.py +28 -0
- django_spire/core/tests/test_context_processors.py +93 -0
- django_spire/core/tests/test_decorators.py +95 -0
- django_spire/core/tests/test_django_spire_utils.py +56 -0
- django_spire/core/tests/test_exceptions.py +37 -0
- django_spire/core/tests/test_models.py +54 -0
- django_spire/core/tests/test_settings.py +45 -0
- django_spire/core/tests/test_shortcuts.py +74 -0
- django_spire/core/tests/test_urls.py +16 -0
- django_spire/core/tests/test_utils.py +58 -0
- django_spire/core/urls.py +4 -1
- django_spire/core/utils.py +12 -8
- django_spire/exceptions.py +16 -1
- django_spire/file/admin.py +4 -2
- django_spire/file/apps.py +8 -10
- django_spire/file/fields.py +7 -7
- django_spire/file/forms.py +1 -1
- django_spire/file/interfaces.py +15 -15
- django_spire/file/mixins.py +1 -4
- django_spire/file/models.py +3 -5
- django_spire/file/tests/factories.py +59 -0
- django_spire/file/tests/test_admin.py +69 -0
- django_spire/file/tests/test_apps.py +24 -0
- django_spire/file/tests/test_fields.py +114 -0
- django_spire/file/tests/test_forms.py +20 -0
- django_spire/file/tests/test_interfaces.py +183 -0
- django_spire/file/tests/test_models.py +82 -0
- django_spire/file/tests/test_querysets.py +102 -0
- django_spire/file/tests/test_utils.py +32 -0
- django_spire/file/tests/test_views.py +145 -0
- django_spire/file/tests/test_widgets.py +82 -0
- django_spire/file/tools.py +8 -2
- django_spire/file/views.py +7 -3
- django_spire/file/widgets.py +12 -12
- django_spire/help_desk/admin.py +15 -0
- django_spire/help_desk/apps.py +2 -0
- django_spire/help_desk/auth/controller.py +2 -0
- django_spire/help_desk/choices.py +2 -0
- django_spire/help_desk/enums.py +2 -0
- django_spire/help_desk/exceptions.py +31 -3
- django_spire/help_desk/forms.py +2 -0
- django_spire/help_desk/models.py +2 -0
- django_spire/help_desk/querysets.py +4 -1
- django_spire/help_desk/services/notification_service.py +26 -27
- django_spire/help_desk/services/service.py +2 -3
- django_spire/help_desk/tests/factories.py +8 -3
- django_spire/help_desk/tests/test_admin.py +41 -0
- django_spire/help_desk/tests/test_apps.py +41 -0
- django_spire/help_desk/tests/test_choices.py +50 -0
- django_spire/help_desk/tests/test_controller.py +87 -0
- django_spire/help_desk/tests/test_enums.py +18 -0
- django_spire/help_desk/tests/test_exceptions.py +37 -0
- django_spire/help_desk/tests/test_forms.py +89 -0
- django_spire/help_desk/tests/test_models.py +59 -0
- django_spire/help_desk/tests/test_querysets.py +38 -0
- django_spire/help_desk/tests/test_services/test_notification_service.py +15 -8
- django_spire/help_desk/tests/test_services/test_service.py +92 -0
- django_spire/help_desk/tests/test_urls/test_form_urls.py +6 -6
- django_spire/help_desk/tests/test_urls/test_page_urls.py +8 -9
- django_spire/help_desk/tests/test_views/test_form_views.py +46 -19
- django_spire/help_desk/tests/test_views/test_page_views.py +32 -9
- django_spire/help_desk/urls/__init__.py +4 -1
- django_spire/help_desk/urls/form_urls.py +3 -0
- django_spire/help_desk/urls/page_urls.py +3 -0
- django_spire/help_desk/views/form_views.py +13 -5
- django_spire/help_desk/views/page_views.py +11 -3
- django_spire/history/activity/admin.py +2 -0
- django_spire/history/activity/apps.py +3 -1
- django_spire/history/activity/mixins.py +13 -7
- django_spire/history/activity/models.py +6 -5
- django_spire/history/activity/querysets.py +2 -0
- django_spire/history/activity/tests/__init__.py +0 -0
- django_spire/history/activity/tests/test_activity.py +176 -0
- django_spire/history/admin.py +9 -2
- django_spire/history/choices.py +3 -0
- django_spire/history/models.py +5 -5
- django_spire/history/tests/test_admin.py +93 -0
- django_spire/history/tests/test_history.py +101 -0
- django_spire/history/tests/test_mixins.py +84 -0
- django_spire/history/viewed/admin.py +3 -1
- django_spire/history/viewed/apps.py +3 -1
- django_spire/history/viewed/models.py +2 -0
- django_spire/history/viewed/tests/__init__.py +0 -0
- django_spire/history/viewed/tests/test_viewed.py +46 -0
- django_spire/knowledge/auth/tests/__init__.py +0 -0
- django_spire/knowledge/auth/tests/test_controller.py +116 -0
- django_spire/knowledge/collection/admin.py +5 -1
- django_spire/knowledge/collection/models.py +3 -1
- django_spire/knowledge/collection/seeding/seed.py +1 -0
- django_spire/knowledge/collection/services/factory_service.py +10 -11
- django_spire/knowledge/collection/services/ordering_service.py +1 -2
- django_spire/knowledge/collection/services/service.py +5 -10
- django_spire/knowledge/collection/services/tag_service.py +5 -2
- django_spire/knowledge/collection/tests/factories.py +28 -1
- django_spire/knowledge/collection/tests/test_models.py +48 -0
- django_spire/knowledge/collection/tests/test_querysets.py +93 -0
- django_spire/knowledge/collection/tests/test_services/test_factory_service.py +100 -0
- django_spire/knowledge/collection/tests/test_services/test_services.py +160 -0
- django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +21 -3
- django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +39 -1
- django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +12 -4
- django_spire/knowledge/collection/urls/__init__.py +3 -0
- django_spire/knowledge/collection/urls/form_urls.py +2 -0
- django_spire/knowledge/collection/urls/json_urls.py +2 -0
- django_spire/knowledge/collection/urls/page_urls.py +2 -0
- django_spire/knowledge/collection/views/form_views.py +4 -4
- django_spire/knowledge/collection/views/json_views.py +5 -1
- django_spire/knowledge/collection/views/page_views.py +5 -2
- django_spire/knowledge/entry/admin.py +7 -1
- django_spire/knowledge/entry/forms.py +2 -0
- django_spire/knowledge/entry/models.py +2 -0
- django_spire/knowledge/entry/seeding/seed.py +3 -0
- django_spire/knowledge/entry/services/automation_service.py +5 -4
- django_spire/knowledge/entry/services/factory_service.py +7 -5
- django_spire/knowledge/entry/services/service.py +4 -7
- django_spire/knowledge/entry/services/tag_service.py +0 -1
- django_spire/knowledge/entry/services/tool_service.py +1 -0
- django_spire/knowledge/entry/services/transformation_services.py +1 -5
- django_spire/knowledge/entry/tests/factories.py +1 -2
- django_spire/knowledge/entry/tests/test_factory_service.py +20 -0
- django_spire/knowledge/entry/tests/test_models.py +41 -0
- django_spire/knowledge/entry/tests/test_querysets.py +71 -0
- django_spire/knowledge/entry/tests/test_services.py +94 -0
- django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +9 -14
- django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +48 -5
- django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +6 -8
- django_spire/knowledge/entry/tests/test_urls/test_template_urls.py +40 -0
- django_spire/knowledge/entry/urls/form_urls.py +2 -0
- django_spire/knowledge/entry/urls/json_urls.py +2 -0
- django_spire/knowledge/entry/urls/page_urls.py +2 -0
- django_spire/knowledge/entry/urls/template_urls.py +2 -0
- django_spire/knowledge/entry/version/block/choices.py +2 -0
- django_spire/knowledge/entry/version/block/data/data.py +1 -0
- django_spire/knowledge/entry/version/block/data/list/data.py +8 -13
- django_spire/knowledge/entry/version/block/data/list/maps.py +3 -0
- django_spire/knowledge/entry/version/block/data/list/meta.py +1 -2
- django_spire/knowledge/entry/version/block/data/list/tests/__init__.py +0 -0
- django_spire/knowledge/entry/version/block/data/list/tests/test_maps.py +32 -0
- django_spire/knowledge/entry/version/block/data/list/tests/test_meta.py +58 -0
- django_spire/knowledge/entry/version/block/data/maps.py +3 -6
- django_spire/knowledge/entry/version/block/models.py +7 -5
- django_spire/knowledge/entry/version/block/seeding/constants.py +5 -4
- django_spire/knowledge/entry/version/block/services/service.py +2 -3
- django_spire/knowledge/entry/version/block/tests/factories.py +4 -10
- django_spire/knowledge/entry/version/block/tests/test_choices.py +56 -0
- django_spire/knowledge/entry/version/block/tests/test_data.py +90 -0
- django_spire/knowledge/entry/version/block/tests/test_maps.py +37 -0
- django_spire/knowledge/entry/version/block/tests/test_models.py +55 -0
- django_spire/knowledge/entry/version/block/tests/test_querysets.py +35 -0
- django_spire/knowledge/entry/version/block/tests/test_services.py +65 -0
- django_spire/knowledge/entry/version/choices.py +2 -0
- django_spire/knowledge/entry/version/converters/converter.py +1 -1
- django_spire/knowledge/entry/version/converters/docx_converter.py +4 -7
- django_spire/knowledge/entry/version/converters/markdown_converter.py +20 -20
- django_spire/knowledge/entry/version/maps.py +4 -5
- django_spire/knowledge/entry/version/querysets.py +1 -1
- django_spire/knowledge/entry/version/seeding/seeder.py +1 -2
- django_spire/knowledge/entry/version/services/processor_service.py +5 -4
- django_spire/knowledge/entry/version/services/service.py +1 -2
- django_spire/knowledge/entry/version/tests/factories.py +2 -2
- django_spire/knowledge/entry/version/tests/test_choices.py +18 -0
- django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +56 -8
- django_spire/knowledge/entry/version/tests/test_converters/test_markdown_converter.py +78 -0
- django_spire/knowledge/entry/version/tests/test_maps.py +58 -0
- django_spire/knowledge/entry/version/tests/test_models.py +23 -0
- django_spire/knowledge/entry/version/tests/test_querysets.py +26 -0
- django_spire/knowledge/entry/version/tests/test_services.py +62 -0
- django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +27 -8
- django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +15 -8
- django_spire/knowledge/entry/version/tests/test_urls/test_redirect_urls.py +38 -0
- django_spire/knowledge/entry/version/urls/__init__.py +3 -0
- django_spire/knowledge/entry/version/urls/json_urls.py +2 -1
- django_spire/knowledge/entry/version/urls/page_urls.py +2 -0
- django_spire/knowledge/entry/version/urls/redirect_urls.py +2 -0
- django_spire/knowledge/entry/version/views/json_views.py +5 -1
- django_spire/knowledge/entry/version/views/page_views.py +10 -3
- django_spire/knowledge/entry/version/views/redirect_views.py +5 -1
- django_spire/knowledge/entry/views/form_views.py +16 -8
- django_spire/knowledge/entry/views/json_views.py +3 -1
- django_spire/knowledge/entry/views/page_views.py +8 -2
- django_spire/knowledge/entry/views/template_views.py +7 -1
- django_spire/knowledge/exceptions.py +2 -1
- django_spire/knowledge/intelligence/intel/answer_intel.py +2 -1
- django_spire/knowledge/intelligence/intel/entry_intel.py +0 -1
- django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +4 -5
- django_spire/knowledge/models.py +1 -2
- django_spire/knowledge/tests/__init__.py +0 -0
- django_spire/knowledge/tests/test_templatetags.py +40 -0
- django_spire/knowledge/tests/test_urls/__init__.py +0 -0
- django_spire/knowledge/tests/test_urls/test_page_urls.py +24 -0
- django_spire/knowledge/urls/__init__.py +2 -0
- django_spire/knowledge/urls/page_urls.py +2 -0
- django_spire/knowledge/views/page_views.py +8 -3
- django_spire/notification/admin.py +3 -1
- django_spire/notification/app/admin.py +2 -0
- django_spire/notification/app/apps.py +3 -1
- django_spire/notification/app/exceptions.py +9 -2
- django_spire/notification/app/models.py +8 -4
- django_spire/notification/app/processor.py +22 -26
- django_spire/notification/app/querysets.py +2 -0
- django_spire/notification/app/tests/__init__.py +0 -0
- django_spire/notification/app/tests/factories.py +34 -0
- django_spire/notification/app/tests/test_apps.py +24 -0
- django_spire/notification/app/tests/test_models.py +72 -0
- django_spire/notification/app/tests/test_processor.py +111 -0
- django_spire/notification/app/tests/test_querysets.py +90 -0
- django_spire/notification/app/tests/test_views/__init__.py +0 -0
- django_spire/notification/app/tests/test_views/test_json_views.py +48 -0
- django_spire/notification/app/tests/test_views/test_page_views.py +19 -0
- django_spire/notification/app/urls/__init__.py +3 -1
- django_spire/notification/app/urls/json_urls.py +6 -4
- django_spire/notification/app/urls/page_urls.py +4 -3
- django_spire/notification/app/urls/template_urls.py +4 -2
- django_spire/notification/apps.py +4 -1
- django_spire/notification/email/admin.py +5 -1
- django_spire/notification/email/apps.py +3 -1
- django_spire/notification/email/exceptions.py +4 -2
- django_spire/notification/email/helper.py +5 -3
- django_spire/notification/email/models.py +4 -0
- django_spire/notification/email/processor.py +19 -15
- django_spire/notification/email/querysets.py +3 -0
- django_spire/notification/email/tests/__init__.py +0 -0
- django_spire/notification/email/tests/factories.py +35 -0
- django_spire/notification/email/tests/test_apps.py +24 -0
- django_spire/notification/email/tests/test_models.py +52 -0
- django_spire/notification/email/tests/test_processor.py +92 -0
- django_spire/notification/email/tests/test_querysets.py +43 -0
- django_spire/notification/exceptions.py +17 -2
- django_spire/notification/managers.py +7 -1
- django_spire/notification/maps.py +4 -1
- django_spire/notification/mixins.py +2 -0
- django_spire/notification/models.py +3 -1
- django_spire/notification/processors/notification.py +12 -5
- django_spire/notification/processors/processor.py +2 -0
- django_spire/notification/processors/tests/__init__.py +0 -0
- django_spire/notification/processors/tests/test_notification.py +106 -0
- django_spire/notification/push/admin.py +10 -1
- django_spire/notification/push/apps.py +3 -1
- django_spire/notification/push/models.py +2 -3
- django_spire/notification/push/tests/__init__.py +0 -0
- django_spire/notification/push/tests/test_apps.py +24 -0
- django_spire/notification/push/tests/test_models.py +28 -0
- django_spire/notification/querysets.py +7 -1
- django_spire/notification/sms/admin.py +2 -0
- django_spire/notification/sms/apps.py +4 -1
- django_spire/notification/sms/automations.py +2 -0
- django_spire/notification/sms/choices.py +2 -0
- django_spire/notification/sms/exceptions.py +19 -5
- django_spire/notification/sms/helper.py +33 -23
- django_spire/notification/sms/models.py +5 -1
- django_spire/notification/sms/processor.py +20 -20
- django_spire/notification/sms/querysets.py +2 -0
- django_spire/notification/sms/tests/factories.py +33 -0
- django_spire/notification/sms/tests/test_apps.py +24 -0
- django_spire/notification/sms/tests/test_automation.py +38 -0
- django_spire/notification/sms/tests/test_choices.py +15 -0
- django_spire/notification/sms/tests/test_consts.py +17 -0
- django_spire/notification/sms/tests/test_exceptions.py +27 -0
- django_spire/notification/sms/tests/test_helper.py +50 -0
- django_spire/notification/sms/tests/test_models.py +81 -0
- django_spire/notification/sms/tests/test_processor.py +107 -0
- django_spire/notification/sms/tests/test_tools.py +25 -11
- django_spire/notification/sms/tools.py +16 -5
- django_spire/notification/sms/urls/__init__.py +3 -1
- django_spire/notification/sms/urls/media_urls.py +2 -0
- django_spire/notification/sms/views/media_views.py +14 -4
- django_spire/notification/tests/__init__.py +0 -0
- django_spire/notification/tests/factories.py +26 -0
- django_spire/notification/tests/test_admin.py +55 -0
- django_spire/notification/tests/test_apps.py +30 -0
- django_spire/notification/tests/test_automation.py +18 -0
- django_spire/notification/tests/test_choices.py +59 -0
- django_spire/notification/tests/test_exceptions.py +58 -0
- django_spire/notification/tests/test_managers.py +100 -0
- django_spire/notification/tests/test_maps.py +31 -0
- django_spire/notification/tests/test_models.py +76 -0
- django_spire/notification/tests/test_querysets.py +184 -0
- django_spire/notification/tests/test_utils.py +23 -0
- django_spire/notification/urls.py +3 -1
- django_spire/notification/utils.py +3 -1
- django_spire/settings.py +3 -0
- django_spire/theme/tests/test_context_processor.py +15 -13
- django_spire/theme/tests/test_enums.py +2 -2
- django_spire/theme/tests/test_filesystem.py +2 -5
- django_spire/theme/tests/test_integration.py +12 -12
- django_spire/theme/tests/test_model.py +40 -38
- django_spire/theme/tests/test_views/test_json_views.py +33 -33
- django_spire/theme/urls/json_urls.py +3 -0
- django_spire/theme/urls/page_urls.py +3 -0
- django_spire/urls.py +19 -15
- django_spire/utils.py +13 -4
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/METADATA +1 -1
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/RECORD +530 -358
- django_spire/contrib/options/tests/test_unit.py +0 -148
- django_spire/contrib/seeding/tests/test_seeding.py +0 -25
- django_spire/core/tests/test_templatetags.py +0 -117
- django_spire/core/tests/tests_shortcuts.py +0 -73
- django_spire/history/activity/tests.py +0 -3
- django_spire/history/activity/views.py +0 -3
- django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +0 -71
- django_spire/notification/app/tests.py +0 -3
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/WHEEL +0 -0
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/top_level.txt +0 -0
|
@@ -1,36 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
1
5
|
from django.utils import timezone
|
|
2
6
|
from faker import Faker
|
|
3
7
|
|
|
4
8
|
from django_spire.contrib.seeding.field.base import BaseFieldSeeder
|
|
5
9
|
from django_spire.contrib.seeding.field.enums import FieldSeederTypesEnum
|
|
6
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from django.db.models import Model
|
|
16
|
+
|
|
7
17
|
|
|
8
18
|
class CustomFieldSeeder(BaseFieldSeeder):
|
|
9
19
|
keyword = FieldSeederTypesEnum.CUSTOM
|
|
10
20
|
|
|
11
|
-
def in_order(self, values: list, index: int) ->
|
|
21
|
+
def in_order(self, values: list, index: int) -> Any:
|
|
12
22
|
if not values:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
index = index % len(values)
|
|
23
|
+
message = 'Cannot select from empty values list. Make sure the related model has existing records before seeding foreign keys.'
|
|
24
|
+
raise ValueError(message)
|
|
25
|
+
|
|
26
|
+
# Index loops back on itself
|
|
27
|
+
index = index % len(values)
|
|
18
28
|
return values[index]
|
|
19
29
|
|
|
20
|
-
def date_time_between(self, start_date: str, end_date: str):
|
|
30
|
+
def date_time_between(self, start_date: str, end_date: str) -> datetime:
|
|
21
31
|
faker = Faker()
|
|
22
32
|
naive_dt = faker.date_time_between(start_date=start_date, end_date=end_date)
|
|
23
33
|
return timezone.make_aware(naive_dt)
|
|
24
34
|
|
|
25
|
-
def fk_random(self, model_class, ids: list[int]):
|
|
35
|
+
def fk_random(self, model_class: type[Model], ids: list[int]) -> int:
|
|
26
36
|
faker = Faker()
|
|
27
37
|
return faker.random_element(elements=ids)
|
|
28
38
|
|
|
29
|
-
def fk_in_order(self, model_class, index: int, ids: list[int]):
|
|
39
|
+
def fk_in_order(self, model_class: type[Model], index: int, ids: list[int]) -> int:
|
|
30
40
|
"""Takes a queryset, calls it then returns a random id"""
|
|
31
41
|
return self.in_order(values=ids, index=index)
|
|
32
42
|
|
|
33
|
-
def seed(self, manager, count) -> list[dict]:
|
|
43
|
+
def seed(self, manager: Any, count: int) -> list[dict]:
|
|
34
44
|
data = []
|
|
35
45
|
|
|
36
46
|
foreign_keys = {}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from dandy import BaseIntel, Prompt
|
|
4
6
|
|
|
5
7
|
from django_spire.core.converters import django_to_pydantic_model, fake_model_field_value
|
|
@@ -7,11 +9,14 @@ from django_spire.contrib.seeding.field.base import BaseFieldSeeder
|
|
|
7
9
|
from django_spire.contrib.seeding.field.enums import FieldSeederTypesEnum
|
|
8
10
|
from django_spire.contrib.seeding.intelligence.bots.field_seeding_bots import LlmFieldSeedingBot
|
|
9
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
10
15
|
|
|
11
16
|
class DjangoFieldLlmSeeder(BaseFieldSeeder):
|
|
12
17
|
keyword = FieldSeederTypesEnum.LLM
|
|
13
18
|
|
|
14
|
-
def seed(self, model_seeder, count: int = 1
|
|
19
|
+
def seed(self, model_seeder: Any, count: int = 1) -> list[dict]:
|
|
15
20
|
|
|
16
21
|
include_fields = list(self.seeder_fields.keys())
|
|
17
22
|
|
|
@@ -31,10 +36,7 @@ class DjangoFieldLlmSeeder(BaseFieldSeeder):
|
|
|
31
36
|
base_prompt = (
|
|
32
37
|
Prompt()
|
|
33
38
|
.heading('General Seeding Rules')
|
|
34
|
-
.list(
|
|
35
|
-
[
|
|
36
|
-
'Create data for each field provided.'
|
|
37
|
-
])
|
|
39
|
+
.list(['Create data for each field provided.'])
|
|
38
40
|
.heading('Field Rules & Context')
|
|
39
41
|
.prompt(self.field_prompt)
|
|
40
42
|
)
|
|
@@ -115,7 +117,7 @@ class DjangoFieldLlmSeeder(BaseFieldSeeder):
|
|
|
115
117
|
class DjangoFieldFakerSeeder(BaseFieldSeeder):
|
|
116
118
|
keyword = FieldSeederTypesEnum.FAKER
|
|
117
119
|
|
|
118
|
-
def seed(self, model_seeder, count = 1) -> list[dict]:
|
|
120
|
+
def seed(self, model_seeder: Any, count: int = 1) -> list[dict]:
|
|
119
121
|
data = []
|
|
120
122
|
|
|
121
123
|
for _ in range(count):
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from enum import Enum
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class FieldSeederTypesEnum(str, Enum):
|
|
5
|
-
LLM =
|
|
6
|
-
FAKER =
|
|
7
|
-
STATIC =
|
|
8
|
-
CALLABLE =
|
|
9
|
-
CUSTOM =
|
|
7
|
+
LLM = 'llm'
|
|
8
|
+
FAKER = 'faker'
|
|
9
|
+
STATIC = 'static'
|
|
10
|
+
CALLABLE = 'callable'
|
|
11
|
+
CUSTOM = 'custom'
|
|
@@ -1,18 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
1
9
|
class FieldOverride:
|
|
2
|
-
def __init__(self, seeder_class):
|
|
10
|
+
def __init__(self, seeder_class: Any) -> None:
|
|
3
11
|
self.seeder_class = seeder_class
|
|
4
12
|
self.overrides = {}
|
|
5
13
|
|
|
6
|
-
def __getattr__(self, name):
|
|
14
|
+
def __getattr__(self, name: str) -> Any:
|
|
7
15
|
"""
|
|
8
16
|
Delegate attribute lookup to self.seeder_class if the attribute isn't found
|
|
9
17
|
in this FieldOverride instance.
|
|
10
18
|
"""
|
|
11
19
|
|
|
12
20
|
attr = getattr(self.seeder_class, name)
|
|
21
|
+
|
|
13
22
|
if callable(attr):
|
|
14
|
-
def wrapper(*args, **kwargs):
|
|
23
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
15
24
|
# TODO: Error here if fields is passed as an arg.
|
|
25
|
+
|
|
16
26
|
# Pass the overrides field to the seed method
|
|
17
27
|
if 'fields' in kwargs and isinstance(kwargs['fields'], dict):
|
|
18
28
|
kwargs['fields'] = {**kwargs['fields'], **self.overrides}
|
|
@@ -22,12 +32,12 @@ class FieldOverride:
|
|
|
22
32
|
return attr(*args, **kwargs)
|
|
23
33
|
|
|
24
34
|
return wrapper
|
|
35
|
+
|
|
25
36
|
return attr
|
|
26
37
|
|
|
27
|
-
def filter(self, **kwargs):
|
|
38
|
+
def filter(self, **kwargs: Any) -> FieldOverride:
|
|
28
39
|
self.overrides.update(kwargs)
|
|
29
40
|
return self
|
|
30
41
|
|
|
31
|
-
def seed(self, count=1):
|
|
42
|
+
def seed(self, count: int = 1) -> list:
|
|
32
43
|
return self.seeder_class.seed(count=count, fields=self.overrides)
|
|
33
|
-
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
1
5
|
from django_spire.contrib.seeding.field.base import BaseFieldSeeder
|
|
2
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
3
10
|
|
|
4
11
|
class StaticFieldSeeder(BaseFieldSeeder):
|
|
5
|
-
keyword =
|
|
12
|
+
keyword = 'static'
|
|
6
13
|
|
|
7
|
-
def seed(self, manager = None, count = 1) -> list:
|
|
14
|
+
def seed(self, manager: Any = None, count: int = 1) -> list[dict]:
|
|
8
15
|
return [
|
|
9
16
|
{field_name: value[1] for field_name, value in self.seeder_fields.items()}
|
|
10
17
|
for _ in range(count)
|
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.test import TestCase
|
|
4
|
+
|
|
2
5
|
from django_spire.contrib.seeding.field.base import BaseFieldSeeder
|
|
3
6
|
|
|
4
7
|
|
|
5
8
|
class DummyFieldSeeder(BaseFieldSeeder):
|
|
6
|
-
keyword =
|
|
7
|
-
|
|
8
|
-
def seed(self, model_seeder_cls=None, count: int = 1):
|
|
9
|
-
return [{"fake": True} for _ in range(count)]
|
|
9
|
+
keyword = 'faker'
|
|
10
10
|
|
|
11
|
+
def seed(self, model_seeder_cls=None, count: int = 1) -> list[dict]:
|
|
12
|
+
return [{'fake': True} for _ in range(count)]
|
|
11
13
|
|
|
12
|
-
class TestBaseFieldSeeder(unittest.TestCase):
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
class TestBaseFieldSeeder(TestCase):
|
|
16
|
+
def test_filter_fields(self) -> None:
|
|
15
17
|
fields = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
'name': ('faker', 'name'),
|
|
19
|
+
'email': ('faker', 'email'),
|
|
20
|
+
'description': ('llm', 'prompt')
|
|
19
21
|
}
|
|
20
22
|
seeder = DummyFieldSeeder(fields=fields)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
|
|
24
|
+
faker_fields = seeder.filter_fields('faker')
|
|
25
|
+
|
|
26
|
+
assert 'name' in faker_fields
|
|
27
|
+
assert 'email' in faker_fields
|
|
28
|
+
assert 'description' not in faker_fields
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.test import TestCase
|
|
3
4
|
|
|
5
|
+
from django_spire.contrib.seeding.field.callable import CallableFieldSeeder
|
|
4
6
|
|
|
5
|
-
class TestCallableFieldSeeder(unittest.TestCase):
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
class TestCallableFieldSeeder(TestCase):
|
|
9
|
+
def test_seeds_callable_fields(self) -> None:
|
|
8
10
|
fields = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
'timestamp': lambda: '2024-01-01 12:00:00',
|
|
12
|
+
'status': lambda: 'active'
|
|
11
13
|
}
|
|
12
14
|
seeder = CallableFieldSeeder(fields=fields)
|
|
15
|
+
|
|
13
16
|
result = seeder.seed(None, count=3)
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
assert len(result) == 3
|
|
19
|
+
|
|
16
20
|
for row in result:
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
assert row['timestamp'] == '2024-01-01 12:00:00'
|
|
22
|
+
assert row['status'] == 'active'
|
|
@@ -1,61 +1,74 @@
|
|
|
1
|
-
import
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
from copy import copy
|
|
4
|
+
|
|
5
|
+
from django.test import TestCase
|
|
6
|
+
|
|
3
7
|
from django_spire.contrib.seeding.field.cleaners import normalize_seeder_fields
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
FIELDS = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
'faker_tuple': ('faker', 'name'),
|
|
12
|
+
'llm_type': 'llm',
|
|
13
|
+
'faker_type': 'faker',
|
|
14
|
+
'static_type': 'static',
|
|
15
|
+
'callable_type': 'callable',
|
|
16
|
+
'custom_type': 'custom',
|
|
17
|
+
'static_bool': True,
|
|
18
|
+
'static_str': 'approved',
|
|
19
|
+
'static_int': 10,
|
|
20
|
+
'callable_func': lambda: 'now',
|
|
21
|
+
'exclude_str': 'exclude',
|
|
22
|
+
'exclude_tuple': ('exclude',)
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
|
|
22
|
-
class TestNormalizeSeederFields(
|
|
26
|
+
class TestNormalizeSeederFields(TestCase):
|
|
27
|
+
def test_excludes_fields(self) -> None:
|
|
28
|
+
fields = copy(FIELDS)
|
|
23
29
|
|
|
24
|
-
def test_normalizes_faker_tuple(self):
|
|
25
|
-
fields = {"faker_tuple": FIELDS["faker_tuple"]}
|
|
26
30
|
normalized = normalize_seeder_fields(fields)
|
|
27
|
-
self.assertEqual(normalized["faker_tuple"], ("faker", "name"))
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
assert 'exclude_str' not in normalized
|
|
33
|
+
assert 'exclude_tuple' not in normalized
|
|
34
|
+
assert 'static_str' in normalized
|
|
35
|
+
|
|
36
|
+
def test_normalizes_callable(self) -> None:
|
|
37
|
+
fields = {'callable_func': FIELDS['callable_func']}
|
|
38
|
+
|
|
39
|
+
normalized = normalize_seeder_fields(fields)
|
|
40
|
+
|
|
41
|
+
assert normalized['callable_func'][0] == 'callable'
|
|
42
|
+
assert callable(normalized['callable_func'][1])
|
|
43
|
+
|
|
44
|
+
def test_normalizes_faker_tuple(self) -> None:
|
|
45
|
+
fields = {'faker_tuple': FIELDS['faker_tuple']}
|
|
46
|
+
|
|
31
47
|
normalized = normalize_seeder_fields(fields)
|
|
32
|
-
self.assertNotIn("exclude_str", normalized)
|
|
33
|
-
self.assertNotIn("exclude_tuple", normalized)
|
|
34
|
-
self.assertIn("static_str", normalized)
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
assert normalized['faker_tuple'] == ('faker', 'name')
|
|
50
|
+
|
|
51
|
+
def test_normalizes_single_value_strings_to_tuples(self) -> None:
|
|
37
52
|
fields = {
|
|
38
53
|
k: FIELDS[k] for k in (
|
|
39
|
-
|
|
54
|
+
'llm_type', 'faker_type', 'static_type', 'callable_type', 'custom_type'
|
|
40
55
|
)
|
|
41
56
|
}
|
|
42
|
-
normalized = normalize_seeder_fields(fields)
|
|
43
|
-
for key, value in fields.items():
|
|
44
|
-
self.assertEqual(normalized[key], (value,))
|
|
45
57
|
|
|
46
|
-
def test_normalizes_callable(self):
|
|
47
|
-
fields = {"callable_func": FIELDS["callable_func"]}
|
|
48
58
|
normalized = normalize_seeder_fields(fields)
|
|
49
|
-
self.assertEqual(normalized["callable_func"][0], "callable")
|
|
50
|
-
self.assertTrue(callable(normalized["callable_func"][1]))
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
for key, value in fields.items():
|
|
61
|
+
assert normalized[key] == (value,)
|
|
62
|
+
|
|
63
|
+
def test_normalizes_static_values(self) -> None:
|
|
53
64
|
fields = {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
'static_bool': FIELDS['static_bool'],
|
|
66
|
+
'static_str': FIELDS['static_str'],
|
|
67
|
+
'static_int': FIELDS['static_int']
|
|
57
68
|
}
|
|
69
|
+
|
|
58
70
|
normalized = normalize_seeder_fields(fields)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
|
|
72
|
+
assert normalized['static_bool'] == ('static', True)
|
|
73
|
+
assert normalized['static_str'] == ('static', 'approved')
|
|
74
|
+
assert normalized['static_int'] == ('static', 10)
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.test import TestCase
|
|
3
4
|
|
|
5
|
+
from django_spire.contrib.seeding.field.static import StaticFieldSeeder
|
|
4
6
|
|
|
5
|
-
class TestStaticFieldSeeder(unittest.TestCase):
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
class TestStaticFieldSeeder(TestCase):
|
|
9
|
+
def test_seeds_static_fields(self) -> None:
|
|
8
10
|
fields = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
'category': ('static', 'books'),
|
|
12
|
+
'in_stock': ('static', True)
|
|
11
13
|
}
|
|
12
14
|
seeder = StaticFieldSeeder(fields=fields)
|
|
15
|
+
|
|
13
16
|
result = seeder.seed(count=3)
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
assert len(result) == 3
|
|
19
|
+
|
|
16
20
|
for row in result:
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
assert row['category'] == 'books'
|
|
22
|
+
assert row['in_stock'] is True
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Iterator
|
|
4
|
+
|
|
1
5
|
from dandy import BaseIntel
|
|
2
6
|
|
|
3
7
|
|
|
4
8
|
class SeedingIntel(BaseIntel):
|
|
5
9
|
items: list[dict]
|
|
6
10
|
|
|
7
|
-
def __iter__(self):
|
|
11
|
+
def __iter__(self) -> Iterator[dict]:
|
|
8
12
|
return iter(self.items)
|
|
9
13
|
|
|
10
14
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
2
4
|
|
|
3
5
|
from dandy import Prompt
|
|
4
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from django.db.models.base import Model
|
|
9
|
+
|
|
5
10
|
|
|
6
11
|
class SeedingModelClassPromptFactory:
|
|
7
12
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
2
4
|
|
|
3
5
|
from dandy import Prompt
|
|
4
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from django.db.models import Model
|
|
9
|
+
|
|
5
10
|
|
|
6
11
|
def foreign_key_selection_prompt(
|
|
7
12
|
model_class: type[Model],
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
1
5
|
from django.contrib.contenttypes.models import ContentType
|
|
2
|
-
from django.db.models import Model
|
|
3
6
|
|
|
4
7
|
from dandy import Prompt
|
|
5
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from django.db.models import Model
|
|
11
|
+
|
|
6
12
|
|
|
7
13
|
def generic_relationship_selection_prompt(
|
|
8
14
|
model_class: type[Model],
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from random import shuffle
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
5
6
|
from dandy import Prompt
|
|
6
7
|
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from django.db.models import Model
|
|
10
|
+
|
|
7
11
|
|
|
8
12
|
def hierarchical_selection_prompt(
|
|
9
13
|
model_class: type[Model],
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from dandy import Prompt
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from django.db.models.fields import Field
|
|
9
|
+
from django.db.models.enums import TextChoices
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
def model_field_choices_prompt(
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
2
4
|
|
|
3
5
|
from dandy import Prompt
|
|
4
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from django.db.models import Model
|
|
9
|
+
|
|
5
10
|
|
|
6
11
|
def objective_prompt(
|
|
7
12
|
model_class: type[Model],
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
2
5
|
|
|
3
6
|
from django.core.management.base import BaseCommand, CommandError
|
|
4
7
|
|
|
5
8
|
from dandy.conf import settings
|
|
6
|
-
from dandy.recorder import recorder_to_html_file
|
|
7
9
|
|
|
8
10
|
from django_spire.contrib.seeding.intelligence.bots.seeder_generator_bot import SeederGeneratorBot
|
|
9
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from argparse import ArgumentParser
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
10
16
|
|
|
11
17
|
_SEEDING_OUTPUT_PATH = Path(settings.BASE_PATH, '.seeding_generator_output')
|
|
12
18
|
|
|
@@ -14,7 +20,7 @@ _SEEDING_OUTPUT_PATH = Path(settings.BASE_PATH, '.seeding_generator_output')
|
|
|
14
20
|
class Command(BaseCommand):
|
|
15
21
|
help = 'Generate a Seeder'
|
|
16
22
|
|
|
17
|
-
def add_arguments(self, parser):
|
|
23
|
+
def add_arguments(self, parser: ArgumentParser) -> None:
|
|
18
24
|
parser.add_argument(
|
|
19
25
|
'model_import',
|
|
20
26
|
type=str,
|
|
@@ -29,7 +35,7 @@ class Command(BaseCommand):
|
|
|
29
35
|
)
|
|
30
36
|
|
|
31
37
|
# @recorder_to_html_file('seeding_generator')
|
|
32
|
-
def handle(self, *args, **kwargs):
|
|
38
|
+
def handle(self, *args: Any, **kwargs: Any) -> None:
|
|
33
39
|
if not kwargs['model_import'] or not kwargs['model_description']:
|
|
34
40
|
message = 'You must provide a model import path and a model description'
|
|
35
41
|
raise CommandError(message)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
2
5
|
|
|
3
6
|
from dandy.recorder import recorder_to_html_file
|
|
4
7
|
from dandy import SqliteCache, generate_cache_key
|
|
@@ -6,12 +9,15 @@ from dandy import SqliteCache, generate_cache_key
|
|
|
6
9
|
from django_spire.contrib.seeding.field.override import FieldOverride
|
|
7
10
|
from django_spire.contrib.seeding.model.config import FieldsConfig
|
|
8
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
9
15
|
|
|
10
16
|
class classproperty:
|
|
11
|
-
def __init__(self, fget):
|
|
17
|
+
def __init__(self, fget: Any) -> None:
|
|
12
18
|
self.fget = fget
|
|
13
19
|
|
|
14
|
-
def __get__(self, instance, owner):
|
|
20
|
+
def __get__(self, instance: Any, owner: Any) -> Any:
|
|
15
21
|
return self.fget(owner)
|
|
16
22
|
|
|
17
23
|
|
|
@@ -33,30 +39,33 @@ class BaseModelSeeder(ABC):
|
|
|
33
39
|
_field_config: FieldsConfig = None
|
|
34
40
|
|
|
35
41
|
@classproperty
|
|
36
|
-
def override(cls):
|
|
42
|
+
def override(cls) -> FieldOverride:
|
|
37
43
|
return cls.override_class(seeder_class=cls)
|
|
38
44
|
|
|
39
45
|
@classmethod
|
|
40
46
|
def get_field_config(cls) -> FieldsConfig:
|
|
41
47
|
if cls._field_config is None:
|
|
42
48
|
if cls.model_class is None:
|
|
43
|
-
|
|
49
|
+
message = 'model_class must be defined before using seeder.'
|
|
50
|
+
raise ValueError(message)
|
|
44
51
|
|
|
45
52
|
raw_fields = cls.__dict__.get('fields', {})
|
|
53
|
+
|
|
46
54
|
cls._field_config = cls.field_config_class(
|
|
47
55
|
raw_fields=raw_fields,
|
|
48
56
|
field_names=cls.field_names(),
|
|
49
57
|
default_to=cls.default_to,
|
|
50
58
|
model_class=cls.model_class,
|
|
51
59
|
)
|
|
60
|
+
|
|
52
61
|
return cls._field_config
|
|
53
62
|
|
|
54
63
|
@classmethod
|
|
55
|
-
def resolved_fields(cls):
|
|
64
|
+
def resolved_fields(cls) -> dict:
|
|
56
65
|
return cls.get_field_config().fields
|
|
57
66
|
|
|
58
67
|
@classmethod
|
|
59
|
-
def clear_cache(cls):
|
|
68
|
+
def clear_cache(cls) -> None:
|
|
60
69
|
SqliteCache.clear(cache_name=cls.cache_name)
|
|
61
70
|
|
|
62
71
|
@classmethod
|
|
@@ -68,7 +77,7 @@ class BaseModelSeeder(ABC):
|
|
|
68
77
|
@recorder_to_html_file('model_seeder')
|
|
69
78
|
def seed_data(
|
|
70
79
|
cls,
|
|
71
|
-
count=1,
|
|
80
|
+
count: int = 1,
|
|
72
81
|
fields: dict | None = None,
|
|
73
82
|
) -> list[dict]:
|
|
74
83
|
field_config = (
|