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,496 @@
1
+ """
2
+ Custom managers for toolset models.
3
+ """
4
+
5
+ from django.db import models
6
+ from django.utils import timezone
7
+ from typing import Optional, Dict, Any, List
8
+
9
+
10
+ class ToolExecutionQuerySet(models.QuerySet):
11
+ """Custom queryset for ToolExecution."""
12
+
13
+ def for_user(self, user):
14
+ """Filter tool executions for specific user."""
15
+ return self.filter(user=user)
16
+
17
+ def by_status(self, status: str):
18
+ """Filter by execution status."""
19
+ return self.filter(status=status)
20
+
21
+ def by_tool(self, tool_name: str):
22
+ """Filter by tool name."""
23
+ return self.filter(tool_name=tool_name)
24
+
25
+ def by_toolset(self, toolset_name: str):
26
+ """Filter by toolset name."""
27
+ return self.filter(toolset_name=toolset_name)
28
+
29
+ def successful(self):
30
+ """Get successful executions."""
31
+ return self.filter(status='completed')
32
+
33
+ def failed(self):
34
+ """Get failed executions."""
35
+ return self.filter(status='failed')
36
+
37
+ def recent(self, days: int = 7):
38
+ """Get recent executions."""
39
+ from datetime import timedelta
40
+ cutoff = timezone.now() - timedelta(days=days)
41
+ return self.filter(created_at__gte=cutoff)
42
+
43
+ def with_retries(self):
44
+ """Get executions that had retries."""
45
+ return self.filter(retry_count__gt=0)
46
+
47
+ def expensive(self, min_time: float = 1.0):
48
+ """Get time-expensive executions."""
49
+ return self.filter(execution_time__gte=min_time)
50
+
51
+
52
+ class ToolExecutionManager(models.Manager):
53
+ """Custom manager for ToolExecution."""
54
+
55
+ def get_queryset(self):
56
+ return ToolExecutionQuerySet(self.model, using=self._db)
57
+
58
+ def for_user(self, user):
59
+ """Get executions for specific user."""
60
+ return self.get_queryset().for_user(user)
61
+
62
+ def by_status(self, status: str):
63
+ """Get executions by status."""
64
+ return self.get_queryset().by_status(status)
65
+
66
+ def by_tool(self, tool_name: str):
67
+ """Get executions by tool."""
68
+ return self.get_queryset().by_tool(tool_name)
69
+
70
+ def by_toolset(self, toolset_name: str):
71
+ """Get executions by toolset."""
72
+ return self.get_queryset().by_toolset(toolset_name)
73
+
74
+ def successful(self):
75
+ """Get successful executions."""
76
+ return self.get_queryset().successful()
77
+
78
+ def failed(self):
79
+ """Get failed executions."""
80
+ return self.get_queryset().failed()
81
+
82
+ def recent(self, days: int = 7):
83
+ """Get recent executions."""
84
+ return self.get_queryset().recent(days)
85
+
86
+ def with_retries(self):
87
+ """Get executions with retries."""
88
+ return self.get_queryset().with_retries()
89
+
90
+ def expensive(self, min_time: float = 1.0):
91
+ """Get expensive executions."""
92
+ return self.get_queryset().expensive(min_time)
93
+
94
+ def create_execution(
95
+ self,
96
+ tool_name: str,
97
+ toolset_name: str,
98
+ user,
99
+ arguments: Dict,
100
+ agent_execution=None,
101
+ **kwargs
102
+ ):
103
+ """Create a new tool execution."""
104
+ return self.create(
105
+ tool_name=tool_name,
106
+ toolset_name=toolset_name,
107
+ user=user,
108
+ arguments=arguments,
109
+ agent_execution=agent_execution,
110
+ **kwargs
111
+ )
112
+
113
+ def get_stats_for_user(self, user) -> Dict[str, Any]:
114
+ """Get tool execution statistics for user."""
115
+ executions = self.for_user(user)
116
+
117
+ return {
118
+ 'total': executions.count(),
119
+ 'successful': executions.successful().count(),
120
+ 'failed': executions.failed().count(),
121
+ 'recent': executions.recent().count(),
122
+ 'with_retries': executions.with_retries().count(),
123
+ 'avg_execution_time': executions.aggregate(
124
+ avg=models.Avg('execution_time')
125
+ )['avg'],
126
+ 'total_retries': executions.aggregate(
127
+ total=models.Sum('retry_count')
128
+ )['total'] or 0,
129
+ }
130
+
131
+ def get_tool_stats(self) -> Dict[str, Any]:
132
+ """Get overall tool statistics."""
133
+ return {
134
+ 'unique_tools': self.values('tool_name').distinct().count(),
135
+ 'unique_toolsets': self.values('toolset_name').distinct().count(),
136
+ 'total_executions': self.count(),
137
+ 'success_rate': self.successful().count() / max(self.count(), 1) * 100,
138
+ 'avg_execution_time': self.aggregate(
139
+ avg=models.Avg('execution_time')
140
+ )['avg'],
141
+ }
142
+
143
+
144
+ class ApprovalLogQuerySet(models.QuerySet):
145
+ """Custom queryset for ApprovalLog."""
146
+
147
+ def for_user(self, user):
148
+ """Filter approvals for specific user."""
149
+ return self.filter(user=user)
150
+
151
+ def by_status(self, status: str):
152
+ """Filter by approval status."""
153
+ return self.filter(status=status)
154
+
155
+ def by_tool(self, tool_name: str):
156
+ """Filter by tool name."""
157
+ return self.filter(tool_name=tool_name)
158
+
159
+ def pending(self):
160
+ """Get pending approvals."""
161
+ return self.filter(status='pending')
162
+
163
+ def approved(self):
164
+ """Get approved requests."""
165
+ return self.filter(status='approved')
166
+
167
+ def rejected(self):
168
+ """Get rejected requests."""
169
+ return self.filter(status='rejected')
170
+
171
+ def expired(self):
172
+ """Get expired requests."""
173
+ return self.filter(status='expired')
174
+
175
+ def recent(self, days: int = 7):
176
+ """Get recent requests."""
177
+ from datetime import timedelta
178
+ cutoff = timezone.now() - timedelta(days=days)
179
+ return self.filter(requested_at__gte=cutoff)
180
+
181
+ def decided_by(self, user):
182
+ """Get requests decided by specific user."""
183
+ return self.filter(
184
+ models.Q(approved_by=user) | models.Q(rejected_by=user)
185
+ )
186
+
187
+ def fast_decisions(self, max_seconds: int = 60):
188
+ """Get quickly decided requests."""
189
+ return self.filter(time_to_decision__lte=max_seconds)
190
+
191
+
192
+ class ApprovalLogManager(models.Manager):
193
+ """Custom manager for ApprovalLog."""
194
+
195
+ def get_queryset(self):
196
+ return ApprovalLogQuerySet(self.model, using=self._db)
197
+
198
+ def for_user(self, user):
199
+ """Get approvals for specific user."""
200
+ return self.get_queryset().for_user(user)
201
+
202
+ def by_status(self, status: str):
203
+ """Get approvals by status."""
204
+ return self.get_queryset().by_status(status)
205
+
206
+ def by_tool(self, tool_name: str):
207
+ """Get approvals by tool."""
208
+ return self.get_queryset().by_tool(tool_name)
209
+
210
+ def pending(self):
211
+ """Get pending approvals."""
212
+ return self.get_queryset().pending()
213
+
214
+ def approved(self):
215
+ """Get approved requests."""
216
+ return self.get_queryset().approved()
217
+
218
+ def rejected(self):
219
+ """Get rejected requests."""
220
+ return self.get_queryset().rejected()
221
+
222
+ def expired(self):
223
+ """Get expired requests."""
224
+ return self.get_queryset().expired()
225
+
226
+ def recent(self, days: int = 7):
227
+ """Get recent requests."""
228
+ return self.get_queryset().recent(days)
229
+
230
+ def decided_by(self, user):
231
+ """Get requests decided by user."""
232
+ return self.get_queryset().decided_by(user)
233
+
234
+ def fast_decisions(self, max_seconds: int = 60):
235
+ """Get fast decisions."""
236
+ return self.get_queryset().fast_decisions(max_seconds)
237
+
238
+ def create_approval_request(
239
+ self,
240
+ approval_id: str,
241
+ tool_name: str,
242
+ user,
243
+ tool_args: Dict,
244
+ justification: str = "",
245
+ expires_at=None,
246
+ **kwargs
247
+ ):
248
+ """Create a new approval request."""
249
+ if expires_at is None:
250
+ from datetime import timedelta
251
+ expires_at = timezone.now() + timedelta(hours=24)
252
+
253
+ return self.create(
254
+ approval_id=approval_id,
255
+ tool_name=tool_name,
256
+ user=user,
257
+ tool_args=tool_args,
258
+ justification=justification,
259
+ expires_at=expires_at,
260
+ **kwargs
261
+ )
262
+
263
+ def get_stats_for_user(self, user) -> Dict[str, Any]:
264
+ """Get approval statistics for user."""
265
+ requests = self.for_user(user)
266
+
267
+ return {
268
+ 'total': requests.count(),
269
+ 'pending': requests.pending().count(),
270
+ 'approved': requests.approved().count(),
271
+ 'rejected': requests.rejected().count(),
272
+ 'expired': requests.expired().count(),
273
+ 'recent': requests.recent().count(),
274
+ 'approval_rate': requests.approved().count() / max(requests.count(), 1) * 100,
275
+ 'avg_decision_time': requests.exclude(
276
+ time_to_decision__isnull=True
277
+ ).aggregate(
278
+ avg=models.Avg('time_to_decision')
279
+ )['avg'],
280
+ }
281
+
282
+
283
+ class ToolsetConfigurationQuerySet(models.QuerySet):
284
+ """Custom queryset for ToolsetConfiguration."""
285
+
286
+ def active(self):
287
+ """Get active toolset configurations."""
288
+ return self.filter(is_active=True)
289
+
290
+ def for_user(self, user):
291
+ """Get toolsets available for specific user."""
292
+ return self.filter(
293
+ models.Q(allowed_users=user) |
294
+ models.Q(allowed_groups__in=user.groups.all()) |
295
+ models.Q(created_by=user)
296
+ ).distinct()
297
+
298
+ def by_creator(self, user):
299
+ """Get toolsets created by specific user."""
300
+ return self.filter(created_by=user)
301
+
302
+ def by_class(self, toolset_class: str):
303
+ """Filter by toolset class."""
304
+ return self.filter(toolset_class=toolset_class)
305
+
306
+ def recent(self, days: int = 7):
307
+ """Get recently created toolsets."""
308
+ from datetime import timedelta
309
+ cutoff = timezone.now() - timedelta(days=days)
310
+ return self.filter(created_at__gte=cutoff)
311
+
312
+ def search(self, query: str):
313
+ """Search toolsets by name or description."""
314
+ return self.filter(
315
+ models.Q(name__icontains=query) |
316
+ models.Q(description__icontains=query) |
317
+ models.Q(toolset_class__icontains=query)
318
+ )
319
+
320
+
321
+ class ToolsetConfigurationManager(models.Manager):
322
+ """Custom manager for ToolsetConfiguration."""
323
+
324
+ def get_queryset(self):
325
+ return ToolsetConfigurationQuerySet(self.model, using=self._db)
326
+
327
+ def active(self):
328
+ """Get active toolsets."""
329
+ return self.get_queryset().active()
330
+
331
+ def for_user(self, user):
332
+ """Get toolsets for user."""
333
+ return self.get_queryset().for_user(user)
334
+
335
+ def by_creator(self, user):
336
+ """Get toolsets by creator."""
337
+ return self.get_queryset().by_creator(user)
338
+
339
+ def by_class(self, toolset_class: str):
340
+ """Get toolsets by class."""
341
+ return self.get_queryset().by_class(toolset_class)
342
+
343
+ def recent(self, days: int = 7):
344
+ """Get recent toolsets."""
345
+ return self.get_queryset().recent(days)
346
+
347
+ def search(self, query: str):
348
+ """Search toolsets."""
349
+ return self.get_queryset().search(query)
350
+
351
+ def create_toolset(
352
+ self,
353
+ name: str,
354
+ toolset_class: str,
355
+ created_by,
356
+ description: str = "",
357
+ config: Optional[Dict] = None,
358
+ **kwargs
359
+ ):
360
+ """Create a new toolset configuration."""
361
+ return self.create(
362
+ name=name,
363
+ toolset_class=toolset_class,
364
+ description=description,
365
+ config=config or {},
366
+ created_by=created_by,
367
+ **kwargs
368
+ )
369
+
370
+ def get_stats(self) -> Dict[str, Any]:
371
+ """Get toolset statistics."""
372
+ queryset = self.get_queryset()
373
+
374
+ return {
375
+ 'total': queryset.count(),
376
+ 'active': queryset.active().count(),
377
+ 'unique_classes': queryset.values('toolset_class').distinct().count(),
378
+ 'recent': queryset.recent().count(),
379
+ }
380
+
381
+
382
+ class ToolPermissionQuerySet(models.QuerySet):
383
+ """Custom queryset for ToolPermission."""
384
+
385
+ def for_user(self, user):
386
+ """Filter permissions for specific user."""
387
+ return self.filter(user=user)
388
+
389
+ def by_tool(self, tool_name: str):
390
+ """Filter by tool name."""
391
+ return self.filter(tool_name=tool_name)
392
+
393
+ def by_permission(self, permission: str):
394
+ """Filter by permission type."""
395
+ return self.filter(permission=permission)
396
+
397
+ def allowed(self):
398
+ """Get allowed permissions."""
399
+ return self.filter(permission='allow')
400
+
401
+ def denied(self):
402
+ """Get denied permissions."""
403
+ return self.filter(permission='deny')
404
+
405
+ def requiring_approval(self):
406
+ """Get permissions requiring approval."""
407
+ return self.filter(permission='require_approval')
408
+
409
+ def by_creator(self, user):
410
+ """Get permissions created by specific user."""
411
+ return self.filter(created_by=user)
412
+
413
+ def recent(self, days: int = 7):
414
+ """Get recently created permissions."""
415
+ from datetime import timedelta
416
+ cutoff = timezone.now() - timedelta(days=days)
417
+ return self.filter(created_at__gte=cutoff)
418
+
419
+
420
+ class ToolPermissionManager(models.Manager):
421
+ """Custom manager for ToolPermission."""
422
+
423
+ def get_queryset(self):
424
+ return ToolPermissionQuerySet(self.model, using=self._db)
425
+
426
+ def for_user(self, user):
427
+ """Get permissions for specific user."""
428
+ return self.get_queryset().for_user(user)
429
+
430
+ def by_tool(self, tool_name: str):
431
+ """Get permissions by tool."""
432
+ return self.get_queryset().by_tool(tool_name)
433
+
434
+ def by_permission(self, permission: str):
435
+ """Get permissions by type."""
436
+ return self.get_queryset().by_permission(permission)
437
+
438
+ def allowed(self):
439
+ """Get allowed permissions."""
440
+ return self.get_queryset().allowed()
441
+
442
+ def denied(self):
443
+ """Get denied permissions."""
444
+ return self.get_queryset().denied()
445
+
446
+ def requiring_approval(self):
447
+ """Get permissions requiring approval."""
448
+ return self.get_queryset().requiring_approval()
449
+
450
+ def by_creator(self, user):
451
+ """Get permissions by creator."""
452
+ return self.get_queryset().by_creator(user)
453
+
454
+ def recent(self, days: int = 7):
455
+ """Get recent permissions."""
456
+ return self.get_queryset().recent(days)
457
+
458
+ def create_permission(
459
+ self,
460
+ user,
461
+ tool_name: str,
462
+ permission: str,
463
+ created_by,
464
+ conditions: Optional[Dict] = None,
465
+ **kwargs
466
+ ):
467
+ """Create a new tool permission."""
468
+ return self.create(
469
+ user=user,
470
+ tool_name=tool_name,
471
+ permission=permission,
472
+ conditions=conditions or {},
473
+ created_by=created_by,
474
+ **kwargs
475
+ )
476
+
477
+ def get_user_permission(self, user, tool_name: str):
478
+ """Get user's permission for specific tool."""
479
+ try:
480
+ return self.get(user=user, tool_name=tool_name)
481
+ except self.model.DoesNotExist:
482
+ return None
483
+
484
+ def get_stats(self) -> Dict[str, Any]:
485
+ """Get permission statistics."""
486
+ queryset = self.get_queryset()
487
+
488
+ return {
489
+ 'total': queryset.count(),
490
+ 'allowed': queryset.allowed().count(),
491
+ 'denied': queryset.denied().count(),
492
+ 'requiring_approval': queryset.requiring_approval().count(),
493
+ 'unique_tools': queryset.values('tool_name').distinct().count(),
494
+ 'unique_users': queryset.values('user').distinct().count(),
495
+ 'recent': queryset.recent().count(),
496
+ }