django-cfg 1.1.82__py3-none-any.whl → 1.2.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 (244) hide show
  1. django_cfg/__init__.py +20 -448
  2. django_cfg/apps/accounts/README.md +3 -3
  3. django_cfg/apps/accounts/admin/__init__.py +0 -2
  4. django_cfg/apps/accounts/admin/activity.py +2 -9
  5. django_cfg/apps/accounts/admin/filters.py +0 -42
  6. django_cfg/apps/accounts/admin/inlines.py +8 -8
  7. django_cfg/apps/accounts/admin/otp.py +5 -5
  8. django_cfg/apps/accounts/admin/registration_source.py +1 -8
  9. django_cfg/apps/accounts/admin/user.py +12 -20
  10. django_cfg/apps/accounts/managers/user_manager.py +2 -129
  11. django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
  12. django_cfg/apps/accounts/models.py +3 -123
  13. django_cfg/apps/accounts/serializers/otp.py +40 -44
  14. django_cfg/apps/accounts/serializers/profile.py +0 -2
  15. django_cfg/apps/accounts/services/otp_service.py +98 -186
  16. django_cfg/apps/accounts/signals.py +25 -15
  17. django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
  18. django_cfg/apps/accounts/views/otp.py +35 -36
  19. django_cfg/apps/agents/README.md +129 -0
  20. django_cfg/apps/agents/__init__.py +68 -0
  21. django_cfg/apps/agents/admin/__init__.py +17 -0
  22. django_cfg/apps/agents/admin/execution_admin.py +460 -0
  23. django_cfg/apps/agents/admin/registry_admin.py +360 -0
  24. django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
  25. django_cfg/apps/agents/apps.py +29 -0
  26. django_cfg/apps/agents/core/__init__.py +20 -0
  27. django_cfg/apps/agents/core/agent.py +281 -0
  28. django_cfg/apps/agents/core/dependencies.py +154 -0
  29. django_cfg/apps/agents/core/exceptions.py +66 -0
  30. django_cfg/apps/agents/core/models.py +106 -0
  31. django_cfg/apps/agents/core/orchestrator.py +391 -0
  32. django_cfg/apps/agents/examples/__init__.py +3 -0
  33. django_cfg/apps/agents/examples/simple_example.py +161 -0
  34. django_cfg/apps/agents/integration/__init__.py +14 -0
  35. django_cfg/apps/agents/integration/middleware.py +80 -0
  36. django_cfg/apps/agents/integration/registry.py +345 -0
  37. django_cfg/apps/agents/integration/signals.py +50 -0
  38. django_cfg/apps/agents/management/__init__.py +3 -0
  39. django_cfg/apps/agents/management/commands/__init__.py +3 -0
  40. django_cfg/apps/agents/management/commands/create_agent.py +365 -0
  41. django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
  42. django_cfg/apps/agents/managers/__init__.py +23 -0
  43. django_cfg/apps/agents/managers/execution.py +236 -0
  44. django_cfg/apps/agents/managers/registry.py +254 -0
  45. django_cfg/apps/agents/managers/toolsets.py +496 -0
  46. django_cfg/apps/agents/migrations/0001_initial.py +286 -0
  47. django_cfg/apps/agents/migrations/__init__.py +5 -0
  48. django_cfg/apps/agents/models/__init__.py +15 -0
  49. django_cfg/apps/agents/models/execution.py +215 -0
  50. django_cfg/apps/agents/models/registry.py +220 -0
  51. django_cfg/apps/agents/models/toolsets.py +305 -0
  52. django_cfg/apps/agents/patterns/__init__.py +24 -0
  53. django_cfg/apps/agents/patterns/content_agents.py +234 -0
  54. django_cfg/apps/agents/toolsets/__init__.py +15 -0
  55. django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
  56. django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
  57. django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
  58. django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
  59. django_cfg/apps/agents/urls.py +46 -0
  60. django_cfg/apps/knowbase/README.md +150 -0
  61. django_cfg/apps/knowbase/__init__.py +27 -0
  62. django_cfg/apps/knowbase/admin/__init__.py +23 -0
  63. django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
  64. django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
  65. django_cfg/apps/knowbase/admin/document_admin.py +650 -0
  66. django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
  67. django_cfg/apps/knowbase/apps.py +81 -0
  68. django_cfg/apps/knowbase/config/README.md +176 -0
  69. django_cfg/apps/knowbase/config/__init__.py +51 -0
  70. django_cfg/apps/knowbase/config/constance_fields.py +186 -0
  71. django_cfg/apps/knowbase/config/constance_settings.py +200 -0
  72. django_cfg/apps/knowbase/config/settings.py +444 -0
  73. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  74. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  75. django_cfg/apps/knowbase/management/__init__.py +0 -0
  76. django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
  77. django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
  78. django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
  79. django_cfg/apps/knowbase/managers/__init__.py +22 -0
  80. django_cfg/apps/knowbase/managers/archive.py +426 -0
  81. django_cfg/apps/knowbase/managers/base.py +32 -0
  82. django_cfg/apps/knowbase/managers/chat.py +141 -0
  83. django_cfg/apps/knowbase/managers/document.py +203 -0
  84. django_cfg/apps/knowbase/managers/external_data.py +471 -0
  85. django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
  86. django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
  87. django_cfg/apps/knowbase/migrations/__init__.py +5 -0
  88. django_cfg/apps/knowbase/mixins/__init__.py +15 -0
  89. django_cfg/apps/knowbase/mixins/config.py +108 -0
  90. django_cfg/apps/knowbase/mixins/creator.py +81 -0
  91. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  92. django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
  93. django_cfg/apps/knowbase/mixins/service.py +362 -0
  94. django_cfg/apps/knowbase/models/__init__.py +41 -0
  95. django_cfg/apps/knowbase/models/archive.py +599 -0
  96. django_cfg/apps/knowbase/models/base.py +58 -0
  97. django_cfg/apps/knowbase/models/chat.py +157 -0
  98. django_cfg/apps/knowbase/models/document.py +267 -0
  99. django_cfg/apps/knowbase/models/external_data.py +376 -0
  100. django_cfg/apps/knowbase/serializers/__init__.py +68 -0
  101. django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
  102. django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
  103. django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
  104. django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
  105. django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
  106. django_cfg/apps/knowbase/services/__init__.py +40 -0
  107. django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
  108. django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
  109. django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
  110. django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
  111. django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
  112. django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
  113. django_cfg/apps/knowbase/services/base.py +53 -0
  114. django_cfg/apps/knowbase/services/chat_service.py +239 -0
  115. django_cfg/apps/knowbase/services/document_service.py +144 -0
  116. django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
  117. django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
  118. django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
  119. django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
  120. django_cfg/apps/knowbase/services/embedding/models.py +229 -0
  121. django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
  122. django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
  123. django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
  124. django_cfg/apps/knowbase/services/search_service.py +293 -0
  125. django_cfg/apps/knowbase/signals/__init__.py +21 -0
  126. django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
  127. django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
  128. django_cfg/apps/knowbase/signals/document_signals.py +143 -0
  129. django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
  130. django_cfg/apps/knowbase/tasks/__init__.py +39 -0
  131. django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
  132. django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
  133. django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
  134. django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
  135. django_cfg/apps/knowbase/urls.py +43 -0
  136. django_cfg/apps/knowbase/utils/__init__.py +12 -0
  137. django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
  138. django_cfg/apps/knowbase/utils/text_processing.py +375 -0
  139. django_cfg/apps/knowbase/utils/validation.py +99 -0
  140. django_cfg/apps/knowbase/views/__init__.py +28 -0
  141. django_cfg/apps/knowbase/views/archive_views.py +469 -0
  142. django_cfg/apps/knowbase/views/base.py +49 -0
  143. django_cfg/apps/knowbase/views/chat_views.py +181 -0
  144. django_cfg/apps/knowbase/views/document_views.py +183 -0
  145. django_cfg/apps/knowbase/views/public_views.py +129 -0
  146. django_cfg/apps/leads/admin.py +70 -0
  147. django_cfg/apps/newsletter/admin.py +234 -0
  148. django_cfg/apps/newsletter/admin_filters.py +124 -0
  149. django_cfg/apps/support/admin.py +196 -0
  150. django_cfg/apps/support/admin_filters.py +71 -0
  151. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  152. django_cfg/apps/urls.py +5 -4
  153. django_cfg/cli/README.md +1 -1
  154. django_cfg/cli/commands/create_project.py +2 -2
  155. django_cfg/cli/commands/info.py +1 -1
  156. django_cfg/config.py +44 -0
  157. django_cfg/core/config.py +29 -82
  158. django_cfg/core/environment.py +1 -1
  159. django_cfg/core/generation.py +19 -107
  160. django_cfg/{integration.py → core/integration.py} +18 -16
  161. django_cfg/core/validation.py +1 -1
  162. django_cfg/management/__init__.py +1 -1
  163. django_cfg/management/commands/__init__.py +1 -1
  164. django_cfg/management/commands/auto_generate.py +482 -0
  165. django_cfg/management/commands/migrator.py +19 -101
  166. django_cfg/management/commands/test_email.py +1 -1
  167. django_cfg/middleware/README.md +0 -158
  168. django_cfg/middleware/__init__.py +0 -2
  169. django_cfg/middleware/user_activity.py +3 -3
  170. django_cfg/models/api.py +145 -0
  171. django_cfg/models/base.py +287 -0
  172. django_cfg/models/cache.py +4 -4
  173. django_cfg/models/constance.py +25 -88
  174. django_cfg/models/database.py +9 -9
  175. django_cfg/models/drf.py +3 -36
  176. django_cfg/models/email.py +163 -0
  177. django_cfg/models/environment.py +276 -0
  178. django_cfg/models/limits.py +1 -1
  179. django_cfg/models/logging.py +366 -0
  180. django_cfg/models/revolution.py +41 -2
  181. django_cfg/models/security.py +125 -0
  182. django_cfg/models/services.py +1 -1
  183. django_cfg/modules/__init__.py +2 -56
  184. django_cfg/modules/base.py +78 -52
  185. django_cfg/modules/django_currency/service.py +2 -2
  186. django_cfg/modules/django_email.py +2 -2
  187. django_cfg/modules/django_health.py +267 -0
  188. django_cfg/modules/django_llm/llm/client.py +79 -17
  189. django_cfg/modules/django_llm/translator/translator.py +2 -2
  190. django_cfg/modules/django_logger.py +2 -2
  191. django_cfg/modules/django_ngrok.py +2 -2
  192. django_cfg/modules/django_tasks.py +68 -3
  193. django_cfg/modules/django_telegram.py +3 -3
  194. django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
  195. django_cfg/modules/django_twilio/service.py +2 -2
  196. django_cfg/modules/django_twilio/simple_service.py +2 -2
  197. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  198. django_cfg/modules/django_unfold/__init__.py +69 -0
  199. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  200. django_cfg/modules/django_unfold/dashboard.py +278 -0
  201. django_cfg/modules/django_unfold/icons/README.md +145 -0
  202. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  203. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  204. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  205. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  206. django_cfg/modules/django_unfold/models/config.py +601 -0
  207. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  208. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  209. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  210. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  211. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  212. django_cfg/modules/django_unfold/utils.py +140 -0
  213. django_cfg/registry/__init__.py +23 -0
  214. django_cfg/registry/core.py +61 -0
  215. django_cfg/registry/exceptions.py +11 -0
  216. django_cfg/registry/modules.py +12 -0
  217. django_cfg/registry/services.py +26 -0
  218. django_cfg/registry/third_party.py +52 -0
  219. django_cfg/routing/__init__.py +19 -0
  220. django_cfg/routing/callbacks.py +198 -0
  221. django_cfg/routing/routers.py +48 -0
  222. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  223. django_cfg/templatetags/__init__.py +0 -0
  224. django_cfg/templatetags/django_cfg.py +33 -0
  225. django_cfg/urls.py +33 -0
  226. django_cfg/utils/path_resolution.py +1 -1
  227. django_cfg/utils/smart_defaults.py +7 -61
  228. django_cfg/utils/toolkit.py +663 -0
  229. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
  230. django_cfg-1.2.0.dist-info/RECORD +441 -0
  231. django_cfg/archive/django_sample.zip +0 -0
  232. django_cfg/models/unfold.py +0 -271
  233. django_cfg/modules/unfold/__init__.py +0 -29
  234. django_cfg/modules/unfold/dashboard.py +0 -318
  235. django_cfg/pyproject.toml +0 -370
  236. django_cfg/routers.py +0 -83
  237. django_cfg-1.1.82.dist-info/RECORD +0 -278
  238. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  239. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  240. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  241. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  242. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  243. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  244. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,486 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Material Icons Generator for Django CFG Unfold.
4
+
5
+ This script automatically downloads the latest Material Icons from Google
6
+ and generates IDE-friendly constants for better autocompletion.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import json
12
+ import requests
13
+ from pathlib import Path
14
+ from typing import Dict, List, Set
15
+ import logging
16
+
17
+ # Setup logging
18
+ logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class MaterialIconsGenerator:
23
+ """Generator for Material Icons constants."""
24
+
25
+ # URLs for different sources
26
+ SOURCES = {
27
+ 'codepoints': 'https://raw.githubusercontent.com/google/material-design-icons/master/font/MaterialIcons-Regular.codepoints',
28
+ 'metadata': 'https://fonts.google.com/metadata/icons',
29
+ 'github_api': 'https://api.github.com/repos/google/material-design-icons/contents/symbols'
30
+ }
31
+
32
+ def __init__(self, output_dir: Path):
33
+ self.output_dir = Path(output_dir)
34
+ self.icons_data: Dict[str, str] = {}
35
+ self.categories: Dict[str, List[str]] = {}
36
+
37
+ def download_codepoints(self) -> Dict[str, str]:
38
+ """Download Material Icons codepoints from GitHub."""
39
+ logger.info("šŸ“„ Downloading Material Icons codepoints...")
40
+
41
+ try:
42
+ response = requests.get(self.SOURCES['codepoints'], timeout=30)
43
+ response.raise_for_status()
44
+
45
+ icons = {}
46
+ for line in response.text.strip().split('\n'):
47
+ if line.strip() and not line.startswith('#'):
48
+ parts = line.split()
49
+ if len(parts) >= 2:
50
+ name = parts[0]
51
+ codepoint = parts[1]
52
+ icons[name] = codepoint
53
+
54
+ logger.info(f"āœ… Downloaded {len(icons)} icons from codepoints")
55
+ return icons
56
+
57
+ except Exception as e:
58
+ logger.error(f"āŒ Failed to download codepoints: {e}")
59
+ return {}
60
+
61
+ def download_metadata(self) -> Dict[str, any]:
62
+ """Download Material Icons metadata from Google Fonts."""
63
+ logger.info("šŸ“„ Downloading Material Icons metadata...")
64
+
65
+ try:
66
+ response = requests.get(self.SOURCES['metadata'], timeout=30)
67
+ response.raise_for_status()
68
+
69
+ # Remove the first line (it's not JSON)
70
+ content = response.text
71
+ if content.startswith(")]}'"):
72
+ content = content[4:]
73
+
74
+ metadata = json.loads(content)
75
+
76
+ # Extract icons from metadata
77
+ icons_metadata = {}
78
+ if 'icons' in metadata:
79
+ for icon in metadata['icons']:
80
+ name = icon.get('name', '')
81
+ if name:
82
+ icons_metadata[name] = {
83
+ 'categories': icon.get('categories', []),
84
+ 'tags': icon.get('tags', []),
85
+ 'version': icon.get('version', 1),
86
+ 'popularity': icon.get('popularity', 0)
87
+ }
88
+
89
+ logger.info(f"āœ… Downloaded metadata for {len(icons_metadata)} icons")
90
+ return icons_metadata
91
+
92
+ except Exception as e:
93
+ logger.warning(f"āš ļø Failed to download metadata: {e}")
94
+ return {}
95
+
96
+ def categorize_icons(self, metadata: Dict[str, any]) -> Dict[str, List[str]]:
97
+ """Categorize icons based on metadata."""
98
+ categories = {
99
+ 'navigation': [],
100
+ 'users': [],
101
+ 'documents': [],
102
+ 'communication': [],
103
+ 'ai_automation': [],
104
+ 'actions': [],
105
+ 'status': [],
106
+ 'media': [],
107
+ 'settings': [],
108
+ 'commerce': [],
109
+ 'travel': [],
110
+ 'social': [],
111
+ 'device': [],
112
+ 'editor': [],
113
+ 'maps': [],
114
+ 'notification': [],
115
+ 'content': [],
116
+ 'hardware': [],
117
+ 'image': [],
118
+ 'av': [],
119
+ 'places': [],
120
+ 'file': [],
121
+ 'toggle': [],
122
+ }
123
+
124
+ # Keywords for categorization
125
+ category_keywords = {
126
+ 'navigation': ['dashboard', 'menu', 'home', 'apps', 'navigate', 'arrow', 'chevron', 'expand', 'more'],
127
+ 'users': ['people', 'person', 'group', 'account', 'face', 'user', 'profile'],
128
+ 'documents': ['description', 'text', 'article', 'note', 'folder', 'file', 'document', 'page'],
129
+ 'communication': ['chat', 'message', 'email', 'mail', 'forum', 'comment', 'call', 'phone'],
130
+ 'ai_automation': ['smart', 'auto', 'sync', 'refresh', 'repeat', 'psychology', 'memory', 'robot'],
131
+ 'actions': ['play', 'pause', 'stop', 'add', 'remove', 'edit', 'delete', 'save', 'cancel', 'done'],
132
+ 'status': ['check', 'error', 'warning', 'info', 'pending', 'success', 'failed'],
133
+ 'media': ['video', 'audio', 'music', 'photo', 'image', 'camera', 'mic', 'volume'],
134
+ 'settings': ['settings', 'tune', 'build', 'construction', 'gear', 'config'],
135
+ 'commerce': ['shopping', 'cart', 'store', 'payment', 'money', 'price', 'sell'],
136
+ 'travel': ['flight', 'hotel', 'car', 'train', 'directions', 'map', 'location'],
137
+ 'social': ['share', 'favorite', 'like', 'star', 'bookmark', 'follow'],
138
+ 'device': ['phone', 'tablet', 'laptop', 'desktop', 'watch', 'tv', 'speaker'],
139
+ 'editor': ['format', 'text', 'font', 'color', 'align', 'indent', 'bold', 'italic'],
140
+ 'maps': ['map', 'location', 'place', 'pin', 'navigation', 'gps'],
141
+ 'notification': ['notification', 'alert', 'bell', 'announce'],
142
+ 'content': ['content', 'copy', 'paste', 'cut', 'select', 'clipboard'],
143
+ 'hardware': ['memory', 'storage', 'battery', 'wifi', 'bluetooth', 'usb'],
144
+ 'image': ['image', 'photo', 'picture', 'crop', 'filter', 'camera'],
145
+ 'av': ['play', 'pause', 'stop', 'volume', 'music', 'video', 'audio'],
146
+ 'places': ['home', 'work', 'school', 'hospital', 'restaurant', 'hotel'],
147
+ 'file': ['folder', 'file', 'upload', 'download', 'attach', 'archive'],
148
+ 'toggle': ['toggle', 'switch', 'radio', 'checkbox', 'on', 'off'],
149
+ }
150
+
151
+ for icon_name in self.icons_data.keys():
152
+ # Use metadata categories if available
153
+ if icon_name in metadata and metadata[icon_name].get('categories'):
154
+ for cat in metadata[icon_name]['categories']:
155
+ cat_key = cat.lower().replace(' ', '_')
156
+ if cat_key in categories:
157
+ categories[cat_key].append(icon_name)
158
+ continue
159
+
160
+ # Fallback to keyword matching
161
+ categorized = False
162
+ for category, keywords in category_keywords.items():
163
+ if any(keyword in icon_name.lower() for keyword in keywords):
164
+ categories[category].append(icon_name)
165
+ categorized = True
166
+ break
167
+
168
+ # Default category for uncategorized icons
169
+ if not categorized:
170
+ categories.setdefault('other', []).append(icon_name)
171
+
172
+ # Remove empty categories and sort icons
173
+ return {k: sorted(v) for k, v in categories.items() if v}
174
+
175
+ def generate_constants_file(self):
176
+ """Generate the constants.py file with all icons."""
177
+ logger.info("šŸ“ Generating constants.py...")
178
+
179
+ # Sort icons alphabetically
180
+ sorted_icons = sorted(self.icons_data.keys())
181
+
182
+ content = '''"""
183
+ Material Icons constants for IDE autocompletion.
184
+
185
+ This file is auto-generated by generate_icons.py script.
186
+ DO NOT EDIT MANUALLY - run the script to update.
187
+
188
+ Generated from Google Material Design Icons.
189
+ Total icons: {total_icons}
190
+ """
191
+
192
+ from typing import Dict, Final
193
+
194
+
195
+ class Icons:
196
+ """
197
+ Material Design Icons constants for IDE autocompletion.
198
+
199
+ Usage:
200
+ from django_cfg.modules.django_unfold.icons import Icons
201
+
202
+ # IDE will provide autocompletion
203
+ icon = Icons.DASHBOARD
204
+ icon = Icons.SETTINGS
205
+ icon = Icons.PEOPLE
206
+ """
207
+
208
+ '''.format(total_icons=len(sorted_icons))
209
+
210
+ # Generate icon constants
211
+ for icon_name in sorted_icons:
212
+ # Convert to valid Python identifier
213
+ const_name = icon_name.upper().replace('-', '_')
214
+ # Prefix with underscore if starts with digit
215
+ if const_name[0].isdigit():
216
+ const_name = f'_{const_name}'
217
+ content += f' {const_name}: Final[str] = "{icon_name}"\n'
218
+
219
+ # Add common aliases
220
+ content += '''
221
+ # Common aliases for better IDE experience
222
+ USERS = PEOPLE
223
+ USER = PERSON
224
+ DOCUMENTS = DESCRIPTION
225
+ FILES = INSERT_DRIVE_FILE
226
+ FOLDERS = FOLDER
227
+ MESSAGES = MESSAGE
228
+ EMAILS = EMAIL
229
+ TASKS = QUEUE
230
+ AGENTS = SMART_TOY
231
+ AI = SMART_TOY
232
+
233
+
234
+ '''
235
+
236
+ # Generate categories
237
+ content += '''# IDE-friendly icon categories for easy discovery
238
+ class IconCategories:
239
+ """Categorized icon collections for easy discovery."""
240
+
241
+ '''
242
+
243
+ for category, icons in self.categories.items():
244
+ if len(icons) > 0:
245
+ category_name = category.upper()
246
+ content += f' {category_name}: Dict[str, str] = {{\n'
247
+ for icon in icons[:20]: # Limit to first 20 icons per category
248
+ const_name = icon.upper().replace('-', '_')
249
+ # Prefix with underscore if starts with digit
250
+ if const_name[0].isdigit():
251
+ const_name = f'_{const_name}'
252
+ content += f" '{icon}': Icons.{const_name},\n"
253
+ content += ' }\n \n'
254
+
255
+ # Add validation function
256
+ content += '''
257
+
258
+ # Validation function for IDE
259
+ def validate_icon_constant(icon_name: str) -> bool:
260
+ """
261
+ Validate that an icon constant exists.
262
+
263
+ Args:
264
+ icon_name: The icon name to validate
265
+
266
+ Returns:
267
+ True if the icon exists in Material Icons
268
+ """
269
+ from .icons import MaterialIcons
270
+ return MaterialIcons.is_valid_icon(icon_name)
271
+
272
+
273
+ # Export commonly used icons for direct import
274
+ __all__ = [
275
+ 'Icons',
276
+ 'IconCategories',
277
+ 'validate_icon_constant',
278
+ ]
279
+ '''
280
+
281
+ # Write to file
282
+ output_file = self.output_dir / 'constants.py'
283
+ with open(output_file, 'w', encoding='utf-8') as f:
284
+ f.write(content)
285
+
286
+ logger.info(f"āœ… Generated {output_file} with {len(sorted_icons)} icons")
287
+
288
+ def generate_readme(self):
289
+ """Generate README.md for the icons module."""
290
+ logger.info("šŸ“ Generating README.md...")
291
+
292
+ content = f'''# Material Icons for Django CFG Unfold
293
+
294
+ This module provides Material Design Icons integration for Django CFG Unfold admin interface.
295
+
296
+ ## šŸ“Š Statistics
297
+
298
+ - **Total Icons**: {len(self.icons_data)}
299
+ - **Categories**: {len(self.categories)}
300
+ - **Auto-generated**: Yes (via `generate_icons.py`)
301
+
302
+ ## šŸš€ Usage
303
+
304
+ ### Basic Usage
305
+
306
+ ```python
307
+ from django_cfg.modules.django_unfold.icons import Icons
308
+
309
+ # Use in navigation configuration
310
+ navigation_item = {{
311
+ "title": "Dashboard",
312
+ "icon": Icons.DASHBOARD, # IDE autocompletion!
313
+ "link": "/admin/",
314
+ }}
315
+ ```
316
+
317
+ ### Category-based Selection
318
+
319
+ ```python
320
+ from django_cfg.modules.django_unfold.icons import IconCategories
321
+
322
+ # Get all navigation-related icons
323
+ nav_icons = IconCategories.NAVIGATION
324
+
325
+ # Get all user-related icons
326
+ user_icons = IconCategories.USERS
327
+ ```
328
+
329
+ ### Validation
330
+
331
+ ```python
332
+ from django_cfg.modules.django_unfold.icons import validate_icon_constant
333
+
334
+ # Validate icon exists
335
+ is_valid = validate_icon_constant(Icons.DASHBOARD) # True
336
+ is_valid = validate_icon_constant("nonexistent") # False
337
+ ```
338
+
339
+ ## šŸ”„ Updating Icons
340
+
341
+ To update to the latest Material Icons:
342
+
343
+ ```bash
344
+ cd /path/to/django-cfg/src/django_cfg/modules/django_unfold/icons/
345
+ python generate_icons.py
346
+ ```
347
+
348
+ This will:
349
+ 1. Download the latest Material Icons from Google
350
+ 2. Generate new `constants.py` with all icons
351
+ 3. Categorize icons for easy discovery
352
+ 4. Provide IDE-friendly autocompletion
353
+
354
+ ## šŸ“‚ File Structure
355
+
356
+ ```
357
+ icons/
358
+ ā”œā”€ā”€ __init__.py # Main exports
359
+ ā”œā”€ā”€ constants.py # šŸ¤– Auto-generated icon constants
360
+ ā”œā”€ā”€ icons.py # MaterialIcons class & validation
361
+ ā”œā”€ā”€ icon_validator.py # Navigation validation utilities
362
+ ā”œā”€ā”€ generate_icons.py # šŸ”„ Icon generator script
363
+ ā”œā”€ā”€ example_usage.py # Usage examples
364
+ └── README.md # This file
365
+ ```
366
+
367
+ ## šŸŽÆ Available Categories
368
+
369
+ {self._generate_categories_list()}
370
+
371
+ ## šŸ› ļø Development
372
+
373
+ ### Adding New Categories
374
+
375
+ Edit the `category_keywords` in `generate_icons.py`:
376
+
377
+ ```python
378
+ category_keywords = {{
379
+ 'my_category': ['keyword1', 'keyword2', 'keyword3'],
380
+ # ...
381
+ }}
382
+ ```
383
+
384
+ ### Custom Icon Validation
385
+
386
+ ```python
387
+ from django_cfg.modules.django_unfold.icons import MaterialIcons
388
+
389
+ # Check if icon exists
390
+ if MaterialIcons.is_valid_icon('my_icon'):
391
+ print("Icon exists!")
392
+
393
+ # Get suggestions for invalid icons
394
+ suggestions = MaterialIcons.suggest_similar_icons('invalid_icon')
395
+ print(f"Did you mean: {{suggestions}}")
396
+ ```
397
+
398
+ ## šŸ“‹ Icon Guidelines
399
+
400
+ 1. **Use Constants**: Always use `Icons.CONSTANT_NAME` instead of strings
401
+ 2. **Validate**: Use validation functions to check icon existence
402
+ 3. **Categories**: Browse `IconCategories` for organized icon selection
403
+ 4. **Update Regularly**: Run the generator script to get latest icons
404
+
405
+ ## šŸ”— Resources
406
+
407
+ - [Material Design Icons](https://fonts.google.com/icons)
408
+ - [Google Material Icons GitHub](https://github.com/google/material-design-icons)
409
+ - [Django Unfold Documentation](https://unfoldadmin.com/)
410
+
411
+ ---
412
+
413
+ *This file is auto-generated. Last updated: {self._get_current_timestamp()}*
414
+ '''
415
+
416
+ # Write to file
417
+ output_file = self.output_dir / 'README.md'
418
+ with open(output_file, 'w', encoding='utf-8') as f:
419
+ f.write(content)
420
+
421
+ logger.info(f"āœ… Generated {output_file}")
422
+
423
+ def _generate_categories_list(self) -> str:
424
+ """Generate markdown list of categories."""
425
+ lines = []
426
+ for category, icons in self.categories.items():
427
+ icon_count = len(icons)
428
+ example_icons = ', '.join(icons[:5])
429
+ if len(icons) > 5:
430
+ example_icons += f", ... (+{len(icons) - 5} more)"
431
+
432
+ lines.append(f"- **{category.title()}** ({icon_count} icons): {example_icons}")
433
+
434
+ return '\n'.join(lines)
435
+
436
+ def _get_current_timestamp(self) -> str:
437
+ """Get current timestamp for documentation."""
438
+ from datetime import datetime
439
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
440
+
441
+ def run(self):
442
+ """Run the complete icon generation process."""
443
+ logger.info("šŸš€ Starting Material Icons generation...")
444
+
445
+ # Download icons data
446
+ self.icons_data = self.download_codepoints()
447
+ if not self.icons_data:
448
+ logger.error("āŒ Failed to download icons data")
449
+ return False
450
+
451
+ # Download metadata for categorization
452
+ metadata = self.download_metadata()
453
+
454
+ # Categorize icons
455
+ self.categories = self.categorize_icons(metadata)
456
+ logger.info(f"šŸ“‚ Categorized icons into {len(self.categories)} categories")
457
+
458
+ # Generate files
459
+ self.generate_constants_file()
460
+ self.generate_readme()
461
+
462
+ logger.info("šŸŽ‰ Icon generation completed successfully!")
463
+ return True
464
+
465
+
466
+ def main():
467
+ """Main entry point."""
468
+ # Get the directory where this script is located
469
+ script_dir = Path(__file__).parent
470
+
471
+ generator = MaterialIconsGenerator(script_dir)
472
+ success = generator.run()
473
+
474
+ if success:
475
+ print("\nāœ… Material Icons updated successfully!")
476
+ print("šŸ“ Files generated:")
477
+ print(f" - {script_dir / 'constants.py'}")
478
+ print(f" - {script_dir / 'README.md'}")
479
+ print("\nšŸ’” Don't forget to commit the changes!")
480
+ else:
481
+ print("\nāŒ Icon generation failed!")
482
+ sys.exit(1)
483
+
484
+
485
+ if __name__ == '__main__':
486
+ main()
@@ -0,0 +1,42 @@
1
+ """
2
+ Unfold Models Package
3
+
4
+ All Pydantic models for Django Unfold admin interface.
5
+ """
6
+
7
+ from .config import UnfoldConfig, UnfoldTheme, UnfoldColors, UnfoldSidebar, UnfoldThemeConfig, UnfoldDashboardConfig
8
+ from .navigation import NavigationItem, NavigationSection, NavigationItemType
9
+ from .dropdown import SiteDropdownItem
10
+ from .dashboard import StatCard, SystemHealthItem, QuickAction, DashboardWidget, DashboardData, ChartDataset, ChartData
11
+ from .tabs import TabConfiguration, TabItem
12
+
13
+ __all__ = [
14
+ # Config models
15
+ 'UnfoldConfig',
16
+ 'UnfoldTheme',
17
+ 'UnfoldColors',
18
+ 'UnfoldSidebar',
19
+ 'UnfoldThemeConfig',
20
+ 'UnfoldDashboardConfig',
21
+
22
+ # Navigation models
23
+ 'NavigationItem',
24
+ 'NavigationSection',
25
+ 'NavigationItemType',
26
+
27
+ # Dropdown models
28
+ 'SiteDropdownItem',
29
+
30
+ # Dashboard models
31
+ 'StatCard',
32
+ 'SystemHealthItem',
33
+ 'QuickAction',
34
+ 'DashboardWidget',
35
+ 'DashboardData',
36
+ 'ChartDataset',
37
+ 'ChartData',
38
+
39
+ # Tab models
40
+ 'TabConfiguration',
41
+ 'TabItem',
42
+ ]