- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
147 lines
4.7 KiB
Python
147 lines
4.7 KiB
Python
"""
|
|
Votes Repository — Database operations for votes
|
|
Phase 8: DAO Dashboard
|
|
"""
|
|
import uuid
|
|
from typing import List, Optional
|
|
from decimal import Decimal
|
|
import asyncpg
|
|
from models import VoteCreate, VoteRead
|
|
|
|
class VoteRepository:
|
|
def __init__(self, db_pool: asyncpg.Pool):
|
|
self.db = db_pool
|
|
|
|
async def create_or_update_vote(
|
|
self,
|
|
proposal_id: str,
|
|
voter_user_id: str,
|
|
vote_value: str,
|
|
weight: Decimal,
|
|
raw_power: Optional[Decimal] = None
|
|
) -> VoteRead:
|
|
"""Create or update vote (user can change their vote)"""
|
|
vote_id = uuid.uuid4()
|
|
|
|
row = await self.db.fetchrow(
|
|
"""
|
|
INSERT INTO dao_votes (id, proposal_id, voter_user_id, vote_value, weight, raw_power)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
ON CONFLICT (proposal_id, voter_user_id) DO UPDATE
|
|
SET vote_value = EXCLUDED.vote_value,
|
|
weight = EXCLUDED.weight,
|
|
raw_power = EXCLUDED.raw_power,
|
|
created_at = NOW()
|
|
RETURNING id, proposal_id, voter_user_id, vote_value, weight, raw_power, created_at
|
|
""",
|
|
vote_id,
|
|
uuid.UUID(proposal_id),
|
|
uuid.UUID(voter_user_id),
|
|
vote_value,
|
|
weight,
|
|
raw_power
|
|
)
|
|
|
|
return self._row_to_vote(row)
|
|
|
|
async def get_vote(
|
|
self,
|
|
proposal_id: str,
|
|
voter_user_id: str
|
|
) -> Optional[VoteRead]:
|
|
"""Get user's vote on a proposal"""
|
|
row = await self.db.fetchrow(
|
|
"""
|
|
SELECT id, proposal_id, voter_user_id, vote_value, weight, raw_power, created_at
|
|
FROM dao_votes
|
|
WHERE proposal_id = $1 AND voter_user_id = $2
|
|
""",
|
|
uuid.UUID(proposal_id),
|
|
uuid.UUID(voter_user_id)
|
|
)
|
|
|
|
if not row:
|
|
return None
|
|
|
|
return self._row_to_vote(row)
|
|
|
|
async def list_votes_for_proposal(
|
|
self,
|
|
proposal_id: str
|
|
) -> List[VoteRead]:
|
|
"""List all votes for a proposal"""
|
|
rows = await self.db.fetch(
|
|
"""
|
|
SELECT id, proposal_id, voter_user_id, vote_value, weight, raw_power, created_at
|
|
FROM dao_votes
|
|
WHERE proposal_id = $1
|
|
ORDER BY created_at DESC
|
|
""",
|
|
uuid.UUID(proposal_id)
|
|
)
|
|
|
|
return [self._row_to_vote(row) for row in rows]
|
|
|
|
async def count_votes_by_value(
|
|
self,
|
|
proposal_id: str
|
|
) -> dict:
|
|
"""Count votes by value (yes/no/abstain)"""
|
|
row = await self.db.fetchrow(
|
|
"""
|
|
SELECT
|
|
COUNT(CASE WHEN vote_value = 'yes' THEN 1 END) as yes_count,
|
|
COUNT(CASE WHEN vote_value = 'no' THEN 1 END) as no_count,
|
|
COUNT(CASE WHEN vote_value = 'abstain' THEN 1 END) as abstain_count,
|
|
COUNT(*) as total_count
|
|
FROM dao_votes
|
|
WHERE proposal_id = $1
|
|
""",
|
|
uuid.UUID(proposal_id)
|
|
)
|
|
|
|
return {
|
|
'yes': row['yes_count'] or 0,
|
|
'no': row['no_count'] or 0,
|
|
'abstain': row['abstain_count'] or 0,
|
|
'total': row['total_count'] or 0
|
|
}
|
|
|
|
async def get_vote_weights_sum(
|
|
self,
|
|
proposal_id: str
|
|
) -> dict:
|
|
"""Get sum of weights by vote value"""
|
|
row = await self.db.fetchrow(
|
|
"""
|
|
SELECT
|
|
COALESCE(SUM(CASE WHEN vote_value = 'yes' THEN weight ELSE 0 END), 0) as yes_weight,
|
|
COALESCE(SUM(CASE WHEN vote_value = 'no' THEN weight ELSE 0 END), 0) as no_weight,
|
|
COALESCE(SUM(CASE WHEN vote_value = 'abstain' THEN weight ELSE 0 END), 0) as abstain_weight,
|
|
COALESCE(SUM(weight), 0) as total_weight
|
|
FROM dao_votes
|
|
WHERE proposal_id = $1
|
|
""",
|
|
uuid.UUID(proposal_id)
|
|
)
|
|
|
|
return {
|
|
'yes': Decimal(str(row['yes_weight'] or 0)),
|
|
'no': Decimal(str(row['no_weight'] or 0)),
|
|
'abstain': Decimal(str(row['abstain_weight'] or 0)),
|
|
'total': Decimal(str(row['total_weight'] or 0))
|
|
}
|
|
|
|
def _row_to_vote(self, row: asyncpg.Record) -> VoteRead:
|
|
"""Convert database row to VoteRead"""
|
|
return VoteRead(
|
|
id=str(row['id']),
|
|
proposal_id=str(row['proposal_id']),
|
|
voter_user_id=str(row['voter_user_id']),
|
|
vote_value=row['vote_value'],
|
|
weight=row['weight'],
|
|
raw_power=row['raw_power'],
|
|
created_at=row['created_at']
|
|
)
|
|
|