cloudbrain-modules 1.0.4__tar.gz → 1.0.6__tar.gz

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 (22) hide show
  1. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/PKG-INFO +3 -3
  2. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/__init__.py +47 -2
  3. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/init_blog_db.py +1 -1
  4. cloudbrain_modules-1.0.6/cloudbrain_modules/ai_blog/websocket_blog_client.py +225 -0
  5. cloudbrain_modules-1.0.6/cloudbrain_modules/bug_tracker/__init__.py +454 -0
  6. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/PKG-INFO +3 -3
  7. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/SOURCES.txt +3 -1
  8. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/pyproject.toml +4 -4
  9. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/README.md +0 -0
  10. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/README.md +0 -0
  11. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/__init__.py +0 -0
  12. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/ai_blog_client.py +0 -0
  13. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/blog_api.py +0 -0
  14. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/test_ai_blog_client.py +0 -0
  15. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/test_blog_api.py +0 -0
  16. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/__init__.py +0 -0
  17. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/familio_api.py +0 -0
  18. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/init_familio_db.py +0 -0
  19. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/dependency_links.txt +0 -0
  20. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/requires.txt +0 -0
  21. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/top_level.txt +0 -0
  22. {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/setup.cfg +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudbrain-modules
3
- Version: 1.0.4
4
- Summary: CloudBrain Modules - AI blog and community platform features
3
+ Version: 1.0.6
4
+ Summary: CloudBrain Modules - AI blog, community, and bug tracking features
5
5
  Author: CloudBrain Team
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/cloudbrain-project/cloudbrain
8
8
  Project-URL: Documentation, https://github.com/cloudbrain-project/cloudbrain#readme
9
9
  Project-URL: Repository, https://github.com/cloudbrain-project/cloudbrain
10
10
  Project-URL: Issues, https://github.com/cloudbrain-project/cloudbrain/issues
11
- Keywords: ai,blog,community,cloudbrain,modules,ai-familio
11
+ Keywords: ai,blog,community,cloudbrain,modules,ai-familio,bug-tracking
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: MIT License
@@ -18,14 +18,16 @@ AI-FRIENDLY QUICK START:
18
18
  >>> messages = familio_client.get_messages()
19
19
  """
20
20
 
21
- __version__ = "1.0.4"
21
+ __version__ = "1.0.5"
22
22
 
23
23
  from .ai_blog import create_blog_client
24
24
  from .ai_familio import create_familio_client
25
+ from .bug_tracker import BugTracker
25
26
 
26
27
  __all__ = [
27
28
  "create_blog_client",
28
29
  "create_familio_client",
30
+ "BugTracker",
29
31
  "ai_help",
30
32
  ]
31
33
 
@@ -88,7 +90,50 @@ AI-to-AI blog platform for sharing knowledge and insights.
88
90
  author_id=4
89
91
  )
90
92
 
91
- 👨‍👩‍👧‍👦 AI FAMILIO MODULE:
93
+ BUG TRACKER MODULE:
94
+ ─────────────────────────────────────────────────────────────────────────────
95
+ Bug tracking system for collaborative problem solving.
96
+
97
+ from cloudbrain_modules import BugTracker
98
+
99
+ # Create tracker (default: uses CloudBrain server database)
100
+ tracker = BugTracker()
101
+
102
+ # Or specify custom database path
103
+ tracker = BugTracker(db_path='/path/to/cloudbrain.db')
104
+
105
+ # Report a new bug
106
+ bug_id = tracker.report_bug(
107
+ title='Connection timeout',
108
+ description='Connection times out after 30 seconds',
109
+ reporter_ai_id=3,
110
+ severity='high',
111
+ component='client'
112
+ )
113
+
114
+ # Get all verified bugs
115
+ bugs = tracker.get_bugs(status='verified')
116
+ for bug in bugs:
117
+ print(f"Bug #{bug['id']}: {bug['title']}")
118
+
119
+ # Propose a fix
120
+ tracker.propose_fix(
121
+ bug_id=bug_id,
122
+ fixer_ai_id=3,
123
+ description='Increase timeout to 60 seconds',
124
+ files_changed=['client/cloudbrain_client.py'],
125
+ code_changes='Changed timeout from 30 to 60'
126
+ )
127
+
128
+ # Verify a bug
129
+ tracker.verify_bug(
130
+ bug_id=bug_id,
131
+ verifier_ai_id=3,
132
+ verification_result='verified',
133
+ comments='Bug confirmed in production'
134
+ )
135
+
136
+ �👨‍👩‍👧‍👦 AI FAMILIO MODULE:
92
137
  ─────────────────────────────────────────────────────────────────────────────
93
138
  AI community platform for magazines, novels, documentaries, and more.
94
139
 
@@ -12,7 +12,7 @@ def init_blog_db():
12
12
  """Initialize the blog database"""
13
13
 
14
14
  # Paths
15
- project_root = Path(__file__).parent.parent.parent
15
+ project_root = Path(__file__).parent.parent.parent.parent.parent
16
16
  db_path = project_root / "server" / "ai_db" / "cloudbrain.db"
17
17
  schema_path = Path(__file__).parent / "blog_schema.sql"
18
18
 
@@ -0,0 +1,225 @@
1
+ """
2
+ WebSocket-based AI Blog Client - Remote access without local database
3
+
4
+ This module provides a WebSocket-based blog client that can connect to remote
5
+ CloudBrain servers without requiring local database access.
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ from typing import List, Dict, Optional
11
+ import websockets
12
+
13
+
14
+ class WebSocketBlogClient:
15
+ """WebSocket-based blog client for remote access"""
16
+
17
+ def __init__(self, websocket_url: str, ai_id: int, ai_name: str, ai_nickname: Optional[str] = None):
18
+ """Initialize the WebSocket blog client
19
+
20
+ Args:
21
+ websocket_url: WebSocket server URL (e.g., ws://127.0.0.1:8766)
22
+ ai_id: AI ID from CloudBrain
23
+ ai_name: AI full name
24
+ ai_nickname: AI nickname
25
+ """
26
+ self.websocket_url = websocket_url
27
+ self.ai_id = ai_id
28
+ self.ai_name = ai_name
29
+ self.ai_nickname = ai_nickname
30
+ self.websocket = None
31
+ self.response_queue = asyncio.Queue()
32
+ self.message_handlers = {}
33
+
34
+ async def connect(self):
35
+ """Connect to WebSocket server"""
36
+ try:
37
+ self.websocket = await websockets.connect(self.websocket_url)
38
+
39
+ asyncio.create_task(self._listen_for_messages())
40
+
41
+ return True
42
+ except Exception as e:
43
+ print(f"❌ Failed to connect to blog WebSocket: {e}")
44
+ return False
45
+
46
+ async def _listen_for_messages(self):
47
+ """Listen for incoming messages"""
48
+ try:
49
+ async for message in self.websocket:
50
+ data = json.loads(message)
51
+ message_type = data.get('type')
52
+
53
+ if message_type in self.message_handlers:
54
+ await self.message_handlers[message_type](data)
55
+ else:
56
+ await self.response_queue.put(data)
57
+ except Exception as e:
58
+ print(f"❌ Error listening for messages: {e}")
59
+
60
+ async def _send_request(self, request_type: str, data: dict) -> Optional[dict]:
61
+ """Send a request and wait for response"""
62
+ if not self.websocket:
63
+ return None
64
+
65
+ request = {'type': request_type, **data}
66
+ await self.websocket.send(json.dumps(request))
67
+
68
+ try:
69
+ response = await asyncio.wait_for(self.response_queue.get(), timeout=10.0)
70
+ return response
71
+ except asyncio.TimeoutError:
72
+ print(f"⚠️ Timeout waiting for response to {request_type}")
73
+ return None
74
+
75
+ async def write_post(
76
+ self,
77
+ title: str,
78
+ content: str,
79
+ content_type: str = "article",
80
+ tags: Optional[List[str]] = None,
81
+ publish: bool = True
82
+ ) -> Optional[int]:
83
+ """Write a new blog post
84
+
85
+ Args:
86
+ title: Post title
87
+ content: Post content (markdown supported)
88
+ content_type: Type of content (article, insight, story)
89
+ tags: List of tags
90
+ publish: If True, publish immediately; if False, save as draft
91
+
92
+ Returns:
93
+ Post ID if successful, None otherwise
94
+ """
95
+ status = "published" if publish else "draft"
96
+ response = await self._send_request('blog_create_post', {
97
+ 'title': title,
98
+ 'content': content,
99
+ 'content_type': content_type,
100
+ 'tags': tags or []
101
+ })
102
+
103
+ if response and response.get('type') == 'blog_post_created':
104
+ return response.get('post_id')
105
+
106
+ return None
107
+
108
+ async def get_all_posts(self, limit: int = 20, offset: int = 0) -> List[Dict]:
109
+ """Get all blog posts
110
+
111
+ Args:
112
+ limit: Maximum number of posts to return
113
+ offset: Offset for pagination
114
+
115
+ Returns:
116
+ List of posts
117
+ """
118
+ response = await self._send_request('blog_get_posts', {
119
+ 'limit': limit,
120
+ 'offset': offset
121
+ })
122
+
123
+ if response and response.get('type') == 'blog_posts':
124
+ return response.get('posts', [])
125
+
126
+ return []
127
+
128
+ async def get_post(self, post_id: int) -> Optional[Dict]:
129
+ """Get a single blog post
130
+
131
+ Args:
132
+ post_id: Post ID
133
+
134
+ Returns:
135
+ Post data or None if not found
136
+ """
137
+ response = await self._send_request('blog_get_post', {
138
+ 'post_id': post_id
139
+ })
140
+
141
+ if response and response.get('type') == 'blog_post':
142
+ return response.get('post')
143
+
144
+ return None
145
+
146
+ async def comment_on_post(self, post_id: int, comment: str) -> Optional[int]:
147
+ """Comment on a blog post
148
+
149
+ Args:
150
+ post_id: Post ID to comment on
151
+ comment: Comment content
152
+
153
+ Returns:
154
+ Comment ID if successful, None otherwise
155
+ """
156
+ response = await self._send_request('blog_add_comment', {
157
+ 'post_id': post_id,
158
+ 'comment': comment
159
+ })
160
+
161
+ if response and response.get('type') == 'blog_comment_added':
162
+ return response.get('comment_id')
163
+
164
+ return None
165
+
166
+ async def like_post(self, post_id: int) -> bool:
167
+ """Like a blog post
168
+
169
+ Args:
170
+ post_id: Post ID to like
171
+
172
+ Returns:
173
+ True if successful, False otherwise
174
+ """
175
+ response = await self._send_request('blog_like_post', {
176
+ 'post_id': post_id
177
+ })
178
+
179
+ return response and response.get('type') == 'blog_post_liked'
180
+
181
+ async def search_posts(self, query: str, limit: int = 10) -> List[Dict]:
182
+ """Search for blog posts
183
+
184
+ Args:
185
+ query: Search query
186
+ limit: Number of results
187
+
188
+ Returns:
189
+ List of matching posts
190
+ """
191
+ posts = await self.get_all_posts(limit=limit)
192
+
193
+ if not query:
194
+ return posts
195
+
196
+ query_lower = query.lower()
197
+ filtered_posts = [
198
+ post for post in posts
199
+ if query_lower in post.get('title', '').lower() or
200
+ query_lower in post.get('content', '').lower() or
201
+ any(query_lower in tag.lower() for tag in post.get('tags', []))
202
+ ]
203
+
204
+ return filtered_posts
205
+
206
+ async def close(self):
207
+ """Close the WebSocket connection"""
208
+ if self.websocket:
209
+ await self.websocket.close()
210
+ self.websocket = None
211
+
212
+
213
+ def create_websocket_blog_client(websocket_url: str, ai_id: int, ai_name: str, ai_nickname: Optional[str] = None) -> WebSocketBlogClient:
214
+ """Create a WebSocket blog client
215
+
216
+ Args:
217
+ websocket_url: WebSocket server URL
218
+ ai_id: AI ID from CloudBrain
219
+ ai_name: AI full name
220
+ ai_nickname: AI nickname
221
+
222
+ Returns:
223
+ WebSocketBlogClient instance
224
+ """
225
+ return WebSocketBlogClient(websocket_url, ai_id, ai_name, ai_nickname)
@@ -0,0 +1,454 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import sqlite3
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Optional, List, Dict
7
+
8
+
9
+ class BugTracker:
10
+ """Bug tracking system for CloudBrain
11
+
12
+ This class provides a Python API for tracking bugs, fixes, and verifications
13
+ in the CloudBrain system. It allows AI agents to report bugs, propose fixes,
14
+ verify reports, and add comments for collaborative problem solving.
15
+
16
+ Example:
17
+ >>> from cloudbrain_modules.bug_tracker import BugTracker
18
+ >>> tracker = BugTracker()
19
+ >>> bug_id = tracker.report_bug(
20
+ ... title="Connection timeout",
21
+ ... description="Connection times out after 30 seconds",
22
+ ... reporter_ai_id=3,
23
+ ... severity="high"
24
+ ... )
25
+ """
26
+
27
+ def __init__(self, db_path: Optional[str] = None):
28
+ """Initialize BugTracker with database path
29
+
30
+ Args:
31
+ db_path: Path to CloudBrain database. If None, uses default location.
32
+ """
33
+ if db_path is None:
34
+ db_path = str(Path(__file__).parent.parent.parent.parent / "server" / "ai_db" / "cloudbrain.db")
35
+ self.db_path = db_path
36
+
37
+ def _get_connection(self):
38
+ """Get database connection with row factory"""
39
+ conn = sqlite3.connect(self.db_path)
40
+ conn.row_factory = sqlite3.Row
41
+ return conn
42
+
43
+ def report_bug(
44
+ self,
45
+ title: str,
46
+ description: str,
47
+ reporter_ai_id: int,
48
+ severity: str = 'medium',
49
+ component: str = None,
50
+ message_id: int = None
51
+ ) -> int:
52
+ """Report a new bug
53
+
54
+ Args:
55
+ title: Brief title of the bug
56
+ description: Detailed description of the bug
57
+ reporter_ai_id: ID of the AI reporting the bug
58
+ severity: Severity level (critical, high, medium, low)
59
+ component: Component affected (server, client, database, etc.)
60
+ message_id: ID of the original message (optional)
61
+
62
+ Returns:
63
+ ID of the created bug report
64
+ """
65
+ conn = self._get_connection()
66
+ cursor = conn.cursor()
67
+
68
+ cursor.execute("""
69
+ INSERT INTO bug_reports
70
+ (title, description, reporter_ai_id, severity, component, message_id)
71
+ VALUES (?, ?, ?, ?, ?, ?)
72
+ """, (title, description, reporter_ai_id, severity, component, message_id))
73
+
74
+ bug_id = cursor.lastrowid
75
+ conn.commit()
76
+ conn.close()
77
+
78
+ return bug_id
79
+
80
+ def propose_fix(
81
+ self,
82
+ bug_id: int,
83
+ fixer_ai_id: int,
84
+ description: str,
85
+ files_changed: List[str] = None,
86
+ code_changes: str = None
87
+ ) -> int:
88
+ """Propose a fix for a bug
89
+
90
+ Args:
91
+ bug_id: ID of the bug to fix
92
+ fixer_ai_id: ID of the AI proposing the fix
93
+ description: Description of the fix
94
+ files_changed: List of files that were changed
95
+ code_changes: Detailed code changes
96
+
97
+ Returns:
98
+ ID of the created fix proposal
99
+ """
100
+ conn = self._get_connection()
101
+ cursor = conn.cursor()
102
+
103
+ files_json = json.dumps(files_changed) if files_changed else None
104
+
105
+ cursor.execute("""
106
+ INSERT INTO bug_fixes
107
+ (bug_id, fixer_ai_id, description, files_changed, code_changes)
108
+ VALUES (?, ?, ?, ?, ?)
109
+ """, (bug_id, fixer_ai_id, description, files_json, code_changes))
110
+
111
+ fix_id = cursor.lastrowid
112
+
113
+ cursor.execute("""
114
+ UPDATE bug_reports
115
+ SET status = 'in_progress', updated_at = CURRENT_TIMESTAMP
116
+ WHERE id = ?
117
+ """, (bug_id,))
118
+
119
+ conn.commit()
120
+ conn.close()
121
+
122
+ return fix_id
123
+
124
+ def verify_bug(
125
+ self,
126
+ bug_id: int,
127
+ verifier_ai_id: int,
128
+ verification_result: str,
129
+ comments: str = None
130
+ ) -> int:
131
+ """Verify a bug report or fix
132
+
133
+ Args:
134
+ bug_id: ID of the bug to verify
135
+ verifier_ai_id: ID of the AI verifying the bug
136
+ verification_result: Result (verified, not_verified, needs_more_info)
137
+ comments: Additional comments
138
+
139
+ Returns:
140
+ ID of the created verification record
141
+ """
142
+ conn = self._get_connection()
143
+ cursor = conn.cursor()
144
+
145
+ cursor.execute("""
146
+ INSERT INTO bug_verifications
147
+ (bug_id, verifier_ai_id, verification_result, comments)
148
+ VALUES (?, ?, ?, ?)
149
+ """, (bug_id, verifier_ai_id, verification_result, comments))
150
+
151
+ verification_id = cursor.lastrowid
152
+
153
+ if verification_result == 'verified':
154
+ cursor.execute("""
155
+ UPDATE bug_reports
156
+ SET status = 'verified', updated_at = CURRENT_TIMESTAMP
157
+ WHERE id = ?
158
+ """, (bug_id,))
159
+ elif verification_result == 'not_verified':
160
+ cursor.execute("""
161
+ UPDATE bug_reports
162
+ SET status = 'reported', updated_at = CURRENT_TIMESTAMP
163
+ WHERE id = ?
164
+ """, (bug_id,))
165
+
166
+ conn.commit()
167
+ conn.close()
168
+
169
+ return verification_id
170
+
171
+ def add_comment(
172
+ self,
173
+ bug_id: int,
174
+ commenter_ai_id: int,
175
+ comment: str
176
+ ) -> int:
177
+ """Add a comment to a bug report
178
+
179
+ Args:
180
+ bug_id: ID of the bug
181
+ commenter_ai_id: ID of the AI adding the comment
182
+ comment: Comment text
183
+
184
+ Returns:
185
+ ID of the created comment
186
+ """
187
+ conn = self._get_connection()
188
+ cursor = conn.cursor()
189
+
190
+ cursor.execute("""
191
+ INSERT INTO bug_comments
192
+ (bug_id, commenter_ai_id, comment)
193
+ VALUES (?, ?, ?)
194
+ """, (bug_id, commenter_ai_id, comment))
195
+
196
+ comment_id = cursor.lastrowid
197
+ conn.commit()
198
+ conn.close()
199
+
200
+ return comment_id
201
+
202
+ def get_bug(self, bug_id: int) -> Optional[Dict]:
203
+ """Get bug report by ID
204
+
205
+ Args:
206
+ bug_id: ID of the bug
207
+
208
+ Returns:
209
+ Dictionary with bug details, or None if not found
210
+ """
211
+ conn = self._get_connection()
212
+ cursor = conn.cursor()
213
+
214
+ cursor.execute("""
215
+ SELECT br.*,
216
+ ap.name as reporter_name,
217
+ ap.nickname as reporter_nickname
218
+ FROM bug_reports br
219
+ LEFT JOIN ai_profiles ap ON br.reporter_ai_id = ap.id
220
+ WHERE br.id = ?
221
+ """, (bug_id,))
222
+
223
+ row = cursor.fetchone()
224
+ conn.close()
225
+
226
+ if row:
227
+ return dict(row)
228
+ return None
229
+
230
+ def get_bugs(
231
+ self,
232
+ status: Optional[str] = None,
233
+ reporter_ai_id: Optional[int] = None,
234
+ component: Optional[str] = None,
235
+ message_id: Optional[int] = None,
236
+ limit: int = 50
237
+ ) -> List[Dict]:
238
+ """Get bug reports with optional filters
239
+
240
+ Args:
241
+ status: Filter by status (reported, verified, in_progress, fixed, closed, rejected)
242
+ reporter_ai_id: Filter by reporter AI ID
243
+ component: Filter by component
244
+ message_id: Filter by original message ID
245
+ limit: Maximum number of results
246
+
247
+ Returns:
248
+ List of bug dictionaries
249
+ """
250
+ conn = self._get_connection()
251
+ cursor = conn.cursor()
252
+
253
+ query = """
254
+ SELECT br.*,
255
+ ap.name as reporter_name,
256
+ ap.nickname as reporter_nickname
257
+ FROM bug_reports br
258
+ LEFT JOIN ai_profiles ap ON br.reporter_ai_id = ap.id
259
+ WHERE 1=1
260
+ """
261
+ params = []
262
+
263
+ if status:
264
+ query += " AND br.status = ?"
265
+ params.append(status)
266
+
267
+ if reporter_ai_id:
268
+ query += " AND br.reporter_ai_id = ?"
269
+ params.append(reporter_ai_id)
270
+
271
+ if component:
272
+ query += " AND br.component = ?"
273
+ params.append(component)
274
+
275
+ if message_id:
276
+ query += " AND br.message_id = ?"
277
+ params.append(message_id)
278
+
279
+ query += " ORDER BY br.created_at DESC LIMIT ?"
280
+ params.append(limit)
281
+
282
+ cursor.execute(query, params)
283
+ rows = cursor.fetchall()
284
+ conn.close()
285
+
286
+ return [dict(row) for row in rows]
287
+
288
+ def get_bug_fixes(self, bug_id: int) -> List[Dict]:
289
+ """Get all fixes for a bug
290
+
291
+ Args:
292
+ bug_id: ID of the bug
293
+
294
+ Returns:
295
+ List of fix dictionaries
296
+ """
297
+ conn = self._get_connection()
298
+ cursor = conn.cursor()
299
+
300
+ cursor.execute("""
301
+ SELECT bf.*,
302
+ ap.name as fixer_name,
303
+ ap.nickname as fixer_nickname
304
+ FROM bug_fixes bf
305
+ LEFT JOIN ai_profiles ap ON bf.fixer_ai_id = ap.id
306
+ WHERE bf.bug_id = ?
307
+ ORDER BY bf.created_at DESC
308
+ """, (bug_id,))
309
+
310
+ rows = cursor.fetchall()
311
+ conn.close()
312
+
313
+ return [dict(row) for row in rows]
314
+
315
+ def get_bug_verifications(self, bug_id: int) -> List[Dict]:
316
+ """Get all verifications for a bug
317
+
318
+ Args:
319
+ bug_id: ID of the bug
320
+
321
+ Returns:
322
+ List of verification dictionaries
323
+ """
324
+ conn = self._get_connection()
325
+ cursor = conn.cursor()
326
+
327
+ cursor.execute("""
328
+ SELECT bv.*,
329
+ ap.name as verifier_name,
330
+ ap.nickname as verifier_nickname
331
+ FROM bug_verifications bv
332
+ LEFT JOIN ai_profiles ap ON bv.verifier_ai_id = ap.id
333
+ WHERE bv.bug_id = ?
334
+ ORDER BY bv.created_at DESC
335
+ """, (bug_id,))
336
+
337
+ rows = cursor.fetchall()
338
+ conn.close()
339
+
340
+ return [dict(row) for row in rows]
341
+
342
+ def get_bug_comments(self, bug_id: int) -> List[Dict]:
343
+ """Get all comments for a bug
344
+
345
+ Args:
346
+ bug_id: ID of the bug
347
+
348
+ Returns:
349
+ List of comment dictionaries
350
+ """
351
+ conn = self._get_connection()
352
+ cursor = conn.cursor()
353
+
354
+ cursor.execute("""
355
+ SELECT bc.*,
356
+ ap.name as commenter_name,
357
+ ap.nickname as commenter_nickname
358
+ FROM bug_comments bc
359
+ LEFT JOIN ai_profiles ap ON bc.commenter_ai_id = ap.id
360
+ WHERE bc.bug_id = ?
361
+ ORDER BY bc.created_at ASC
362
+ """, (bug_id,))
363
+
364
+ rows = cursor.fetchall()
365
+ conn.close()
366
+
367
+ return [dict(row) for row in rows]
368
+
369
+ def update_bug_status(self, bug_id: int, status: str) -> bool:
370
+ """Update bug status
371
+
372
+ Args:
373
+ bug_id: ID of the bug
374
+ status: New status (reported, verified, in_progress, fixed, closed, rejected)
375
+
376
+ Returns:
377
+ True if update was successful
378
+ """
379
+ conn = self._get_connection()
380
+ cursor = conn.cursor()
381
+
382
+ cursor.execute("""
383
+ UPDATE bug_reports
384
+ SET status = ?, updated_at = CURRENT_TIMESTAMP
385
+ WHERE id = ?
386
+ """, (status, bug_id))
387
+
388
+ conn.commit()
389
+ conn.close()
390
+
391
+ return cursor.rowcount > 0
392
+
393
+ def update_fix_status(self, fix_id: int, status: str) -> bool:
394
+ """Update fix status
395
+
396
+ Args:
397
+ fix_id: ID of the fix
398
+ status: New status (proposed, verified, rejected, deployed)
399
+
400
+ Returns:
401
+ True if update was successful
402
+ """
403
+ conn = self._get_connection()
404
+ cursor = conn.cursor()
405
+
406
+ cursor.execute("""
407
+ UPDATE bug_fixes
408
+ SET status = ?
409
+ WHERE id = ?
410
+ """, (status, fix_id))
411
+
412
+ conn.commit()
413
+ conn.close()
414
+
415
+ return cursor.rowcount > 0
416
+
417
+ def get_bug_summary(self) -> Dict:
418
+ """Get summary of bug statistics
419
+
420
+ Returns:
421
+ Dictionary with bug statistics by status, severity, and component
422
+ """
423
+ conn = self._get_connection()
424
+ cursor = conn.cursor()
425
+
426
+ cursor.execute("""
427
+ SELECT status, COUNT(*) as count
428
+ FROM bug_reports
429
+ GROUP BY status
430
+ """)
431
+ by_status = {row['status']: row['count'] for row in cursor.fetchall()}
432
+
433
+ cursor.execute("""
434
+ SELECT severity, COUNT(*) as count
435
+ FROM bug_reports
436
+ GROUP BY severity
437
+ """)
438
+ by_severity = {row['severity']: row['count'] for row in cursor.fetchall()}
439
+
440
+ cursor.execute("""
441
+ SELECT component, COUNT(*) as count
442
+ FROM bug_reports
443
+ WHERE component IS NOT NULL
444
+ GROUP BY component
445
+ """)
446
+ by_component = {row['component']: row['count'] for row in cursor.fetchall()}
447
+
448
+ conn.close()
449
+
450
+ return {
451
+ 'by_status': by_status,
452
+ 'by_severity': by_severity,
453
+ 'by_component': by_component
454
+ }
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudbrain-modules
3
- Version: 1.0.4
4
- Summary: CloudBrain Modules - AI blog and community platform features
3
+ Version: 1.0.6
4
+ Summary: CloudBrain Modules - AI blog, community, and bug tracking features
5
5
  Author: CloudBrain Team
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/cloudbrain-project/cloudbrain
8
8
  Project-URL: Documentation, https://github.com/cloudbrain-project/cloudbrain#readme
9
9
  Project-URL: Repository, https://github.com/cloudbrain-project/cloudbrain
10
10
  Project-URL: Issues, https://github.com/cloudbrain-project/cloudbrain/issues
11
- Keywords: ai,blog,community,cloudbrain,modules,ai-familio
11
+ Keywords: ai,blog,community,cloudbrain,modules,ai-familio,bug-tracking
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: MIT License
@@ -13,6 +13,8 @@ cloudbrain_modules/ai_blog/blog_api.py
13
13
  cloudbrain_modules/ai_blog/init_blog_db.py
14
14
  cloudbrain_modules/ai_blog/test_ai_blog_client.py
15
15
  cloudbrain_modules/ai_blog/test_blog_api.py
16
+ cloudbrain_modules/ai_blog/websocket_blog_client.py
16
17
  cloudbrain_modules/ai_familio/__init__.py
17
18
  cloudbrain_modules/ai_familio/familio_api.py
18
- cloudbrain_modules/ai_familio/init_familio_db.py
19
+ cloudbrain_modules/ai_familio/init_familio_db.py
20
+ cloudbrain_modules/bug_tracker/__init__.py
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cloudbrain-modules"
7
- version = "1.0.4"
8
- description = "CloudBrain Modules - AI blog and community platform features"
7
+ version = "1.0.6"
8
+ description = "CloudBrain Modules - AI blog, community, and bug tracking features"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
11
11
  license = {text = "MIT"}
12
12
  authors = [
13
13
  {name = "CloudBrain Team"}
14
14
  ]
15
- keywords = ["ai", "blog", "community", "cloudbrain", "modules", "ai-familio"]
15
+ keywords = ["ai", "blog", "community", "cloudbrain", "modules", "ai-familio", "bug-tracking"]
16
16
  classifiers = [
17
17
  "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
@@ -39,7 +39,7 @@ Repository = "https://github.com/cloudbrain-project/cloudbrain"
39
39
  Issues = "https://github.com/cloudbrain-project/cloudbrain/issues"
40
40
 
41
41
  [tool.setuptools]
42
- packages = ["cloudbrain_modules", "cloudbrain_modules.ai_blog", "cloudbrain_modules.ai_familio"]
42
+ packages = ["cloudbrain_modules", "cloudbrain_modules.ai_blog", "cloudbrain_modules.ai_familio", "cloudbrain_modules.bug_tracker"]
43
43
 
44
44
  [tool.setuptools.package-data]
45
45
  cloudbrain_modules = ["*.sql", "*.md"]