souleyez 2.43.29__py3-none-any.whl → 3.0.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.

Potentially problematic release.


This version of souleyez might be problematic. Click here for more details.

Files changed (358) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9564 -2881
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1239 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2200 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +564 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +409 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +417 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +292 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +913 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +219 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +237 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +23034 -10679
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
  353. souleyez-3.0.0.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Team collaboration features for deliverables.
3
3
  """
4
+
4
5
  from typing import Dict, List, Optional
5
6
  from datetime import datetime
6
7
  from .database import get_db
@@ -9,66 +10,69 @@ import os
9
10
 
10
11
  class TeamCollaboration:
11
12
  """Manage team collaboration on deliverables."""
12
-
13
+
13
14
  def __init__(self):
14
15
  self.db = get_db()
15
-
16
+
16
17
  def _get_current_user(self) -> str:
17
18
  """Get current username from environment or system."""
18
19
  # Try environment variable first
19
- user = os.environ.get('USER') or os.environ.get('USERNAME') or 'unknown'
20
+ user = os.environ.get("USER") or os.environ.get("USERNAME") or "unknown"
20
21
  return user
21
-
22
+
22
23
  def log_activity(
23
24
  self,
24
25
  deliverable_id: int,
25
26
  engagement_id: int,
26
27
  action: str,
27
28
  details: str = None,
28
- user: str = None
29
+ user: str = None,
29
30
  ) -> int:
30
31
  """
31
32
  Log activity on a deliverable.
32
-
33
+
33
34
  Args:
34
35
  deliverable_id: Deliverable ID
35
36
  engagement_id: Engagement ID
36
37
  action: Action type (started, completed, updated, assigned, etc.)
37
38
  details: Additional details
38
39
  user: Username (defaults to current user)
39
-
40
+
40
41
  Returns:
41
42
  Activity log ID
42
43
  """
43
44
  if user is None:
44
45
  user = self._get_current_user()
45
-
46
- activity_id = self.db.insert('deliverable_activity', {
47
- 'deliverable_id': deliverable_id,
48
- 'engagement_id': engagement_id,
49
- 'user': user,
50
- 'action': action,
51
- 'details': details
52
- })
53
-
46
+
47
+ activity_id = self.db.insert(
48
+ "deliverable_activity",
49
+ {
50
+ "deliverable_id": deliverable_id,
51
+ "engagement_id": engagement_id,
52
+ "user": user,
53
+ "action": action,
54
+ "details": details,
55
+ },
56
+ )
57
+
54
58
  return activity_id
55
-
59
+
56
60
  def get_activity(
57
61
  self,
58
62
  deliverable_id: int = None,
59
63
  engagement_id: int = None,
60
64
  user: str = None,
61
- limit: int = 50
65
+ limit: int = 50,
62
66
  ) -> List[Dict]:
63
67
  """
64
68
  Get activity log.
65
-
69
+
66
70
  Args:
67
71
  deliverable_id: Filter by deliverable
68
72
  engagement_id: Filter by engagement
69
73
  user: Filter by user
70
74
  limit: Max results
71
-
75
+
72
76
  Returns:
73
77
  List of activity records
74
78
  """
@@ -82,59 +86,53 @@ class TeamCollaboration:
82
86
  WHERE 1=1
83
87
  """
84
88
  params = []
85
-
89
+
86
90
  if deliverable_id:
87
91
  query += " AND da.deliverable_id = ?"
88
92
  params.append(deliverable_id)
89
-
93
+
90
94
  if engagement_id:
91
95
  query += " AND da.engagement_id = ?"
92
96
  params.append(engagement_id)
93
-
97
+
94
98
  if user:
95
99
  query += " AND da.user = ?"
96
100
  params.append(user)
97
-
101
+
98
102
  query += " ORDER BY da.created_at DESC LIMIT ?"
99
103
  params.append(limit)
100
-
104
+
101
105
  return self.db.execute(query, tuple(params))
102
-
103
- def add_comment(
104
- self,
105
- deliverable_id: int,
106
- comment: str,
107
- user: str = None
108
- ) -> int:
106
+
107
+ def add_comment(self, deliverable_id: int, comment: str, user: str = None) -> int:
109
108
  """
110
109
  Add comment to deliverable.
111
-
110
+
112
111
  Args:
113
112
  deliverable_id: Deliverable ID
114
113
  comment: Comment text
115
114
  user: Username (defaults to current user)
116
-
115
+
117
116
  Returns:
118
117
  Comment ID
119
118
  """
120
119
  if user is None:
121
120
  user = self._get_current_user()
122
-
123
- comment_id = self.db.insert('deliverable_comments', {
124
- 'deliverable_id': deliverable_id,
125
- 'user': user,
126
- 'comment': comment
127
- })
128
-
121
+
122
+ comment_id = self.db.insert(
123
+ "deliverable_comments",
124
+ {"deliverable_id": deliverable_id, "user": user, "comment": comment},
125
+ )
126
+
129
127
  return comment_id
130
-
128
+
131
129
  def get_comments(self, deliverable_id: int) -> List[Dict]:
132
130
  """
133
131
  Get comments for deliverable.
134
-
132
+
135
133
  Args:
136
134
  deliverable_id: Deliverable ID
137
-
135
+
138
136
  Returns:
139
137
  List of comments
140
138
  """
@@ -144,120 +142,116 @@ class TeamCollaboration:
144
142
  WHERE deliverable_id = ?
145
143
  ORDER BY created_at ASC
146
144
  """,
147
- (deliverable_id,)
145
+ (deliverable_id,),
148
146
  )
149
-
147
+
150
148
  def delete_comment(self, comment_id: int, user: str = None) -> bool:
151
149
  """
152
150
  Delete comment (only by original author).
153
-
151
+
154
152
  Args:
155
153
  comment_id: Comment ID
156
154
  user: Username (defaults to current user)
157
-
155
+
158
156
  Returns:
159
157
  True if deleted
160
158
  """
161
159
  if user is None:
162
160
  user = self._get_current_user()
163
-
161
+
164
162
  # Verify ownership
165
163
  comment = self.db.execute_one(
166
- "SELECT user FROM deliverable_comments WHERE id = ?",
167
- (comment_id,)
164
+ "SELECT user FROM deliverable_comments WHERE id = ?", (comment_id,)
168
165
  )
169
-
170
- if not comment or comment['user'] != user:
166
+
167
+ if not comment or comment["user"] != user:
171
168
  return False
172
-
173
- self.db.execute(
174
- "DELETE FROM deliverable_comments WHERE id = ?",
175
- (comment_id,)
176
- )
177
-
169
+
170
+ self.db.execute("DELETE FROM deliverable_comments WHERE id = ?", (comment_id,))
171
+
178
172
  return True
179
-
173
+
180
174
  def assign_deliverable(
181
175
  self,
182
176
  deliverable_id: int,
183
177
  engagement_id: int,
184
178
  assigned_to: str,
185
- assigned_by: str = None
179
+ assigned_by: str = None,
186
180
  ) -> bool:
187
181
  """
188
182
  Assign deliverable to user.
189
-
183
+
190
184
  Args:
191
185
  deliverable_id: Deliverable ID
192
186
  engagement_id: Engagement ID
193
187
  assigned_to: Username to assign to
194
188
  assigned_by: Username doing assignment
195
-
189
+
196
190
  Returns:
197
191
  True if successful
198
192
  """
199
193
  if assigned_by is None:
200
194
  assigned_by = self._get_current_user()
201
-
195
+
202
196
  # Update deliverable
203
197
  self.db.execute(
204
198
  "UPDATE deliverables SET assigned_to = ? WHERE id = ?",
205
- (assigned_to, deliverable_id)
199
+ (assigned_to, deliverable_id),
206
200
  )
207
-
201
+
208
202
  # Log activity
209
- details = f"Assigned to {assigned_to}" if assigned_by != assigned_to else "Self-assigned"
203
+ details = (
204
+ f"Assigned to {assigned_to}"
205
+ if assigned_by != assigned_to
206
+ else "Self-assigned"
207
+ )
210
208
  self.log_activity(
211
209
  deliverable_id=deliverable_id,
212
210
  engagement_id=engagement_id,
213
- action='assigned',
211
+ action="assigned",
214
212
  details=details,
215
- user=assigned_by
213
+ user=assigned_by,
216
214
  )
217
-
215
+
218
216
  return True
219
-
217
+
220
218
  def unassign_deliverable(
221
- self,
222
- deliverable_id: int,
223
- engagement_id: int,
224
- user: str = None
219
+ self, deliverable_id: int, engagement_id: int, user: str = None
225
220
  ) -> bool:
226
221
  """
227
222
  Unassign deliverable.
228
-
223
+
229
224
  Args:
230
225
  deliverable_id: Deliverable ID
231
226
  engagement_id: Engagement ID
232
227
  user: Username doing unassignment
233
-
228
+
234
229
  Returns:
235
230
  True if successful
236
231
  """
237
232
  if user is None:
238
233
  user = self._get_current_user()
239
-
234
+
240
235
  # Update deliverable
241
236
  self.db.execute(
242
- "UPDATE deliverables SET assigned_to = NULL WHERE id = ?",
243
- (deliverable_id,)
237
+ "UPDATE deliverables SET assigned_to = NULL WHERE id = ?", (deliverable_id,)
244
238
  )
245
-
239
+
246
240
  # Log activity
247
241
  self.log_activity(
248
242
  deliverable_id=deliverable_id,
249
243
  engagement_id=engagement_id,
250
- action='unassigned',
244
+ action="unassigned",
251
245
  details="Unassigned",
252
- user=user
246
+ user=user,
253
247
  )
254
-
248
+
255
249
  return True
256
-
250
+
257
251
  def get_team_summary(self, engagement_id: int) -> Dict:
258
252
  """
259
253
  Get team activity summary for engagement.
260
-
254
+
261
255
  Returns:
262
256
  Dict with team statistics
263
257
  """
@@ -268,16 +262,16 @@ class TeamCollaboration:
268
262
  FROM deliverable_activity
269
263
  WHERE engagement_id = ?
270
264
  """,
271
- (engagement_id,)
265
+ (engagement_id,),
272
266
  )
273
-
274
- user_list = [u['user'] for u in users]
275
-
267
+
268
+ user_list = [u["user"] for u in users]
269
+
276
270
  # Get activity count per user
277
271
  user_activity = {}
278
272
  for user_row in users:
279
- user = user_row['user']
280
-
273
+ user = user_row["user"]
274
+
281
275
  # Count activities
282
276
  activity_count = self.db.execute_one(
283
277
  """
@@ -285,9 +279,9 @@ class TeamCollaboration:
285
279
  FROM deliverable_activity
286
280
  WHERE engagement_id = ? AND user = ?
287
281
  """,
288
- (engagement_id, user)
282
+ (engagement_id, user),
289
283
  )
290
-
284
+
291
285
  # Count assignments
292
286
  assigned_count = self.db.execute_one(
293
287
  """
@@ -295,9 +289,9 @@ class TeamCollaboration:
295
289
  FROM deliverables
296
290
  WHERE engagement_id = ? AND assigned_to = ?
297
291
  """,
298
- (engagement_id, user)
292
+ (engagement_id, user),
299
293
  )
300
-
294
+
301
295
  # Count completed
302
296
  completed_count = self.db.execute_one(
303
297
  """
@@ -305,25 +299,25 @@ class TeamCollaboration:
305
299
  FROM deliverables
306
300
  WHERE engagement_id = ? AND assigned_to = ? AND status = 'completed'
307
301
  """,
308
- (engagement_id, user)
302
+ (engagement_id, user),
309
303
  )
310
-
304
+
311
305
  user_activity[user] = {
312
- 'activity_count': activity_count['count'] if activity_count else 0,
313
- 'assigned_count': assigned_count['count'] if assigned_count else 0,
314
- 'completed_count': completed_count['count'] if completed_count else 0
306
+ "activity_count": activity_count["count"] if activity_count else 0,
307
+ "assigned_count": assigned_count["count"] if assigned_count else 0,
308
+ "completed_count": completed_count["count"] if completed_count else 0,
315
309
  }
316
-
310
+
317
311
  return {
318
- 'users': user_list,
319
- 'user_activity': user_activity,
320
- 'total_users': len(user_list)
312
+ "users": user_list,
313
+ "user_activity": user_activity,
314
+ "total_users": len(user_list),
321
315
  }
322
-
316
+
323
317
  def get_user_workload(self, engagement_id: int) -> List[Dict]:
324
318
  """
325
319
  Get workload per user.
326
-
320
+
327
321
  Returns:
328
322
  List of users with workload stats
329
323
  """
@@ -341,66 +335,66 @@ class TeamCollaboration:
341
335
  GROUP BY assigned_to
342
336
  ORDER BY total_assigned DESC
343
337
  """,
344
- (engagement_id,)
338
+ (engagement_id,),
345
339
  )
346
-
340
+
347
341
  return workload
348
-
342
+
349
343
  def get_recent_activity_feed(
350
- self,
351
- engagement_id: int,
352
- limit: int = 20
344
+ self, engagement_id: int, limit: int = 20
353
345
  ) -> List[Dict]:
354
346
  """
355
347
  Get recent activity feed with formatted messages.
356
-
348
+
357
349
  Returns:
358
350
  List of activity items with human-readable messages
359
351
  """
360
352
  activities = self.get_activity(engagement_id=engagement_id, limit=limit)
361
-
353
+
362
354
  feed = []
363
355
  for activity in activities:
364
356
  # Format message based on action
365
- action = activity['action']
366
- user = activity['user']
367
- title = activity.get('deliverable_title', 'Unknown')
368
- details = activity.get('details', '')
369
- created_at = activity['created_at']
370
-
371
- if action == 'started':
357
+ action = activity["action"]
358
+ user = activity["user"]
359
+ title = activity.get("deliverable_title", "Unknown")
360
+ details = activity.get("details", "")
361
+ created_at = activity["created_at"]
362
+
363
+ if action == "started":
372
364
  message = f"{user} started '{title}'"
373
- elif action == 'completed':
365
+ elif action == "completed":
374
366
  message = f"{user} completed '{title}'"
375
- elif action == 'updated':
367
+ elif action == "updated":
376
368
  message = f"{user} updated '{title}'"
377
369
  if details:
378
370
  message += f" ({details})"
379
- elif action == 'assigned':
371
+ elif action == "assigned":
380
372
  message = f"{user} {details} for '{title}'"
381
- elif action == 'unassigned':
373
+ elif action == "unassigned":
382
374
  message = f"{user} unassigned '{title}'"
383
- elif action == 'blocker_set':
375
+ elif action == "blocker_set":
384
376
  message = f"{user} set blocker on '{title}': {details}"
385
- elif action == 'blocker_cleared':
377
+ elif action == "blocker_cleared":
386
378
  message = f"{user} cleared blocker on '{title}'"
387
- elif action == 'evidence_linked':
379
+ elif action == "evidence_linked":
388
380
  message = f"{user} linked evidence to '{title}'"
389
381
  if details:
390
382
  message += f" ({details})"
391
- elif action == 'commented':
383
+ elif action == "commented":
392
384
  message = f"{user} commented on '{title}'"
393
385
  else:
394
386
  message = f"{user} {action} '{title}'"
395
-
396
- feed.append({
397
- 'id': activity['id'],
398
- 'message': message,
399
- 'action': action,
400
- 'user': user,
401
- 'deliverable_id': activity['deliverable_id'],
402
- 'created_at': created_at,
403
- 'details': details
404
- })
405
-
387
+
388
+ feed.append(
389
+ {
390
+ "id": activity["id"],
391
+ "message": message,
392
+ "action": action,
393
+ "user": user,
394
+ "deliverable_id": activity["deliverable_id"],
395
+ "created_at": created_at,
396
+ "details": details,
397
+ }
398
+ )
399
+
406
400
  return feed