-
Notifications
You must be signed in to change notification settings - Fork 5
feat: Phase 3 Workspace View with activity feed #335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
cc59355
ce27326
d4628c6
e2aa9c6
faead7e
150e129
fabdc83
86f1299
460a018
38d0eb6
a1c97e4
d76b303
72b9927
ac91fac
6b58aa2
3ff5cc5
7ccccec
9ab544c
cd5f39d
0152c3a
b77dda4
a59fcdc
d310e23
c41fa61
2f6f077
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| """Events API router for CodeFRAME v2. | ||
|
|
||
| Provides endpoints for fetching workspace activity/event history. | ||
| Delegates to codeframe.core.events module. | ||
| """ | ||
|
|
||
| from typing import Optional | ||
| from fastapi import APIRouter, HTTPException, Query | ||
| from pydantic import BaseModel | ||
|
|
||
| from codeframe.core.workspace import Workspace, get_workspace, workspace_exists | ||
| from codeframe.core import events | ||
| from pathlib import Path | ||
|
|
||
| router = APIRouter(prefix="/api/v2/events", tags=["events"]) | ||
|
|
||
|
|
||
| class EventResponse(BaseModel): | ||
| """Response model for a single event.""" | ||
|
|
||
| id: int | ||
| workspace_id: str | ||
| event_type: str | ||
| payload: dict | ||
| created_at: str # ISO format | ||
|
|
||
|
|
||
| class EventListResponse(BaseModel): | ||
| """Response model for event list.""" | ||
|
|
||
| events: list[EventResponse] | ||
| total: int | ||
|
Comment on lines
+33
to
+37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Misleading The Either rename to Option A: Rename to clarify semantics class EventListResponse(BaseModel):
"""Response model for event list."""
events: list[EventResponse]
- total: int
+ count: int # Number of events in this responseOption B: Return true total for pagination- event_list = events.list_recent(workspace, limit=limit, since_id=since_id)
+ event_list, total_count = events.list_recent_with_count(workspace, limit=limit, since_id=since_id)
return EventListResponse(
events=[...],
- total=len(event_list),
+ total=total_count,
)Also applies to: 71-71 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| def _get_workspace_from_path(workspace_path: str) -> Workspace: | ||
| """Get workspace from path, raising appropriate HTTP errors.""" | ||
| path = Path(workspace_path).resolve() | ||
|
|
||
| if not workspace_exists(path): | ||
| raise HTTPException( | ||
| status_code=404, | ||
| detail=f"Workspace not found at {path}. Initialize with 'cf init {path}'", | ||
| ) | ||
|
|
||
| workspace = get_workspace(path) | ||
| if not workspace: | ||
| raise HTTPException( | ||
| status_code=404, | ||
| detail=f"Workspace not found at {path}", | ||
| ) | ||
|
macroscopeapp[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| return workspace | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| @router.get("", response_model=EventListResponse) | ||
| async def list_events( | ||
| workspace_path: str = Query(..., description="Path to workspace directory"), | ||
| limit: int = Query(20, ge=1, le=100, description="Maximum events to return"), | ||
| since_id: Optional[int] = Query(None, description="Only return events after this ID"), | ||
| ): | ||
| """List recent events for a workspace. | ||
|
|
||
| Returns events in reverse chronological order (newest first). | ||
|
|
||
| Args: | ||
| workspace_path: Path to the workspace | ||
| limit: Maximum number of events (1-100, default 20) | ||
| since_id: Optional event ID for pagination | ||
|
|
||
| Returns: | ||
| List of events with total count | ||
| """ | ||
| workspace = _get_workspace_from_path(workspace_path) | ||
|
|
||
| event_list = events.list_recent(workspace, limit=limit, since_id=since_id) | ||
|
Comment on lines
+42
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Find the events module
find . -name "events.py" -o -name "events" -type d | head -20Repository: frankbria/codeframe Length of output: 89 🏁 Script executed: # Search for list_recent function definition
rg -n -B 2 -A 15 'def list_recent' --type pyRepository: frankbria/codeframe Length of output: 1020 🏁 Script executed: # Also check for async list_recent
rg -n -B 2 -A 15 'async def list_recent' --type pyRepository: frankbria/codeframe Length of output: 45 🏁 Script executed: # Get the full implementation of list_recent
sed -n '219,260p' codeframe/core/events.pyRepository: frankbria/codeframe Length of output: 1232 🏁 Script executed: # Check if get_db_connection is async or sync
rg -n -B 2 -A 10 'def get_db_connection' codeframe/core/events.py --type pyRepository: frankbria/codeframe Length of output: 45 🏁 Script executed: # Check the endpoint implementation more fully to see if there's any special handling
sed -n '35,75p' codeframe/ui/routers/events_v2.pyRepository: frankbria/codeframe Length of output: 1301 Move blocking database operation to async context or executor.
Either make event_list = await asyncio.get_event_loop().run_in_executor(
None, events.list_recent, workspace, limit, since_id
)Alternatively, refactor 🤖 Prompt for AI Agents |
||
|
|
||
| return EventListResponse( | ||
| events=[ | ||
| EventResponse( | ||
| id=e.id, | ||
| workspace_id=e.workspace_id, | ||
| event_type=e.event_type, | ||
| payload=e.payload, | ||
| created_at=e.created_at.isoformat(), | ||
| ) | ||
| for e in event_list | ||
| ], | ||
| total=len(event_list), | ||
| ) | ||
Uh oh!
There was an error while loading. Please reload this page.