django-spire 0.16.12__py3-none-any.whl → 0.17.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_spire/ai/admin.py +3 -1
- django_spire/ai/apps.py +2 -0
- django_spire/ai/chat/admin.py +15 -9
- django_spire/ai/chat/apps.py +4 -1
- django_spire/ai/chat/auth/controller.py +3 -1
- django_spire/ai/chat/choices.py +2 -0
- django_spire/ai/chat/intelligence/maps/intent_llm_map.py +8 -5
- django_spire/ai/chat/intelligence/prompts.py +4 -2
- django_spire/ai/chat/intelligence/workflows/chat_workflow.py +27 -28
- django_spire/ai/chat/message_intel.py +7 -4
- django_spire/ai/chat/models.py +8 -9
- django_spire/ai/chat/querysets.py +3 -1
- django_spire/ai/chat/responses.py +19 -10
- django_spire/ai/chat/tools.py +20 -15
- django_spire/ai/chat/urls/message_urls.py +2 -1
- django_spire/ai/chat/urls/page_urls.py +1 -0
- django_spire/ai/chat/views/message_request_views.py +2 -0
- django_spire/ai/chat/views/message_response_views.py +4 -4
- django_spire/ai/chat/views/message_views.py +2 -0
- django_spire/ai/chat/views/page_views.py +7 -2
- django_spire/ai/chat/views/template_views.py +2 -0
- django_spire/ai/decorators.py +13 -7
- django_spire/ai/mixins.py +4 -2
- django_spire/ai/models.py +7 -2
- django_spire/ai/prompt/bots.py +14 -32
- django_spire/ai/prompt/intel.py +1 -1
- django_spire/ai/prompt/prompts.py +7 -1
- django_spire/ai/prompt/system/bots.py +42 -75
- django_spire/ai/prompt/system/intel.py +5 -4
- django_spire/ai/prompt/system/prompts.py +5 -1
- django_spire/ai/prompt/system/system_prompt_cli.py +15 -9
- django_spire/ai/prompt/tests/test_bots.py +14 -11
- django_spire/ai/prompt/text_to_prompt_cli.py +5 -2
- django_spire/ai/prompt/tuning/bot_tuning_cli.py +14 -13
- django_spire/ai/prompt/tuning/bots.py +68 -116
- django_spire/ai/prompt/tuning/intel.py +1 -1
- django_spire/ai/prompt/tuning/mixins.py +2 -0
- django_spire/ai/prompt/tuning/prompt_tuning_cli.py +8 -8
- django_spire/ai/prompt/tuning/prompts.py +4 -2
- django_spire/ai/sms/admin.py +3 -1
- django_spire/ai/sms/apps.py +2 -0
- django_spire/ai/sms/decorators.py +2 -0
- django_spire/ai/sms/intel.py +4 -2
- django_spire/ai/sms/intelligence/workflows/sms_conversation_workflow.py +8 -8
- django_spire/ai/sms/models.py +16 -14
- django_spire/ai/sms/querysets.py +4 -1
- django_spire/ai/sms/tools.py +18 -16
- django_spire/ai/sms/urls.py +1 -1
- django_spire/ai/sms/views.py +2 -0
- django_spire/ai/tests/test_ai.py +3 -5
- django_spire/ai/urls.py +1 -0
- django_spire/consts.py +1 -1
- django_spire/contrib/seeding/field/django/seeder.py +6 -4
- django_spire/contrib/seeding/field/llm.py +1 -2
- django_spire/contrib/seeding/intelligence/bots/field_seeding_bots.py +7 -8
- django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +15 -16
- django_spire/contrib/seeding/intelligence/intel.py +1 -1
- django_spire/contrib/seeding/intelligence/prompts/factory.py +5 -7
- django_spire/contrib/seeding/intelligence/prompts/foreign_key_selection_prompt.py +3 -5
- django_spire/contrib/seeding/intelligence/prompts/generate_django_model_seeder_prompts.py +1 -2
- django_spire/contrib/seeding/intelligence/prompts/generic_relationship_selection_prompt.py +3 -5
- django_spire/contrib/seeding/intelligence/prompts/hierarchical_selection_prompt.py +1 -1
- django_spire/contrib/seeding/intelligence/prompts/model_field_choices_prompt.py +3 -3
- django_spire/contrib/seeding/intelligence/prompts/negation_prompt.py +1 -1
- django_spire/contrib/seeding/intelligence/prompts/objective_prompt.py +2 -4
- django_spire/contrib/seeding/management/commands/seeding.py +5 -2
- django_spire/contrib/seeding/model/base.py +12 -10
- django_spire/contrib/seeding/model/django/seeder.py +13 -10
- django_spire/contrib/seeding/tests/test_seeding.py +1 -1
- django_spire/core/management/commands/spire_startapp.py +84 -46
- django_spire/core/management/commands/spire_startapp_pkg/__init__.py +60 -0
- django_spire/core/management/commands/spire_startapp_pkg/builder.py +91 -0
- django_spire/core/management/commands/spire_startapp_pkg/config.py +115 -0
- django_spire/core/management/commands/spire_startapp_pkg/filesystem.py +125 -0
- django_spire/core/management/commands/spire_startapp_pkg/generator.py +167 -0
- django_spire/core/management/commands/spire_startapp_pkg/maps.py +783 -25
- django_spire/core/management/commands/spire_startapp_pkg/permissions.py +147 -0
- django_spire/core/management/commands/spire_startapp_pkg/processor.py +144 -57
- django_spire/core/management/commands/spire_startapp_pkg/registry.py +89 -0
- django_spire/core/management/commands/spire_startapp_pkg/reporter.py +245 -108
- django_spire/core/management/commands/spire_startapp_pkg/resolver.py +86 -0
- django_spire/core/management/commands/spire_startapp_pkg/user_input.py +252 -0
- django_spire/core/management/commands/spire_startapp_pkg/validator.py +96 -0
- django_spire/core/middleware/__init__.py +1 -2
- django_spire/knowledge/admin.py +2 -0
- django_spire/knowledge/apps.py +2 -0
- django_spire/knowledge/auth/controller.py +2 -0
- django_spire/knowledge/collection/admin.py +3 -0
- django_spire/knowledge/collection/forms.py +2 -0
- django_spire/knowledge/collection/models.py +4 -0
- django_spire/knowledge/collection/querysets.py +3 -3
- django_spire/knowledge/collection/seeding/seed.py +2 -0
- django_spire/knowledge/collection/tests/factories.py +2 -0
- django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +2 -0
- django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +2 -0
- django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +2 -0
- django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +2 -0
- django_spire/knowledge/collection/views/json_views.py +2 -0
- django_spire/knowledge/collection/views/page_views.py +2 -0
- django_spire/knowledge/context_processors.py +2 -0
- django_spire/knowledge/entry/services/tool_service.py +1 -0
- django_spire/knowledge/entry/services/transformation_services.py +0 -1
- django_spire/knowledge/entry/tests/factories.py +3 -0
- django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +2 -0
- django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +2 -0
- django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +2 -0
- django_spire/knowledge/entry/urls/form_urls.py +1 -0
- django_spire/knowledge/entry/urls/json_urls.py +1 -0
- django_spire/knowledge/entry/urls/page_urls.py +1 -0
- django_spire/knowledge/entry/urls/template_urls.py +1 -0
- django_spire/knowledge/entry/version/admin.py +2 -0
- django_spire/knowledge/entry/version/block/admin.py +2 -0
- django_spire/knowledge/entry/version/block/blocks/heading_block.py +2 -0
- django_spire/knowledge/entry/version/block/blocks/sub_heading_block.py +2 -0
- django_spire/knowledge/entry/version/block/blocks/text_block.py +2 -0
- django_spire/knowledge/entry/version/block/maps.py +2 -0
- django_spire/knowledge/entry/version/block/models.py +2 -0
- django_spire/knowledge/entry/version/block/tests/factories.py +2 -0
- django_spire/knowledge/entry/version/block/tests/test_urls/test_json_urls.py +2 -0
- django_spire/knowledge/entry/version/block/views/json_views.py +2 -0
- django_spire/knowledge/entry/version/intelligence/bots/markdown_format_llm_bot.py +12 -9
- django_spire/knowledge/entry/version/maps.py +2 -0
- django_spire/knowledge/entry/version/models.py +2 -0
- django_spire/knowledge/entry/version/querysets.py +2 -0
- django_spire/knowledge/entry/version/seeding/seeder.py +1 -0
- django_spire/knowledge/entry/version/tests/factories.py +2 -0
- django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +3 -0
- django_spire/knowledge/entry/version/tests/test_urls/test_form_urls.py +2 -0
- django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +2 -0
- django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +2 -0
- django_spire/knowledge/entry/version/urls/form_urls.py +1 -0
- django_spire/knowledge/entry/version/urls/json_urls.py +1 -0
- django_spire/knowledge/entry/version/urls/page_urls.py +1 -0
- django_spire/knowledge/entry/version/urls/redirect_urls.py +1 -0
- django_spire/knowledge/entry/version/views/form_views.py +2 -0
- django_spire/knowledge/entry/version/views/json_views.py +2 -0
- django_spire/knowledge/entry/version/views/page_views.py +2 -0
- django_spire/knowledge/entry/version/views/redirect_views.py +2 -0
- django_spire/knowledge/exceptions.py +2 -0
- django_spire/knowledge/intelligence/bots/entry_search_llm_bot.py +5 -16
- django_spire/knowledge/intelligence/intel/collection_intel.py +3 -1
- django_spire/knowledge/intelligence/intel/entry_intel.py +3 -3
- django_spire/knowledge/intelligence/intel/message_intel.py +2 -0
- django_spire/knowledge/intelligence/maps/collection_map.py +9 -10
- django_spire/knowledge/intelligence/maps/entry_map.py +8 -9
- django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +8 -6
- django_spire/knowledge/models.py +2 -0
- django_spire/knowledge/seeding/seed.py +2 -0
- django_spire/knowledge/templatetags/spire_knowledge_tags.py +3 -0
- django_spire/knowledge/urls/__init__.py +1 -0
- django_spire/profiling/__init__.py +13 -0
- django_spire/profiling/middleware/__init__.py +6 -0
- django_spire/{core → profiling}/middleware/profiling.py +63 -58
- django_spire/profiling/panel.py +345 -0
- django_spire/profiling/templates/panel.html +166 -0
- {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/METADATA +5 -4
- {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/RECORD +160 -157
- django_spire/core/management/commands/spire_startapp_pkg/constants.py +0 -4
- django_spire/core/management/commands/spire_startapp_pkg/manager.py +0 -176
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/spirechildapp_detail_card.html +0 -24
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/spirechildapp_form_card.html +0 -9
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/spirechildapp_list_card.html +0 -18
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/form/spirechildapp_form.html +0 -22
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/item/spirechildapp_item.html +0 -24
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/spirechildapp_detail_page.html +0 -13
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/spirechildapp_form_page.html +0 -13
- django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/spirechildapp_list_page.html +0 -9
- {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/WHEEL +0 -0
- {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/top_level.txt +0 -0
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
from dandy
|
|
2
|
-
|
|
1
|
+
from dandy import Bot, LlmConfigOptions
|
|
3
2
|
|
|
4
3
|
from django_spire.contrib.seeding.intelligence.intel import SourceIntel
|
|
4
|
+
from django_spire.contrib.seeding.intelligence.prompts.generate_django_model_seeder_prompts import (
|
|
5
|
+
generate_django_model_seeder_user_prompt,
|
|
6
|
+
generate_django_model_seeder_system_prompt
|
|
7
|
+
)
|
|
5
8
|
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class SeederGeneratorBot(BaseLlmBot):
|
|
10
|
+
class SeederGeneratorBot(Bot):
|
|
11
|
+
llm_config = 'PYTHON_MODULE'
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
config_options = LlmConfigOptions(
|
|
13
|
+
llm_config_options = LlmConfigOptions(
|
|
16
14
|
temperature=0.3,
|
|
17
15
|
randomize_seed=True
|
|
18
16
|
)
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
llm_role = 'You are an expert Python developer specializing in Django model seeders.'
|
|
19
|
+
|
|
21
20
|
def process(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
):
|
|
26
|
-
return
|
|
21
|
+
self,
|
|
22
|
+
model_import: str,
|
|
23
|
+
model_description: str
|
|
24
|
+
) -> SourceIntel:
|
|
25
|
+
return self.llm.prompt_to_intel(
|
|
27
26
|
prompt=generate_django_model_seeder_user_prompt(
|
|
28
27
|
model_import,
|
|
29
28
|
model_description,
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
from typing import Type
|
|
2
|
-
|
|
3
1
|
from django.db.models.base import Model
|
|
4
2
|
|
|
5
|
-
from dandy
|
|
3
|
+
from dandy import Prompt
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
class SeedingModelClassPromptFactory:
|
|
9
7
|
|
|
10
|
-
def __init__(self, model_class:
|
|
8
|
+
def __init__(self, model_class: type[Model]):
|
|
11
9
|
self.model_class = model_class
|
|
12
10
|
|
|
13
11
|
def objective_prompt(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
self,
|
|
13
|
+
model_description: str,
|
|
14
|
+
sector_description: str
|
|
17
15
|
) -> Prompt:
|
|
18
16
|
model_name = self.model_class._meta.verbose_name_plural.title()
|
|
19
17
|
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
from typing_extensions import Type
|
|
2
|
-
|
|
3
1
|
from django.db.models import Model
|
|
4
2
|
|
|
5
|
-
from dandy
|
|
3
|
+
from dandy import Prompt
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
def foreign_key_selection_prompt(
|
|
9
|
-
model_class:
|
|
10
|
-
related_model_class:
|
|
7
|
+
model_class: type[Model],
|
|
8
|
+
related_model_class: type[Model]
|
|
11
9
|
) -> Prompt:
|
|
12
10
|
model_name = model_class._meta.verbose_name.title()
|
|
13
11
|
related_model_name = related_model_class._meta.verbose_name.title()
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
from typing_extensions import List, Type
|
|
2
|
-
|
|
3
1
|
from django.contrib.contenttypes.models import ContentType
|
|
4
2
|
from django.db.models import Model
|
|
5
3
|
|
|
6
|
-
from dandy
|
|
4
|
+
from dandy import Prompt
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
def generic_relationship_selection_prompt(
|
|
10
|
-
model_class:
|
|
11
|
-
related_model_classes:
|
|
8
|
+
model_class: type[Model],
|
|
9
|
+
related_model_classes: list[type[Model]]
|
|
12
10
|
) -> Prompt:
|
|
13
11
|
related_model_classes_map = {
|
|
14
12
|
related_model_class._meta.verbose_name.title(): {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from dandy
|
|
1
|
+
from dandy import Prompt
|
|
2
2
|
from django.db.models.enums import TextChoices
|
|
3
3
|
from django.db.models.fields import Field
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def model_field_choices_prompt(
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
model_field: Field,
|
|
8
|
+
choices: TextChoices
|
|
9
9
|
) -> Prompt:
|
|
10
10
|
model_name = model_field.field.model.__name__
|
|
11
11
|
field_name = model_field.field.name
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
from django.db.models import Model
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from dandy.llm import Prompt
|
|
3
|
+
from dandy import Prompt
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
def objective_prompt(
|
|
9
|
-
model_class:
|
|
7
|
+
model_class: type[Model],
|
|
10
8
|
model_description: str,
|
|
11
9
|
sector_description: str,
|
|
12
10
|
) -> Prompt:
|
|
@@ -31,12 +31,15 @@ class Command(BaseCommand):
|
|
|
31
31
|
# @recorder_to_html_file('seeding_generator')
|
|
32
32
|
def handle(self, *args, **kwargs):
|
|
33
33
|
if not kwargs['model_import'] or not kwargs['model_description']:
|
|
34
|
-
|
|
34
|
+
message = 'You must provide a model import path and a model description'
|
|
35
|
+
raise CommandError(message)
|
|
35
36
|
|
|
36
37
|
model_import = kwargs['model_import']
|
|
37
38
|
model_description = ' '.join(kwargs['model_description'])
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
# Instantiate the bot
|
|
41
|
+
bot = SeederGeneratorBot()
|
|
42
|
+
source_intel = bot.process(model_import, model_description)
|
|
40
43
|
|
|
41
44
|
Path(_SEEDING_OUTPUT_PATH).mkdir(parents=True, exist_ok=True)
|
|
42
45
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
|
|
3
3
|
from dandy.recorder import recorder_to_html_file
|
|
4
|
-
from dandy
|
|
5
|
-
from dandy.cache.utils import
|
|
4
|
+
from dandy import SqliteCache
|
|
5
|
+
from dandy.cache.utils import generate_cache_key
|
|
6
6
|
|
|
7
7
|
from django_spire.contrib.seeding.field.override import FieldOverride
|
|
8
8
|
from django_spire.contrib.seeding.model.config import FieldsConfig
|
|
@@ -59,10 +59,7 @@ class BaseModelSeeder(ABC):
|
|
|
59
59
|
|
|
60
60
|
@classmethod
|
|
61
61
|
def clear_cache(cls):
|
|
62
|
-
SqliteCache(
|
|
63
|
-
cache_name=cls.cache_name,
|
|
64
|
-
limit=cls.cache_limit
|
|
65
|
-
).clear(cache_name=cls.cache_name)
|
|
62
|
+
SqliteCache.clear(cache_name=cls.cache_name)
|
|
66
63
|
|
|
67
64
|
@classmethod
|
|
68
65
|
@abstractmethod
|
|
@@ -80,9 +77,14 @@ class BaseModelSeeder(ABC):
|
|
|
80
77
|
field_config = cls.get_field_config().override(fields) if fields else cls.get_field_config()
|
|
81
78
|
|
|
82
79
|
if cls.cache_seed:
|
|
83
|
-
|
|
80
|
+
cache_key = generate_cache_key(
|
|
81
|
+
cls.seed_data,
|
|
82
|
+
count=count,
|
|
83
|
+
fields=field_config.fields
|
|
84
|
+
)
|
|
85
|
+
|
|
84
86
|
cache = SqliteCache(cache_name=cls.cache_name, limit=cls.cache_limit)
|
|
85
|
-
formatted_seed_data = cache.get(
|
|
87
|
+
formatted_seed_data = cache.get(cache_key)
|
|
86
88
|
|
|
87
89
|
if formatted_seed_data:
|
|
88
90
|
return formatted_seed_data
|
|
@@ -95,13 +97,13 @@ class BaseModelSeeder(ABC):
|
|
|
95
97
|
if len(seeder.seeder_fields) > 0:
|
|
96
98
|
seed_data.append(seeder.seed(cls, count))
|
|
97
99
|
|
|
98
|
-
formatted_seed_data = [
|
|
100
|
+
formatted_seed_data = [{} for _ in range(max(len(sublist) for sublist in seed_data))]
|
|
99
101
|
for sublist in seed_data:
|
|
100
102
|
for i, d in enumerate(sublist):
|
|
101
103
|
formatted_seed_data[i].update(d)
|
|
102
104
|
|
|
103
105
|
if cls.cache_seed:
|
|
104
|
-
cache.set(
|
|
106
|
+
cache.set(cache_key, formatted_seed_data)
|
|
105
107
|
|
|
106
108
|
return formatted_seed_data
|
|
107
109
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import TypeVar, TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
from dandy.llm import Prompt
|
|
6
5
|
from django.db.models import ForeignKey
|
|
7
|
-
|
|
8
6
|
from django.db.models.base import Model
|
|
9
7
|
|
|
10
8
|
from django_spire.contrib.seeding.field.callable import CallableFieldSeeder
|
|
@@ -14,6 +12,9 @@ from django_spire.contrib.seeding.field.static import StaticFieldSeeder
|
|
|
14
12
|
from django_spire.contrib.seeding.model.base import BaseModelSeeder
|
|
15
13
|
from django_spire.contrib.seeding.model.django.config import DjangoModelFieldsConfig
|
|
16
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from dandy import Prompt
|
|
17
|
+
|
|
17
18
|
|
|
18
19
|
TypeModel = TypeVar('TypeModel', bound=Model)
|
|
19
20
|
|
|
@@ -21,7 +22,7 @@ TypeModel = TypeVar('TypeModel', bound=Model)
|
|
|
21
22
|
class DjangoModelSeeder(BaseModelSeeder):
|
|
22
23
|
field_config_class = DjangoModelFieldsConfig
|
|
23
24
|
|
|
24
|
-
model_class:
|
|
25
|
+
model_class: type[Model]
|
|
25
26
|
prompt: Prompt = None
|
|
26
27
|
_field_seeders = [
|
|
27
28
|
CustomFieldSeeder,
|
|
@@ -32,14 +33,16 @@ class DjangoModelSeeder(BaseModelSeeder):
|
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
@classmethod
|
|
35
|
-
def __init_subclass__(cls, **kwargs):
|
|
36
|
+
def __init_subclass__(cls, **kwargs) -> None:
|
|
36
37
|
super().__init_subclass__(**kwargs)
|
|
37
38
|
|
|
38
39
|
if cls.model_class is None:
|
|
39
|
-
|
|
40
|
+
message = "Seeds must have a model class"
|
|
41
|
+
raise ValueError(message)
|
|
40
42
|
|
|
41
43
|
if cls.fields is None:
|
|
42
|
-
|
|
44
|
+
message = "Seeds must have fields"
|
|
45
|
+
raise ValueError(message)
|
|
43
46
|
|
|
44
47
|
@classmethod
|
|
45
48
|
def field_names(cls) -> list[str]:
|
|
@@ -51,9 +54,9 @@ class DjangoModelSeeder(BaseModelSeeder):
|
|
|
51
54
|
|
|
52
55
|
@classmethod
|
|
53
56
|
def seed_database(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
cls,
|
|
58
|
+
count=1,
|
|
59
|
+
fields: dict | None = None
|
|
57
60
|
) -> list[TypeModel]:
|
|
58
61
|
model_objects = cls.seed(count, fields)
|
|
59
62
|
return cls.model_class.objects.bulk_create(model_objects)
|
|
@@ -1,80 +1,118 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from django.core.management.base import BaseCommand
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
from django_spire.core.management.commands.spire_startapp_pkg.builder import TemplateBuilder
|
|
8
|
+
from django_spire.core.management.commands.spire_startapp_pkg.config import (
|
|
9
|
+
AppConfigFactory,
|
|
10
|
+
PathConfig,
|
|
11
|
+
)
|
|
12
|
+
from django_spire.core.management.commands.spire_startapp_pkg.filesystem import FileSystem
|
|
13
|
+
from django_spire.core.management.commands.spire_startapp_pkg.generator import (
|
|
14
|
+
AppGenerator,
|
|
15
|
+
# TemplateGenerator,
|
|
13
16
|
)
|
|
14
17
|
from django_spire.core.management.commands.spire_startapp_pkg.processor import (
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
TemplateEngine,
|
|
19
|
+
TemplateProcessor,
|
|
17
20
|
)
|
|
21
|
+
from django_spire.core.management.commands.spire_startapp_pkg.registry import AppRegistry
|
|
18
22
|
from django_spire.core.management.commands.spire_startapp_pkg.reporter import Reporter
|
|
23
|
+
from django_spire.core.management.commands.spire_startapp_pkg.resolver import PathResolver
|
|
24
|
+
from django_spire.core.management.commands.spire_startapp_pkg.user_input import UserInputCollector
|
|
25
|
+
from django_spire.core.management.commands.spire_startapp_pkg.validator import AppValidator
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from typing import Any
|
|
19
29
|
|
|
20
30
|
|
|
21
31
|
class Command(BaseCommand):
|
|
32
|
+
"""
|
|
33
|
+
Django management command for creating custom Spire apps.
|
|
34
|
+
|
|
35
|
+
This command guides users through an interactive wizard to create a new Django app
|
|
36
|
+
with a pre-configured structure including models, views, forms, services, and templates.
|
|
37
|
+
It handles nested app structures (e.g., app.parent.child) and validates that all
|
|
38
|
+
parent apps are properly registered before creating child apps.
|
|
39
|
+
"""
|
|
40
|
+
|
|
22
41
|
help = 'Create a custom Spire app.'
|
|
23
42
|
|
|
24
|
-
def
|
|
25
|
-
|
|
43
|
+
def handle(self, *args: Any, **kwargs: Any) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Main entry point for the management command.
|
|
46
|
+
|
|
47
|
+
Orchestrates the entire app creation process by initializing helper classes,
|
|
48
|
+
collecting user input through an interactive wizard, validating the app structure,
|
|
49
|
+
generating app files and templates, and providing feedback to the user.
|
|
26
50
|
|
|
27
|
-
|
|
28
|
-
|
|
51
|
+
:param args: Positional arguments (not used).
|
|
52
|
+
:param kwargs: Keyword arguments (not used).
|
|
53
|
+
"""
|
|
29
54
|
|
|
30
|
-
|
|
31
|
-
|
|
55
|
+
filesystem = FileSystem()
|
|
56
|
+
path_config = PathConfig.default()
|
|
57
|
+
path_resolver = PathResolver()
|
|
58
|
+
registry = AppRegistry()
|
|
59
|
+
reporter = Reporter(self)
|
|
32
60
|
|
|
33
|
-
|
|
34
|
-
|
|
61
|
+
template_engine = TemplateEngine()
|
|
62
|
+
template_processor = TemplateProcessor(template_engine, filesystem)
|
|
35
63
|
|
|
36
|
-
|
|
37
|
-
self.html_processor = HTMLTemplateProcessor()
|
|
64
|
+
validator = AppValidator(reporter, registry, path_resolver, filesystem)
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
user_input_collector = UserInputCollector(reporter, validator)
|
|
40
67
|
|
|
41
|
-
|
|
42
|
-
from django.apps import apps
|
|
43
|
-
return [config.name for config in apps.get_app_configs()]
|
|
68
|
+
config_factory = AppConfigFactory(path_resolver)
|
|
44
69
|
|
|
45
|
-
|
|
46
|
-
app = input('Enter the path of the app (e.g., "app.maintenance.work_order"):')
|
|
70
|
+
template_builder = TemplateBuilder(reporter)
|
|
47
71
|
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
app_generator = AppGenerator(filesystem, template_processor, reporter, path_config)
|
|
73
|
+
# template_generator = TemplateGenerator(filesystem, template_processor, reporter, path_config)
|
|
50
74
|
|
|
51
|
-
|
|
75
|
+
user_inputs = user_input_collector.collect_all_inputs()
|
|
76
|
+
app_path = user_inputs['app_path']
|
|
52
77
|
|
|
53
|
-
|
|
54
|
-
self.app_manager.is_valid_root_apps(components)
|
|
78
|
+
validator.validate_app_format(app_path)
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
config = config_factory.create_config(app_path, user_inputs)
|
|
81
|
+
validator.validate_root_app(config.components)
|
|
57
82
|
|
|
58
|
-
|
|
59
|
-
f'
|
|
60
|
-
|
|
83
|
+
reporter.write(
|
|
84
|
+
f'\nChecking app components: {config.components}\n',
|
|
85
|
+
reporter.style_notice
|
|
61
86
|
)
|
|
62
87
|
|
|
63
|
-
missing =
|
|
88
|
+
missing = registry.get_missing_components(config.components)
|
|
64
89
|
|
|
65
90
|
if missing:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
91
|
+
reporter.report_missing_components(missing)
|
|
92
|
+
|
|
93
|
+
template_builder.build_app_tree_structure(
|
|
94
|
+
path_resolver.get_base_dir(),
|
|
95
|
+
config.components,
|
|
96
|
+
registry.get_installed_apps(),
|
|
97
|
+
path_config.app_template
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# template_builder.build_html_tree_structure(
|
|
101
|
+
# path_resolver.get_base_dir(),
|
|
102
|
+
# config.components,
|
|
103
|
+
# registry.get_installed_apps(),
|
|
104
|
+
# path_config.html_template
|
|
105
|
+
# )
|
|
106
|
+
|
|
107
|
+
if not reporter.prompt_confirmation('\nProceed with app creation? (y/n): '):
|
|
108
|
+
reporter.write('App creation aborted.', reporter.style_error)
|
|
72
109
|
return
|
|
73
110
|
|
|
74
111
|
for module in [missing[-1]]:
|
|
75
|
-
|
|
76
|
-
|
|
112
|
+
module_config = config_factory.create_config(module, user_inputs)
|
|
113
|
+
app_generator.generate(module_config)
|
|
114
|
+
# template_generator.generate(module_config)
|
|
77
115
|
|
|
78
|
-
|
|
116
|
+
reporter.report_installed_apps_suggestion(missing)
|
|
79
117
|
else:
|
|
80
|
-
|
|
118
|
+
reporter.write('All component(s) exist.', reporter.style_success)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.management.commands.spire_startapp_pkg.builder import TemplateBuilder
|
|
4
|
+
from django_spire.core.management.commands.spire_startapp_pkg.config import (
|
|
5
|
+
AppConfig,
|
|
6
|
+
AppConfigFactory,
|
|
7
|
+
PathConfig,
|
|
8
|
+
)
|
|
9
|
+
from django_spire.core.management.commands.spire_startapp_pkg.filesystem import (
|
|
10
|
+
FileSystem,
|
|
11
|
+
FileSystemInterface,
|
|
12
|
+
)
|
|
13
|
+
from django_spire.core.management.commands.spire_startapp_pkg.generator import (
|
|
14
|
+
AppGenerator,
|
|
15
|
+
TemplateGenerator,
|
|
16
|
+
)
|
|
17
|
+
from django_spire.core.management.commands.spire_startapp_pkg.maps import generate_replacement_map
|
|
18
|
+
from django_spire.core.management.commands.spire_startapp_pkg.permissions import PermissionInheritanceHandler
|
|
19
|
+
from django_spire.core.management.commands.spire_startapp_pkg.processor import (
|
|
20
|
+
TemplateEngine,
|
|
21
|
+
TemplateProcessor,
|
|
22
|
+
)
|
|
23
|
+
from django_spire.core.management.commands.spire_startapp_pkg.registry import (
|
|
24
|
+
AppRegistry,
|
|
25
|
+
AppRegistryInterface,
|
|
26
|
+
)
|
|
27
|
+
from django_spire.core.management.commands.spire_startapp_pkg.reporter import (
|
|
28
|
+
Reporter,
|
|
29
|
+
ReporterInterface,
|
|
30
|
+
)
|
|
31
|
+
from django_spire.core.management.commands.spire_startapp_pkg.resolver import (
|
|
32
|
+
PathResolver,
|
|
33
|
+
PathResolverInterface,
|
|
34
|
+
)
|
|
35
|
+
from django_spire.core.management.commands.spire_startapp_pkg.user_input import UserInputCollector
|
|
36
|
+
from django_spire.core.management.commands.spire_startapp_pkg.validator import AppValidator
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
'AppConfig',
|
|
41
|
+
'AppConfigFactory',
|
|
42
|
+
'AppGenerator',
|
|
43
|
+
'AppRegistry',
|
|
44
|
+
'AppRegistryInterface',
|
|
45
|
+
'AppValidator',
|
|
46
|
+
'FileSystem',
|
|
47
|
+
'FileSystemInterface',
|
|
48
|
+
'PathConfig',
|
|
49
|
+
'PathResolver',
|
|
50
|
+
'PathResolverInterface',
|
|
51
|
+
'PermissionInheritanceHandler',
|
|
52
|
+
'Reporter',
|
|
53
|
+
'ReporterInterface',
|
|
54
|
+
'TemplateBuilder',
|
|
55
|
+
'TemplateEngine',
|
|
56
|
+
'TemplateGenerator',
|
|
57
|
+
'TemplateProcessor',
|
|
58
|
+
'UserInputCollector',
|
|
59
|
+
'generate_replacement_map',
|
|
60
|
+
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from django_spire.core.management.commands.spire_startapp_pkg.maps import generate_replacement_map
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from django_spire.core.management.commands.spire_startapp_pkg.reporter import ReporterInterface
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TemplateBuilder:
|
|
14
|
+
"""
|
|
15
|
+
Builds and displays tree structures for app and template creation.
|
|
16
|
+
|
|
17
|
+
This class generates visual representations of the file structure
|
|
18
|
+
that will be created for new apps and their associated templates.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, reporter: ReporterInterface):
|
|
22
|
+
"""
|
|
23
|
+
Initializes the TemplateBuilder with a reporter for output.
|
|
24
|
+
|
|
25
|
+
:param reporter: Reporter instance for displaying output to the user.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
self._reporter = reporter
|
|
29
|
+
|
|
30
|
+
def build_app_tree_structure(
|
|
31
|
+
self,
|
|
32
|
+
base: Path,
|
|
33
|
+
components: list[str],
|
|
34
|
+
registry: list[str],
|
|
35
|
+
template: Path
|
|
36
|
+
) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Displays a tree structure of the app files that will be created.
|
|
39
|
+
|
|
40
|
+
This method shows the user what Python files, directories, and modules
|
|
41
|
+
will be generated for the new Django app before creation.
|
|
42
|
+
|
|
43
|
+
:param base: Base directory where the app will be created.
|
|
44
|
+
:param components: List of app path components (e.g., ['app', 'human_resource', 'employee']).
|
|
45
|
+
:param registry: List of already registered apps in the Django project.
|
|
46
|
+
:param template: Path to the app template directory.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
self._reporter.report_tree_structure(
|
|
50
|
+
title='\nThe following app(s) will be created:\n\n',
|
|
51
|
+
base=base,
|
|
52
|
+
components=components,
|
|
53
|
+
registry=registry,
|
|
54
|
+
template=template,
|
|
55
|
+
formatter=self._reporter.format_app_item,
|
|
56
|
+
transformation=self._reporter.transform_app_component,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def build_html_tree_structure(
|
|
60
|
+
self,
|
|
61
|
+
base: Path,
|
|
62
|
+
components: list[str],
|
|
63
|
+
registry: list[str],
|
|
64
|
+
template: Path
|
|
65
|
+
) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Displays a tree structure of the HTML template files that will be created.
|
|
68
|
+
|
|
69
|
+
This method shows the user what HTML templates, cards, forms, and pages
|
|
70
|
+
will be generated for the new Django app before creation.
|
|
71
|
+
|
|
72
|
+
:param base: Base directory where templates will be created.
|
|
73
|
+
:param components: List of app path components.
|
|
74
|
+
:param registry: List of already registered apps in the Django project.
|
|
75
|
+
:param template: Path to the HTML template directory.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
replacement = generate_replacement_map(components)
|
|
79
|
+
|
|
80
|
+
def html_formatter_with_replacement(item: Path) -> str:
|
|
81
|
+
return self._reporter.format_html_item(item, replacement)
|
|
82
|
+
|
|
83
|
+
self._reporter.report_tree_structure(
|
|
84
|
+
title='\nThe following template(s) will be created:\n\n',
|
|
85
|
+
base=base,
|
|
86
|
+
components=components,
|
|
87
|
+
registry=registry,
|
|
88
|
+
template=template,
|
|
89
|
+
formatter=html_formatter_with_replacement,
|
|
90
|
+
transformation=self._reporter.transform_html_component,
|
|
91
|
+
)
|