Spaces:
Running
Running
| from __future__ import annotations | |
| import json | |
| from datetime import datetime, time, timedelta | |
| from typing import Any, Optional | |
| from pydantic import ValidationError | |
| from sqlalchemy.ext.asyncio import AsyncSession | |
| from sqlmodel import select | |
| from src.utils.sleep import calculate_sleep_duration | |
| from ..database.models import ActivityEntry, DayLog, Goal, User | |
| from ..schemas.activity import activity_create | |
| from ..schemas.daylog import daylog_create | |
| from ..schemas.goal import goal_create | |
| from .exceptions import CommandError | |
| from .command_validation import parse_command_text, build_typed_payload, validate_required_fields | |
| async def create_goal_service(payload: goal_create, current_user: User, db: AsyncSession) -> dict[str, Any]: | |
| statement = select(Goal).where((Goal.user_id == current_user.id) & (Goal.title == payload.title)) | |
| result = await db.execute(statement) | |
| existing_goal = result.scalar_one_or_none() | |
| if existing_goal: | |
| raise CommandError(400, "business_rule_violation", "A goal with the same name already exists.") | |
| new_goal = Goal( | |
| user_id=current_user.id, | |
| title=payload.title, | |
| description=payload.description, | |
| total_productivity_hours=payload.total_productivity_hours, | |
| active_status=payload.active_status, | |
| date=payload.date | |
| ) | |
| db.add(new_goal) | |
| await db.commit() | |
| await db.refresh(new_goal) | |
| return {"message": "Goal created successfully", "goal": new_goal.model_dump()} | |
| async def create_daylog_service(payload: daylog_create, current_user: User, db: AsyncSession) -> dict[str, Any]: | |
| statement = select(DayLog).where((DayLog.user_id == current_user.id) & (DayLog.date == payload.date)) | |
| result = await db.execute(statement) | |
| existing_log = result.scalar_one_or_none() | |
| if existing_log: | |
| raise CommandError(400, "business_rule_violation", "A day log for this date already exists.") | |
| if payload.wake_time: | |
| # 1. Fetch Yesterday's Log | |
| yesterday_date = payload.date - timedelta(days=1) | |
| stmt = select(DayLog).where( | |
| DayLog.date == yesterday_date, | |
| DayLog.user_id == current_user.id | |
| ) | |
| result = await db.execute(stmt) | |
| yesterday_log = result.scalar_one_or_none() | |
| if yesterday_log.sleep_time: | |
| payload.sleep_duration_hours = calculate_sleep_duration( | |
| yesterday_date=yesterday_log.date, | |
| yesterday_sleep=yesterday_log.sleep_time, | |
| today_date=payload.date, | |
| today_wake=payload.wake_time | |
| ) | |
| # 3. Proceed with standard creation... | |
| db_daylog = DayLog(**payload.model_dump(), user_id=current_user.id) | |
| db.add(db_daylog) | |
| await db.commit() | |
| await db.refresh(db_daylog) | |
| return {"message": "Day log created successfully", | |
| "daylog_id": db_daylog.id, | |
| "daylog_data": db_daylog} | |
| def build_help_text(intent: Optional[str] = None) -> dict[str, Any]: | |
| guides = { | |
| None: ( | |
| "Use /goal, /daylog, /activity, or /help. Values with spaces must be quoted. " | |
| "Examples : " | |
| "/goal --name --description --status or -n -desc -s" | |
| "/daylog --date --bed --wake --sleep --sleephr --totalcal --totalprod --note or -d -b -w -s -sh -tc -tp -n" | |
| "/activity --goal --date --name --type --status --time --min --place --quan --what --result --note or -g -d -n -t -s -ti -m -p -q -w -r -no -de" | |
| ), | |
| "goal": ( | |
| "/goal --name --description --status" | |
| "OR -n -desc -s" | |
| ), | |
| "daylog": ( | |
| "/daylog --date YYYY-MM-DD --bed HH:MM:SS --wake HH:MM:SS --note --sleep --sleephr --totalcal --totalprod" | |
| "OR -d, -b, -w, -s, -sh, -tc, -tp, -n" | |
| ), | |
| "activity": ( | |
| "/activity --goal --date YYYY-MM-DD --name --type --status --time HH:MM:SS --min --place --quan --what --result --note details" | |
| "OR -g, -d, -n, -t, -s, -ti, -m, -p, -q, -w, -r, -no, -de" | |
| ), | |
| } | |
| if intent not in guides: | |
| raise CommandError(400, "unknown_intent", f"Unknown help topic '{intent}'.") | |
| return {"message": "Help guide", "guide": guides[intent]} | |
| async def dispatch_command(command_text: str, current_user: User, db: AsyncSession) -> dict[str, Any]: | |
| intent, help_topic, raw_values = parse_command_text(command_text) | |
| if intent == "help": | |
| guide = build_help_text(help_topic) | |
| return {"success": True, "intent": "help", "data": guide} | |
| typed_values = build_typed_payload(intent, raw_values) | |
| validate_required_fields(intent, typed_values) | |
| try: | |
| if intent == "goal": | |
| payload = goal_create.model_validate(typed_values) | |
| result = await create_goal_service(payload, current_user, db) | |
| return {"success": True, "intent": "goal", "data": result} | |
| if intent == "daylog": | |
| payload = daylog_create.model_validate(typed_values) | |
| result = await create_daylog_service(payload, current_user, db) | |
| return {"success": True, "intent": "daylog", "data": result} | |
| if intent == "activity": | |
| payload = activity_create.model_validate(typed_values) | |
| result = await create_activity_service(payload, current_user, db) | |
| return {"success": True, "intent": "activity", "data": result} | |
| raise CommandError(400, "unknown_intent", f"Unknown intent '{intent}'.") | |
| except ValidationError as exc: | |
| raise CommandError(400, "invalid_field_type", "Command data does not match the expected schema.", exc.errors()) from exc | |
| def command_error_payload(exc: CommandError) -> dict[str, Any]: | |
| return { | |
| "success": False, | |
| "error_code": exc.error_code, | |
| "message": exc.message, | |
| "details": exc.details, | |
| } | |
| async def list_user_activities(current_user: User, db: AsyncSession) -> dict[str, Any]: | |
| statement = select(ActivityEntry).where(ActivityEntry.user_id == current_user.id) | |
| result = await db.execute(statement) | |
| activities = result.scalars().all() | |
| return { | |
| "success": True, | |
| "intent": "activity", | |
| "data": {"activities": [activity.model_dump() for activity in activities]}, | |
| } |