|
import uuid |
|
from datetime import datetime |
|
from typing import List, Optional |
|
from core.memory import get_redis_client |
|
from .models import GoalCreate, GoalUpdate, GoalInDB |
|
|
|
def _get_redis_key(user_id: str, goal_id: str) -> str: |
|
return f"user:{user_id}:goals:{goal_id}" |
|
|
|
def _get_index_key(user_id: str) -> str: |
|
return f"user:{user_id}:goals:index" |
|
|
|
def create_goal(user_id: str, goal_data: GoalCreate) -> GoalInDB: |
|
redis_client = get_redis_client() |
|
if not redis_client: |
|
raise RuntimeError("Redis connection unavailable.") |
|
|
|
goal_id = str(uuid.uuid4()) |
|
now = datetime.utcnow() |
|
goal_dict = { |
|
"id": goal_id, |
|
"user_id": user_id, |
|
"title": goal_data.title, |
|
"description": goal_data.description or "", |
|
"target_date": goal_data.target_date.isoformat() if goal_data.target_date else "", |
|
"created_at": now.isoformat(), |
|
"updated_at": now.isoformat() |
|
} |
|
|
|
redis_client.hset(_get_redis_key(user_id, goal_id), mapping=goal_dict) |
|
redis_client.zadd(_get_index_key(user_id), {goal_id: now.timestamp()}) |
|
|
|
return GoalInDB(**goal_dict) |
|
|
|
def get_goals(user_id: str) -> List[GoalInDB]: |
|
redis_client = get_redis_client() |
|
if not redis_client: |
|
return [] |
|
|
|
goal_ids = redis_client.zrange(_get_index_key(user_id), 0, -1) |
|
goals = [] |
|
|
|
for gid in goal_ids: |
|
raw_goal = redis_client.hgetall(_get_redis_key(user_id, gid)) |
|
if raw_goal: |
|
|
|
raw_goal["target_date"] = datetime.fromisoformat(raw_goal["target_date"]) if raw_goal["target_date"] else None |
|
raw_goal["created_at"] = datetime.fromisoformat(raw_goal["created_at"]) |
|
raw_goal["updated_at"] = datetime.fromisoformat(raw_goal["updated_at"]) |
|
goals.append(GoalInDB(**raw_goal)) |
|
|
|
return goals |
|
|
|
def get_goal_by_id(user_id: str, goal_id: str) -> Optional[GoalInDB]: |
|
redis_client = get_redis_client() |
|
if not redis_client: |
|
return None |
|
|
|
raw_goal = redis_client.hgetall(_get_redis_key(user_id, goal_id)) |
|
if not raw_goal: |
|
return None |
|
|
|
raw_goal["target_date"] = datetime.fromisoformat(raw_goal["target_date"]) if raw_goal["target_date"] else None |
|
raw_goal["created_at"] = datetime.fromisoformat(raw_goal["created_at"]) |
|
raw_goal["updated_at"] = datetime.fromisoformat(raw_goal["updated_at"]) |
|
|
|
return GoalInDB(**raw_goal) |
|
|
|
def update_goal(user_id: str, goal_id: str, updates: GoalUpdate) -> Optional[GoalInDB]: |
|
redis_client = get_redis_client() |
|
if not redis_client: |
|
return None |
|
|
|
raw_goal = redis_client.hgetall(_get_redis_key(user_id, goal_id)) |
|
if not raw_goal: |
|
return None |
|
|
|
|
|
update_data = updates.dict(exclude_unset=True) |
|
for field, value in update_data.items(): |
|
if isinstance(value, datetime): |
|
raw_goal[field] = value.isoformat() |
|
else: |
|
raw_goal[field] = value |
|
|
|
raw_goal["updated_at"] = datetime.utcnow().isoformat() |
|
|
|
redis_client.hset(_get_redis_key(user_id, goal_id), mapping=raw_goal) |
|
|
|
parsed_goal = raw_goal.copy() |
|
parsed_goal["target_date"] = datetime.fromisoformat(parsed_goal["target_date"]) if parsed_goal["target_date"] else None |
|
parsed_goal["created_at"] = datetime.fromisoformat(parsed_goal["created_at"]) |
|
parsed_goal["updated_at"] = datetime.fromisoformat(parsed_goal["updated_at"]) |
|
|
|
return GoalInDB(**parsed_goal) |
|
|
|
def delete_goal(user_id: str, goal_id: str) -> bool: |
|
redis_client = get_redis_client() |
|
if not redis_client: |
|
return False |
|
|
|
pipe = redis_client.pipeline() |
|
pipe.delete(_get_redis_key(user_id, goal_id)) |
|
pipe.zrem(_get_index_key(user_id), goal_id) |
|
results = pipe.execute() |
|
|
|
return results[0] > 0 |
|
|