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.
Files changed (170) hide show
  1. django_spire/ai/admin.py +3 -1
  2. django_spire/ai/apps.py +2 -0
  3. django_spire/ai/chat/admin.py +15 -9
  4. django_spire/ai/chat/apps.py +4 -1
  5. django_spire/ai/chat/auth/controller.py +3 -1
  6. django_spire/ai/chat/choices.py +2 -0
  7. django_spire/ai/chat/intelligence/maps/intent_llm_map.py +8 -5
  8. django_spire/ai/chat/intelligence/prompts.py +4 -2
  9. django_spire/ai/chat/intelligence/workflows/chat_workflow.py +27 -28
  10. django_spire/ai/chat/message_intel.py +7 -4
  11. django_spire/ai/chat/models.py +8 -9
  12. django_spire/ai/chat/querysets.py +3 -1
  13. django_spire/ai/chat/responses.py +19 -10
  14. django_spire/ai/chat/tools.py +20 -15
  15. django_spire/ai/chat/urls/message_urls.py +2 -1
  16. django_spire/ai/chat/urls/page_urls.py +1 -0
  17. django_spire/ai/chat/views/message_request_views.py +2 -0
  18. django_spire/ai/chat/views/message_response_views.py +4 -4
  19. django_spire/ai/chat/views/message_views.py +2 -0
  20. django_spire/ai/chat/views/page_views.py +7 -2
  21. django_spire/ai/chat/views/template_views.py +2 -0
  22. django_spire/ai/decorators.py +13 -7
  23. django_spire/ai/mixins.py +4 -2
  24. django_spire/ai/models.py +7 -2
  25. django_spire/ai/prompt/bots.py +14 -32
  26. django_spire/ai/prompt/intel.py +1 -1
  27. django_spire/ai/prompt/prompts.py +7 -1
  28. django_spire/ai/prompt/system/bots.py +42 -75
  29. django_spire/ai/prompt/system/intel.py +5 -4
  30. django_spire/ai/prompt/system/prompts.py +5 -1
  31. django_spire/ai/prompt/system/system_prompt_cli.py +15 -9
  32. django_spire/ai/prompt/tests/test_bots.py +14 -11
  33. django_spire/ai/prompt/text_to_prompt_cli.py +5 -2
  34. django_spire/ai/prompt/tuning/bot_tuning_cli.py +14 -13
  35. django_spire/ai/prompt/tuning/bots.py +68 -116
  36. django_spire/ai/prompt/tuning/intel.py +1 -1
  37. django_spire/ai/prompt/tuning/mixins.py +2 -0
  38. django_spire/ai/prompt/tuning/prompt_tuning_cli.py +8 -8
  39. django_spire/ai/prompt/tuning/prompts.py +4 -2
  40. django_spire/ai/sms/admin.py +3 -1
  41. django_spire/ai/sms/apps.py +2 -0
  42. django_spire/ai/sms/decorators.py +2 -0
  43. django_spire/ai/sms/intel.py +4 -2
  44. django_spire/ai/sms/intelligence/workflows/sms_conversation_workflow.py +8 -8
  45. django_spire/ai/sms/models.py +16 -14
  46. django_spire/ai/sms/querysets.py +4 -1
  47. django_spire/ai/sms/tools.py +18 -16
  48. django_spire/ai/sms/urls.py +1 -1
  49. django_spire/ai/sms/views.py +2 -0
  50. django_spire/ai/tests/test_ai.py +3 -5
  51. django_spire/ai/urls.py +1 -0
  52. django_spire/consts.py +1 -1
  53. django_spire/contrib/seeding/field/django/seeder.py +6 -4
  54. django_spire/contrib/seeding/field/llm.py +1 -2
  55. django_spire/contrib/seeding/intelligence/bots/field_seeding_bots.py +7 -8
  56. django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +15 -16
  57. django_spire/contrib/seeding/intelligence/intel.py +1 -1
  58. django_spire/contrib/seeding/intelligence/prompts/factory.py +5 -7
  59. django_spire/contrib/seeding/intelligence/prompts/foreign_key_selection_prompt.py +3 -5
  60. django_spire/contrib/seeding/intelligence/prompts/generate_django_model_seeder_prompts.py +1 -2
  61. django_spire/contrib/seeding/intelligence/prompts/generic_relationship_selection_prompt.py +3 -5
  62. django_spire/contrib/seeding/intelligence/prompts/hierarchical_selection_prompt.py +1 -1
  63. django_spire/contrib/seeding/intelligence/prompts/model_field_choices_prompt.py +3 -3
  64. django_spire/contrib/seeding/intelligence/prompts/negation_prompt.py +1 -1
  65. django_spire/contrib/seeding/intelligence/prompts/objective_prompt.py +2 -4
  66. django_spire/contrib/seeding/management/commands/seeding.py +5 -2
  67. django_spire/contrib/seeding/model/base.py +12 -10
  68. django_spire/contrib/seeding/model/django/seeder.py +13 -10
  69. django_spire/contrib/seeding/tests/test_seeding.py +1 -1
  70. django_spire/core/management/commands/spire_startapp.py +84 -46
  71. django_spire/core/management/commands/spire_startapp_pkg/__init__.py +60 -0
  72. django_spire/core/management/commands/spire_startapp_pkg/builder.py +91 -0
  73. django_spire/core/management/commands/spire_startapp_pkg/config.py +115 -0
  74. django_spire/core/management/commands/spire_startapp_pkg/filesystem.py +125 -0
  75. django_spire/core/management/commands/spire_startapp_pkg/generator.py +167 -0
  76. django_spire/core/management/commands/spire_startapp_pkg/maps.py +783 -25
  77. django_spire/core/management/commands/spire_startapp_pkg/permissions.py +147 -0
  78. django_spire/core/management/commands/spire_startapp_pkg/processor.py +144 -57
  79. django_spire/core/management/commands/spire_startapp_pkg/registry.py +89 -0
  80. django_spire/core/management/commands/spire_startapp_pkg/reporter.py +245 -108
  81. django_spire/core/management/commands/spire_startapp_pkg/resolver.py +86 -0
  82. django_spire/core/management/commands/spire_startapp_pkg/user_input.py +252 -0
  83. django_spire/core/management/commands/spire_startapp_pkg/validator.py +96 -0
  84. django_spire/core/middleware/__init__.py +1 -2
  85. django_spire/knowledge/admin.py +2 -0
  86. django_spire/knowledge/apps.py +2 -0
  87. django_spire/knowledge/auth/controller.py +2 -0
  88. django_spire/knowledge/collection/admin.py +3 -0
  89. django_spire/knowledge/collection/forms.py +2 -0
  90. django_spire/knowledge/collection/models.py +4 -0
  91. django_spire/knowledge/collection/querysets.py +3 -3
  92. django_spire/knowledge/collection/seeding/seed.py +2 -0
  93. django_spire/knowledge/collection/tests/factories.py +2 -0
  94. django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +2 -0
  95. django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +2 -0
  96. django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +2 -0
  97. django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +2 -0
  98. django_spire/knowledge/collection/views/json_views.py +2 -0
  99. django_spire/knowledge/collection/views/page_views.py +2 -0
  100. django_spire/knowledge/context_processors.py +2 -0
  101. django_spire/knowledge/entry/services/tool_service.py +1 -0
  102. django_spire/knowledge/entry/services/transformation_services.py +0 -1
  103. django_spire/knowledge/entry/tests/factories.py +3 -0
  104. django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +2 -0
  105. django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +2 -0
  106. django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +2 -0
  107. django_spire/knowledge/entry/urls/form_urls.py +1 -0
  108. django_spire/knowledge/entry/urls/json_urls.py +1 -0
  109. django_spire/knowledge/entry/urls/page_urls.py +1 -0
  110. django_spire/knowledge/entry/urls/template_urls.py +1 -0
  111. django_spire/knowledge/entry/version/admin.py +2 -0
  112. django_spire/knowledge/entry/version/block/admin.py +2 -0
  113. django_spire/knowledge/entry/version/block/blocks/heading_block.py +2 -0
  114. django_spire/knowledge/entry/version/block/blocks/sub_heading_block.py +2 -0
  115. django_spire/knowledge/entry/version/block/blocks/text_block.py +2 -0
  116. django_spire/knowledge/entry/version/block/maps.py +2 -0
  117. django_spire/knowledge/entry/version/block/models.py +2 -0
  118. django_spire/knowledge/entry/version/block/tests/factories.py +2 -0
  119. django_spire/knowledge/entry/version/block/tests/test_urls/test_json_urls.py +2 -0
  120. django_spire/knowledge/entry/version/block/views/json_views.py +2 -0
  121. django_spire/knowledge/entry/version/intelligence/bots/markdown_format_llm_bot.py +12 -9
  122. django_spire/knowledge/entry/version/maps.py +2 -0
  123. django_spire/knowledge/entry/version/models.py +2 -0
  124. django_spire/knowledge/entry/version/querysets.py +2 -0
  125. django_spire/knowledge/entry/version/seeding/seeder.py +1 -0
  126. django_spire/knowledge/entry/version/tests/factories.py +2 -0
  127. django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +3 -0
  128. django_spire/knowledge/entry/version/tests/test_urls/test_form_urls.py +2 -0
  129. django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +2 -0
  130. django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +2 -0
  131. django_spire/knowledge/entry/version/urls/form_urls.py +1 -0
  132. django_spire/knowledge/entry/version/urls/json_urls.py +1 -0
  133. django_spire/knowledge/entry/version/urls/page_urls.py +1 -0
  134. django_spire/knowledge/entry/version/urls/redirect_urls.py +1 -0
  135. django_spire/knowledge/entry/version/views/form_views.py +2 -0
  136. django_spire/knowledge/entry/version/views/json_views.py +2 -0
  137. django_spire/knowledge/entry/version/views/page_views.py +2 -0
  138. django_spire/knowledge/entry/version/views/redirect_views.py +2 -0
  139. django_spire/knowledge/exceptions.py +2 -0
  140. django_spire/knowledge/intelligence/bots/entry_search_llm_bot.py +5 -16
  141. django_spire/knowledge/intelligence/intel/collection_intel.py +3 -1
  142. django_spire/knowledge/intelligence/intel/entry_intel.py +3 -3
  143. django_spire/knowledge/intelligence/intel/message_intel.py +2 -0
  144. django_spire/knowledge/intelligence/maps/collection_map.py +9 -10
  145. django_spire/knowledge/intelligence/maps/entry_map.py +8 -9
  146. django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +8 -6
  147. django_spire/knowledge/models.py +2 -0
  148. django_spire/knowledge/seeding/seed.py +2 -0
  149. django_spire/knowledge/templatetags/spire_knowledge_tags.py +3 -0
  150. django_spire/knowledge/urls/__init__.py +1 -0
  151. django_spire/profiling/__init__.py +13 -0
  152. django_spire/profiling/middleware/__init__.py +6 -0
  153. django_spire/{core → profiling}/middleware/profiling.py +63 -58
  154. django_spire/profiling/panel.py +345 -0
  155. django_spire/profiling/templates/panel.html +166 -0
  156. {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/METADATA +5 -4
  157. {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/RECORD +160 -157
  158. django_spire/core/management/commands/spire_startapp_pkg/constants.py +0 -4
  159. django_spire/core/management/commands/spire_startapp_pkg/manager.py +0 -176
  160. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/spirechildapp_detail_card.html +0 -24
  161. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/spirechildapp_form_card.html +0 -9
  162. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/spirechildapp_list_card.html +0 -18
  163. django_spire/core/management/commands/spire_startapp_pkg/template/templates/form/spirechildapp_form.html +0 -22
  164. django_spire/core/management/commands/spire_startapp_pkg/template/templates/item/spirechildapp_item.html +0 -24
  165. django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/spirechildapp_detail_page.html +0 -13
  166. django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/spirechildapp_form_page.html +0 -13
  167. django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/spirechildapp_list_page.html +0 -9
  168. {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/WHEEL +0 -0
  169. {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/licenses/LICENSE.md +0 -0
  170. {django_spire-0.16.12.dist-info → django_spire-0.17.0.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,786 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from dataclasses import asdict, dataclass
3
4
 
4
- def generate_replacement_map(components: list[str]) -> dict[str, str]:
5
- app_name = components[-1]
6
- model_name = ''.join(word.title() for word in app_name.split('_'))
7
- if len(components)> 1:
8
- parents = components[0:-1]
9
- parent = components[-2]
10
- else:
11
- parents = [app_name,]
12
- parent = app_name
13
- module = '.'.join(components)
14
-
15
- # The sequence is important. Plurals must be replaced first;
16
- # otherwise, it will generate _plural variables and names.
17
- return {
18
- 'module': module,
19
- 'spire_model_name_plural': model_name + 's',
20
- 'spire_model_name': model_name,
21
- 'spire_app_name_plural': app_name.lower() + 's',
22
- 'spire_app_name': app_name.lower(),
23
- 'spire_parent_app': parent.lower(),
24
- 'spire_reverse_parent_path': ':'.join(parents[1:]).lower(),
25
- 'spire_reverse_path': ':'.join(parents[1:]).lower() + ':' + app_name.lower(),
26
- 'spire_label_path': '_'.join(parents[1:]).lower() + '_' + app_name.lower(),
27
- 'spire_template_path': '/'.join(parents[1:]).lower() + '/' + app_name.lower(),
28
- }
5
+
6
+ @dataclass
7
+ class AppConfiguration:
8
+ """
9
+ Django app configuration and labeling.
10
+
11
+ Example:
12
+ app_config_class_name: 'EmployeeSkillConfig'
13
+ app_name_component: 'skill'
14
+ django_label: 'employee_skill'
15
+ permission_prefix: 'employee_skill'
16
+ db_table_name: 'employee_skill'
17
+
18
+ """
19
+
20
+ app_config_class_name: str
21
+ app_name_component: str
22
+ db_table_name: str
23
+ django_label: str
24
+ permission_prefix: str
25
+
26
+ @classmethod
27
+ def build(
28
+ cls,
29
+ components: list[str],
30
+ user_inputs: dict[str, str] | None = None
31
+ ) -> AppConfiguration:
32
+ parent_parts = components[1:-1] if len(components) > 1 else []
33
+
34
+ app_name = (
35
+ user_inputs.get('app_name', components[-1])
36
+ if user_inputs
37
+ else components[-1]
38
+ )
39
+
40
+ default_model_name = ''.join(
41
+ word.title()
42
+ for word in app_name.split('_')
43
+ )
44
+
45
+ model_name = (
46
+ user_inputs.get('model_name', default_model_name)
47
+ if user_inputs
48
+ else default_model_name
49
+ )
50
+
51
+ default_label = (
52
+ '_'.join(parent_parts).lower() + '_' + app_name.lower()
53
+ if parent_parts
54
+ else app_name.lower()
55
+ )
56
+
57
+ app_label = (
58
+ user_inputs.get('app_label', default_label)
59
+ if user_inputs
60
+ else default_label
61
+ )
62
+
63
+ db_table = (
64
+ user_inputs.get('db_table_name', default_label)
65
+ if user_inputs
66
+ else default_label
67
+ )
68
+
69
+ inherit_permissions = (
70
+ user_inputs.get('inherit_permissions', False)
71
+ if user_inputs
72
+ else False
73
+ )
74
+
75
+ if inherit_permissions and user_inputs:
76
+ permission_prefix = user_inputs.get('parent_permission_prefix', app_label)
77
+ else:
78
+ permission_prefix = app_label
79
+
80
+ return cls(
81
+ app_config_class_name=model_name + 'Config',
82
+ app_name_component=app_name,
83
+ db_table_name=db_table,
84
+ django_label=app_label,
85
+ permission_prefix=permission_prefix,
86
+ )
87
+
88
+
89
+ @dataclass
90
+ class ContextVariables:
91
+ """
92
+ Template context variable names and django_glue keys.
93
+
94
+ Example:
95
+ glue_model_key: 'employee_skill'
96
+ context_single_var: 'employee_skill'
97
+ context_plural_var: 'employee_skills'
98
+
99
+ """
100
+
101
+ context_plural_var: str
102
+ context_single_var: str
103
+ glue_model_key: str
104
+
105
+ @classmethod
106
+ def build(
107
+ cls,
108
+ components: list[str],
109
+ user_inputs: dict[str, str] | None = None
110
+ ) -> ContextVariables:
111
+ app_name = (
112
+ user_inputs.get('app_name', components[-1])
113
+ if user_inputs
114
+ else components[-1]
115
+ )
116
+
117
+ return cls(
118
+ context_plural_var=app_name.lower() + 's',
119
+ context_single_var=app_name.lower(),
120
+ glue_model_key=app_name.lower(),
121
+ )
122
+
123
+
124
+ @dataclass
125
+ class DataClasses:
126
+ """
127
+ Data layer class names (seeders, querysets, forms).
128
+
129
+ Example:
130
+ seeder_class_name: 'EmployeeSkillSeeder'
131
+ queryset_class_name: 'EmployeeSkillQuerySet'
132
+ form_class_name: 'EmployeeSkillForm'
133
+
134
+ """
135
+
136
+ form_class_name: str
137
+ queryset_class_name: str
138
+ seeder_class_name: str
139
+
140
+ @classmethod
141
+ def build(
142
+ cls,
143
+ components: list[str],
144
+ user_inputs: dict[str, str] | None = None
145
+ ) -> DataClasses:
146
+ app_name = (
147
+ user_inputs.get('app_name', components[-1])
148
+ if user_inputs
149
+ else components[-1]
150
+ )
151
+
152
+ default_model_name = ''.join(
153
+ word.title()
154
+ for word in app_name.split('_')
155
+ )
156
+
157
+ model_name = (
158
+ user_inputs.get('model_name', default_model_name)
159
+ if user_inputs
160
+ else default_model_name
161
+ )
162
+
163
+ return cls(
164
+ form_class_name=model_name + 'Form',
165
+ queryset_class_name=model_name + 'QuerySet',
166
+ seeder_class_name=model_name + 'Seeder',
167
+ )
168
+
169
+
170
+ @dataclass
171
+ class IntelligenceClasses:
172
+ """
173
+ LLM/AI related class names.
174
+
175
+ Example:
176
+ bot_class_name: 'EmployeeSkillBot'
177
+ intel_class_name: 'EmployeeSkillIntel'
178
+
179
+ """
180
+
181
+ bot_class_name: str
182
+ intel_class_name: str
183
+
184
+ @classmethod
185
+ def build(
186
+ cls,
187
+ components: list[str],
188
+ user_inputs: dict[str, str] | None = None
189
+ ) -> IntelligenceClasses:
190
+ app_name = (
191
+ user_inputs.get('app_name', components[-1])
192
+ if user_inputs
193
+ else components[-1]
194
+ )
195
+
196
+ default_model_name = ''.join(
197
+ word.title()
198
+ for word in app_name.split('_')
199
+ )
200
+
201
+ model_name = (
202
+ user_inputs.get('model_name', default_model_name)
203
+ if user_inputs
204
+ else default_model_name
205
+ )
206
+
207
+ return cls(
208
+ bot_class_name=model_name + 'Bot',
209
+ intel_class_name=model_name + 'Intel',
210
+ )
211
+
212
+
213
+ @dataclass
214
+ class ModelNames:
215
+ """
216
+ Model class and instance naming conventions.
217
+
218
+ Example:
219
+ model_class_name: 'EmployeeSkill'
220
+ model_class_name_plural: 'EmployeeSkills'
221
+ model_instance_name: 'employee_skill'
222
+ model_instance_name_plural: 'employee_skills'
223
+ model_verbose_name: 'Employee Skill'
224
+ model_verbose_name_plural: 'Employee Skills'
225
+
226
+ """
227
+
228
+ model_class_name: str
229
+ model_class_name_plural: str
230
+ model_instance_name: str
231
+ model_instance_name_plural: str
232
+ model_verbose_name: str
233
+ model_verbose_name_plural: str
234
+
235
+ @classmethod
236
+ def build(
237
+ cls,
238
+ components: list[str],
239
+ user_inputs: dict[str, str] | None = None
240
+ ) -> ModelNames:
241
+ app_name = (
242
+ user_inputs.get('app_name', components[-1])
243
+ if user_inputs
244
+ else components[-1]
245
+ )
246
+
247
+ default_model_name = ''.join(
248
+ word.title()
249
+ for word in app_name.split('_')
250
+ )
251
+
252
+ model_name = (
253
+ user_inputs.get('model_name', default_model_name)
254
+ if user_inputs
255
+ else default_model_name
256
+ )
257
+
258
+ model_name_plural = (
259
+ user_inputs.get('model_name_plural', model_name + 's')
260
+ if user_inputs
261
+ else model_name + 's'
262
+ )
263
+
264
+ default_verbose_name = ' '.join(
265
+ word.title()
266
+ for word in app_name.split('_')
267
+ )
268
+
269
+ verbose_name = (
270
+ user_inputs.get('verbose_name', default_verbose_name)
271
+ if user_inputs
272
+ else default_verbose_name
273
+ )
274
+
275
+ verbose_name_plural = (
276
+ user_inputs.get('verbose_name_plural', verbose_name + 's')
277
+ if user_inputs
278
+ else verbose_name + 's'
279
+ )
280
+
281
+ inherit_permissions = (
282
+ user_inputs.get('inherit_permissions', False)
283
+ if user_inputs
284
+ else False
285
+ )
286
+
287
+ if inherit_permissions and user_inputs:
288
+ model_instance_name = user_inputs.get('parent_model_instance_name', app_name.lower())
289
+ model_instance_name_plural = model_instance_name + 's'
290
+ else:
291
+ model_instance_name = app_name.lower()
292
+ model_instance_name_plural = app_name.lower() + 's'
293
+
294
+ return cls(
295
+ model_class_name=model_name,
296
+ model_class_name_plural=model_name_plural,
297
+ model_instance_name=model_instance_name,
298
+ model_instance_name_plural=model_instance_name_plural,
299
+ model_verbose_name=verbose_name,
300
+ model_verbose_name_plural=verbose_name_plural,
301
+ )
302
+
303
+
304
+ @dataclass
305
+ class ModelPermissions:
306
+ """
307
+ MODEL_PERMISSIONS configuration for apps.py.
308
+
309
+ Example:
310
+ permission_name: 'employee_skill'
311
+ model_class_path: 'app.human_resource.employee.skill.models.EmployeeSkill'
312
+ is_proxy_model: False
313
+
314
+ """
315
+
316
+ is_proxy_model: bool
317
+ model_class_path: str
318
+ permission_name: str
319
+
320
+ @classmethod
321
+ def build(
322
+ cls,
323
+ components: list[str],
324
+ user_inputs: dict[str, str] | None = None
325
+ ) -> ModelPermissions:
326
+ module = '.'.join(components)
327
+ parent_parts = components[1:-1] if len(components) > 1 else []
328
+
329
+ app_name = (
330
+ user_inputs.get('app_name', components[-1])
331
+ if user_inputs
332
+ else components[-1]
333
+ )
334
+
335
+ default_model_name = ''.join(
336
+ word.title()
337
+ for word in app_name.split('_')
338
+ )
339
+
340
+ model_name = (
341
+ user_inputs.get('model_name', default_model_name)
342
+ if user_inputs
343
+ else default_model_name
344
+ )
345
+
346
+ default_permission_name = (
347
+ '_'.join(parent_parts).lower() + '_' + app_name.lower()
348
+ if parent_parts
349
+ else app_name.lower()
350
+ )
351
+
352
+ inherit_permissions = (
353
+ user_inputs.get('inherit_permissions', False)
354
+ if user_inputs
355
+ else False
356
+ )
357
+
358
+ if inherit_permissions and user_inputs:
359
+ permission_path = user_inputs.get('parent_model_path', f'{module}.models.{model_name}')
360
+ else:
361
+ permission_path = (
362
+ user_inputs.get('model_permission_path', f'{module}.models.{model_name}')
363
+ if user_inputs
364
+ else f'{module}.models.{model_name}'
365
+ )
366
+
367
+ return cls(
368
+ is_proxy_model=False,
369
+ model_class_path=permission_path,
370
+ permission_name=default_permission_name,
371
+ )
372
+
373
+
374
+ @dataclass
375
+ class ModulePaths:
376
+ """
377
+ Module and path references for Python imports.
378
+
379
+ Example:
380
+ module: 'app.human_resource.employee.skill'
381
+ module_path: 'app.human_resource.employee.skill'
382
+
383
+ """
384
+
385
+ module: str
386
+ module_path: str
387
+
388
+ @classmethod
389
+ def build(cls, components: list[str], **_kwargs) -> ModulePaths:
390
+ module = '.'.join(components)
391
+ return cls(module=module, module_path=module)
392
+
393
+
394
+ @dataclass
395
+ class ParentReferences:
396
+ """
397
+ Parent app and model references.
398
+
399
+ Example:
400
+ parent_app_name: 'employee'
401
+ parent_model_class_name: 'Employee'
402
+
403
+ """
404
+
405
+ parent_app_name: str
406
+ parent_model_class_name: str
407
+
408
+ @classmethod
409
+ def build(cls, components: list[str], **_kwargs) -> ParentReferences:
410
+ parent = components[-2] if len(components) > 1 else components[-1]
411
+
412
+ return cls(
413
+ parent_app_name=parent.lower(),
414
+ parent_model_class_name=''.join(
415
+ word.title()
416
+ for word in parent.split('_')
417
+ ),
418
+ )
419
+
420
+
421
+ @dataclass
422
+ class PromptFunctions:
423
+ """
424
+ LLM prompt function names.
425
+
426
+ Example:
427
+ instruction_prompt_function_name: 'employee_skill_instruction_prompt'
428
+ user_input_prompt_function_name: 'employee_skill_user_input_prompt'
429
+
430
+ """
431
+
432
+ instruction_prompt_function_name: str
433
+ user_input_prompt_function_name: str
434
+
435
+ @classmethod
436
+ def build(
437
+ cls,
438
+ components: list[str],
439
+ user_inputs: dict[str, str] | None = None
440
+ ) -> PromptFunctions:
441
+ app_name = (
442
+ user_inputs.get('app_name', components[-1])
443
+ if user_inputs
444
+ else components[-1]
445
+ )
446
+
447
+ return cls(
448
+ instruction_prompt_function_name=app_name.lower() + '_instruction_prompt',
449
+ user_input_prompt_function_name=app_name.lower() + '_user_input_prompt',
450
+ )
451
+
452
+
453
+ @dataclass
454
+ class ReplacementMapBuilder:
455
+ """Aggregates all dataclasses and builds the final replacement map."""
456
+
457
+ app_config: AppConfiguration
458
+ context: ContextVariables
459
+ data_classes: DataClasses
460
+ intelligence: IntelligenceClasses
461
+ model_names: ModelNames
462
+ model_permissions: ModelPermissions
463
+ module_paths: ModulePaths
464
+ parent_refs: ParentReferences
465
+ prompts: PromptFunctions
466
+ services: ServiceClasses
467
+ templates: TemplatePaths
468
+ tests: TestClasses
469
+ urls: URLPatterns
470
+ views: ViewFunctions
471
+
472
+ @classmethod
473
+ def build(
474
+ cls,
475
+ components: list[str],
476
+ user_inputs: dict[str, str] | None = None
477
+ ) -> ReplacementMapBuilder:
478
+ return cls(
479
+ app_config=AppConfiguration.build(components, user_inputs=user_inputs),
480
+ context=ContextVariables.build(components, user_inputs=user_inputs),
481
+ data_classes=DataClasses.build(components, user_inputs=user_inputs),
482
+ intelligence=IntelligenceClasses.build(components, user_inputs=user_inputs),
483
+ model_names=ModelNames.build(components, user_inputs=user_inputs),
484
+ model_permissions=ModelPermissions.build(components, user_inputs=user_inputs),
485
+ module_paths=ModulePaths.build(components, user_inputs=user_inputs),
486
+ parent_refs=ParentReferences.build(components, user_inputs=user_inputs),
487
+ prompts=PromptFunctions.build(components, user_inputs=user_inputs),
488
+ services=ServiceClasses.build(components, user_inputs=user_inputs),
489
+ templates=TemplatePaths.build(components, user_inputs=user_inputs),
490
+ tests=TestClasses.build(components, user_inputs=user_inputs),
491
+ urls=URLPatterns.build(components, user_inputs=user_inputs),
492
+ views=ViewFunctions.build(components, user_inputs=user_inputs),
493
+ )
494
+
495
+ def to_dict(self) -> dict[str, str]:
496
+ """Convert the builder to a flat dictionary for template replacement."""
497
+
498
+ return {
499
+ **asdict(self.app_config),
500
+ **asdict(self.context),
501
+ **asdict(self.data_classes),
502
+ **asdict(self.intelligence),
503
+ **asdict(self.model_names),
504
+ **asdict(self.model_permissions),
505
+ **asdict(self.module_paths),
506
+ **asdict(self.parent_refs),
507
+ **asdict(self.prompts),
508
+ **asdict(self.services),
509
+ **asdict(self.templates),
510
+ **asdict(self.tests),
511
+ **asdict(self.urls),
512
+ **asdict(self.views),
513
+ }
514
+
515
+
516
+ @dataclass
517
+ class ServiceClasses:
518
+ """
519
+ Service layer class names.
520
+
521
+ Example:
522
+ service_class_name: 'EmployeeSkillService'
523
+ factory_service_class_name: 'EmployeeSkillFactoryService'
524
+ intelligence_service_class_name: 'EmployeeSkillIntelligenceService'
525
+ processor_service_class_name: 'EmployeeSkillProcessorService'
526
+ transformation_service_class_name: 'EmployeeSkillTransformationService'
527
+
528
+ """
529
+
530
+ factory_service_class_name: str
531
+ intelligence_service_class_name: str
532
+ processor_service_class_name: str
533
+ service_class_name: str
534
+ transformation_service_class_name: str
535
+
536
+ @classmethod
537
+ def build(
538
+ cls,
539
+ components: list[str],
540
+ user_inputs: dict[str, str] | None = None
541
+ ) -> ServiceClasses:
542
+ app_name = (
543
+ user_inputs.get('app_name', components[-1])
544
+ if user_inputs
545
+ else components[-1]
546
+ )
547
+
548
+ default_model_name = ''.join(
549
+ word.title()
550
+ for word in app_name.split('_')
551
+ )
552
+
553
+ model_name = (
554
+ user_inputs.get('model_name', default_model_name)
555
+ if user_inputs
556
+ else default_model_name
557
+ )
558
+
559
+ return cls(
560
+ factory_service_class_name=model_name + 'FactoryService',
561
+ intelligence_service_class_name=model_name + 'IntelligenceService',
562
+ processor_service_class_name=model_name + 'ProcessorService',
563
+ service_class_name=model_name + 'Service',
564
+ transformation_service_class_name=model_name + 'TransformationService',
565
+ )
566
+
567
+
568
+ @dataclass
569
+ class TemplatePaths:
570
+ """
571
+ Template file paths and names.
572
+
573
+ Example:
574
+ template_directory_path: 'employee/skill'
575
+ detail_card_template_name: 'employee_skill_detail_card.html'
576
+ form_card_template_name: 'employee_skill_form_card.html'
577
+ list_card_template_name: 'employee_skill_list_card.html'
578
+ item_template_name: 'employee_skill_item.html'
579
+ form_template_name: 'employee_skill_form.html'
580
+ detail_page_template_name: 'employee_skill_detail_page.html'
581
+ form_page_template_name: 'employee_skill_form_page.html'
582
+ list_page_template_name: 'employee_skill_list_page.html'
583
+
584
+ """
585
+
586
+ detail_card_template_name: str
587
+ detail_page_template_name: str
588
+ form_card_template_name: str
589
+ form_page_template_name: str
590
+ form_template_name: str
591
+ item_template_name: str
592
+ list_card_template_name: str
593
+ list_page_template_name: str
594
+ template_directory_path: str
595
+
596
+ @classmethod
597
+ def build(
598
+ cls,
599
+ components: list[str],
600
+ user_inputs: dict[str, str] | None = None
601
+ ) -> TemplatePaths:
602
+ app_name = (
603
+ user_inputs.get('app_name', components[-1])
604
+ if user_inputs
605
+ else components[-1]
606
+ )
607
+
608
+ parent_parts = components[1:-1] if len(components) > 1 else []
609
+
610
+ template_path = (
611
+ '/'.join(parent_parts).lower() + '/' + app_name.lower()
612
+ if parent_parts
613
+ else app_name.lower()
614
+ )
615
+
616
+ return cls(
617
+ detail_card_template_name=app_name.lower() + '_detail_card',
618
+ detail_page_template_name=app_name.lower() + '_detail_page',
619
+ form_card_template_name=app_name.lower() + '_form_card',
620
+ form_page_template_name=app_name.lower() + '_form_page',
621
+ form_template_name=app_name.lower() + '_form',
622
+ item_template_name=app_name.lower() + '_item',
623
+ list_card_template_name=app_name.lower() + '_list_card',
624
+ list_page_template_name=app_name.lower() + '_list_page',
625
+ template_directory_path=template_path,
626
+ )
627
+
628
+
629
+ @dataclass
630
+ class TestClasses:
631
+ """
632
+ Test case class names.
633
+
634
+ Example:
635
+ model_test_class_name: 'EmployeeSkillModelTestCase'
636
+ bot_test_class_name: 'EmployeeSkillBotTestCase'
637
+ service_test_class_name: 'EmployeeSkillServiceTestCase'
638
+ factory_service_test_class_name: 'EmployeeSkillFactoryServiceTestCase'
639
+ intelligence_service_test_class_name: 'EmployeeSkillIntelligenceServiceTestCase'
640
+ processor_service_test_class_name: 'EmployeeSkillProcessorServiceTestCase'
641
+ transformation_service_test_class_name: 'EmployeeSkillTransformationServiceTestCase'
642
+ url_test_class_name: 'EmployeeSkillUrlTestCase'
643
+ view_test_class_name: 'EmployeeSkillViewTestCase'
644
+
645
+ """
646
+
647
+ bot_test_class_name: str
648
+ factory_service_test_class_name: str
649
+ intelligence_service_test_class_name: str
650
+ model_test_class_name: str
651
+ processor_service_test_class_name: str
652
+ service_test_class_name: str
653
+ transformation_service_test_class_name: str
654
+ url_test_class_name: str
655
+ view_test_class_name: str
656
+
657
+ @classmethod
658
+ def build(
659
+ cls,
660
+ components: list[str],
661
+ user_inputs: dict[str, str] | None = None
662
+ ) -> TestClasses:
663
+ app_name = (
664
+ user_inputs.get('app_name', components[-1])
665
+ if user_inputs
666
+ else components[-1]
667
+ )
668
+
669
+ default_model_name = ''.join(
670
+ word.title()
671
+ for word in app_name.split('_')
672
+ )
673
+
674
+ model_name = (
675
+ user_inputs.get('model_name', default_model_name)
676
+ if user_inputs
677
+ else default_model_name
678
+ )
679
+
680
+ return cls(
681
+ bot_test_class_name=model_name + 'BotTestCase',
682
+ factory_service_test_class_name=model_name + 'FactoryServiceTestCase',
683
+ intelligence_service_test_class_name=model_name + 'IntelligenceServiceTestCase',
684
+ model_test_class_name=model_name + 'ModelTestCase',
685
+ processor_service_test_class_name=model_name + 'ProcessorServiceTestCase',
686
+ service_test_class_name=model_name + 'ServiceTestCase',
687
+ transformation_service_test_class_name=model_name + 'TransformationServiceTestCase',
688
+ url_test_class_name=model_name + 'UrlTestCase',
689
+ view_test_class_name=model_name + 'ViewTestCase',
690
+ )
691
+
692
+
693
+ @dataclass
694
+ class URLPatterns:
695
+ """
696
+ URL routing and namespaces.
697
+
698
+ Example:
699
+ url_namespace: 'skill'
700
+ url_reverse_path: 'employee:skill'
701
+ url_reverse_parent_path: 'employee'
702
+
703
+ """
704
+
705
+ url_namespace: str
706
+ url_reverse_parent_path: str
707
+ url_reverse_path: str
708
+
709
+ @classmethod
710
+ def build(
711
+ cls,
712
+ components: list[str],
713
+ user_inputs: dict[str, str] | None = None
714
+ ) -> URLPatterns:
715
+ app_name = (
716
+ user_inputs.get('app_name', components[-1])
717
+ if user_inputs
718
+ else components[-1]
719
+ )
720
+
721
+ parent_parts = components[1:-1] if len(components) > 1 else []
722
+
723
+ if parent_parts:
724
+ reverse_parent = ':'.join(parent_parts).lower()
725
+ reverse_path = reverse_parent + ':' + app_name.lower()
726
+ else:
727
+ reverse_parent = ''
728
+ reverse_path = app_name.lower()
729
+
730
+ return cls(
731
+ url_namespace=app_name.lower(),
732
+ url_reverse_parent_path=reverse_parent,
733
+ url_reverse_path=reverse_path,
734
+ )
735
+
736
+
737
+ @dataclass
738
+ class ViewFunctions:
739
+ """
740
+ View function names.
741
+
742
+ Example:
743
+ list_page_view_name: 'list_page_view'
744
+ detail_page_view_name: 'detail_page_view'
745
+ create_form_view_name: 'create_form_view'
746
+ update_form_view_name: 'update_form_view'
747
+ delete_form_view_name: 'delete_form_view'
748
+ create_modal_form_view_name: 'create_modal_form_view'
749
+ update_modal_form_view_name: 'update_modal_form_view'
750
+ delete_modal_form_view_name: 'delete_modal_form_view'
751
+
752
+ """
753
+
754
+ create_form_view_name: str
755
+ create_modal_form_view_name: str
756
+ delete_form_view_name: str
757
+ delete_modal_form_view_name: str
758
+ detail_page_view_name: str
759
+ list_page_view_name: str
760
+ update_form_view_name: str
761
+ update_modal_form_view_name: str
762
+
763
+ @classmethod
764
+ def build(cls, components: list[str], **_kwargs) -> ViewFunctions:
765
+ return cls(
766
+ create_form_view_name='create_form_view',
767
+ create_modal_form_view_name='create_modal_form_view',
768
+ delete_form_view_name='delete_form_view',
769
+ delete_modal_form_view_name='delete_modal_form_view',
770
+ detail_page_view_name='detail_page_view',
771
+ list_page_view_name='list_page_view',
772
+ update_form_view_name='update_form_view',
773
+ update_modal_form_view_name='update_modal_form_view',
774
+ )
775
+
776
+
777
+ def generate_replacement_map(
778
+ components: list[str],
779
+ user_inputs: dict[str, str] | None = None
780
+ ) -> dict[str, str]:
781
+ """Generate replacement mappings for template processing."""
782
+
783
+ builder = ReplacementMapBuilder.build(components, user_inputs)
784
+ replacement_dict = builder.to_dict()
785
+
786
+ return {key: str(value) for key, value in replacement_dict.items()}