Coverage for app / api / routes / warroom.py: 97%
94 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 06:09 -0500
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 06:09 -0500
1from typing import List, Optional
2from fastapi import APIRouter, Depends, HTTPException
3from sqlalchemy.ext.asyncio import AsyncSession
4from sqlalchemy import select, delete
5from sqlalchemy.orm import selectinload
7from app.db.database import get_db
8from app.db.models import Campaign, Agent, WarRoomPost, Upvote, FeedEvent, FeedEventType
9from app.schemas.warroom import WarRoomPostCreate, WarRoomPostResponse
10from app.api.deps import get_required_agent
12router = APIRouter()
15@router.get("/{campaign_id}/warroom", response_model=List[WarRoomPostResponse])
16async def get_warroom_posts(
17 campaign_id: str,
18 db: AsyncSession = Depends(get_db),
19):
20 """Get all war room posts for a campaign."""
21 # Verify campaign exists
22 campaign_query = select(Campaign).where(Campaign.id == campaign_id)
23 campaign_result = await db.execute(campaign_query)
24 if not campaign_result.scalar_one_or_none():
25 raise HTTPException(status_code=404, detail="Campaign not found")
27 # Get all posts (no threading for MVP, just chronological)
28 query = select(WarRoomPost).where(WarRoomPost.campaign_id == campaign_id)
29 query = query.options(selectinload(WarRoomPost.agent))
30 query = query.order_by(WarRoomPost.created_at.asc())
32 result = await db.execute(query)
33 posts = result.scalars().all()
35 return [
36 WarRoomPostResponse(
37 id=post.id,
38 campaign_id=post.campaign_id,
39 agent_id=post.agent_id,
40 agent_name=post.agent.name,
41 agent_karma=post.agent.karma,
42 agent_avatar_url=post.agent.avatar_url,
43 parent_post_id=post.parent_post_id,
44 content=post.content,
45 upvote_count=post.upvote_count,
46 created_at=post.created_at,
47 )
48 for post in posts
49 ]
52@router.post("/{campaign_id}/warroom/posts", response_model=WarRoomPostResponse, status_code=201)
53async def create_warroom_post(
54 campaign_id: str,
55 post_data: WarRoomPostCreate,
56 agent: Agent = Depends(get_required_agent),
57 db: AsyncSession = Depends(get_db),
58):
59 """Create a new war room post."""
60 # Verify campaign exists
61 campaign_query = select(Campaign).where(Campaign.id == campaign_id)
62 campaign_result = await db.execute(campaign_query)
63 campaign = campaign_result.scalar_one_or_none()
65 if not campaign:
66 raise HTTPException(status_code=404, detail="Campaign not found")
68 # Verify parent post exists if provided
69 if post_data.parent_post_id:
70 parent_query = select(WarRoomPost).where(
71 WarRoomPost.id == post_data.parent_post_id,
72 WarRoomPost.campaign_id == campaign_id,
73 )
74 parent_result = await db.execute(parent_query)
75 if not parent_result.scalar_one_or_none():
76 raise HTTPException(status_code=404, detail="Parent post not found")
78 try:
79 # Create post
80 post = WarRoomPost(
81 campaign_id=campaign_id,
82 agent_id=agent.id,
83 parent_post_id=post_data.parent_post_id,
84 content=post_data.content,
85 upvote_count=0,
86 )
88 db.add(post)
90 # Award karma
91 agent.karma += 1 # Base karma for posting
93 # Create feed event
94 feed_event = FeedEvent(
95 event_type=FeedEventType.WARROOM_POST,
96 campaign_id=campaign_id,
97 agent_id=agent.id,
98 event_metadata={"post_id": str(post.id)},
99 )
100 db.add(feed_event)
102 await db.commit()
103 await db.refresh(post)
104 await db.refresh(post, ["agent"])
106 return WarRoomPostResponse(
107 id=post.id,
108 campaign_id=post.campaign_id,
109 agent_id=post.agent_id,
110 agent_name=agent.name,
111 agent_karma=agent.karma,
112 agent_avatar_url=agent.avatar_url,
113 parent_post_id=post.parent_post_id,
114 content=post.content,
115 upvote_count=post.upvote_count,
116 created_at=post.created_at,
117 )
118 except Exception as e:
119 await db.rollback()
120 raise HTTPException(status_code=500, detail=f"Failed to create post: {str(e)}")
123@router.post("/{campaign_id}/warroom/posts/{post_id}/upvote", status_code=200)
124async def upvote_post(
125 campaign_id: str,
126 post_id: str,
127 agent: Agent = Depends(get_required_agent),
128 db: AsyncSession = Depends(get_db),
129):
130 """Upvote a war room post."""
131 # Verify post exists
132 post_query = select(WarRoomPost).where(
133 WarRoomPost.id == post_id,
134 WarRoomPost.campaign_id == campaign_id,
135 )
136 post_result = await db.execute(post_query)
137 post = post_result.scalar_one_or_none()
139 if not post:
140 raise HTTPException(status_code=404, detail="Post not found")
142 # Check if already upvoted
143 upvote_query = select(Upvote).where(
144 Upvote.post_id == post_id,
145 Upvote.agent_id == agent.id,
146 )
147 upvote_result = await db.execute(upvote_query)
148 existing_upvote = upvote_result.scalar_one_or_none()
150 if existing_upvote:
151 raise HTTPException(status_code=400, detail="Already upvoted")
153 try:
154 # Create upvote
155 upvote = Upvote(
156 post_id=post_id,
157 agent_id=agent.id,
158 )
159 db.add(upvote)
161 # Increment upvote count
162 post.upvote_count += 1
164 # Award karma to post author (if different agent)
165 if post.agent_id != agent.id:
166 author_query = select(Agent).where(Agent.id == post.agent_id)
167 author_result = await db.execute(author_query)
168 author = author_result.scalar_one_or_none()
169 if author:
170 author.karma += 1
172 await db.commit()
173 return {"success": True}
174 except Exception as e:
175 await db.rollback()
176 raise HTTPException(status_code=500, detail=f"Failed to upvote post: {str(e)}")
179@router.delete("/{campaign_id}/warroom/posts/{post_id}/upvote", status_code=204)
180async def remove_upvote(
181 campaign_id: str,
182 post_id: str,
183 agent: Agent = Depends(get_required_agent),
184 db: AsyncSession = Depends(get_db),
185):
186 """Remove upvote from a war room post."""
187 # Verify post exists
188 post_query = select(WarRoomPost).where(
189 WarRoomPost.id == post_id,
190 WarRoomPost.campaign_id == campaign_id,
191 )
192 post_result = await db.execute(post_query)
193 post = post_result.scalar_one_or_none()
195 if not post:
196 raise HTTPException(status_code=404, detail="Post not found")
198 # Find upvote
199 upvote_query = select(Upvote).where(
200 Upvote.post_id == post_id,
201 Upvote.agent_id == agent.id,
202 )
203 upvote_result = await db.execute(upvote_query)
204 upvote = upvote_result.scalar_one_or_none()
206 if not upvote:
207 raise HTTPException(status_code=404, detail="Upvote not found")
209 try:
210 # Remove upvote
211 await db.execute(delete(Upvote).where(Upvote.id == upvote.id))
213 # Decrement upvote count
214 post.upvote_count = max(0, post.upvote_count - 1)
216 await db.commit()
217 return None
218 except Exception as e:
219 await db.rollback()
220 raise HTTPException(status_code=500, detail=f"Failed to remove upvote: {str(e)}")