Spaces:
Running
Running
File size: 6,279 Bytes
0a92159 ece07d3 0a92159 ece07d3 0a92159 1bb73f0 0a92159 dc6f3a2 0a92159 dc6f3a2 0a92159 dd482d8 34e21dd 0a92159 34e21dd 0a92159 ece07d3 34e21dd ece07d3 34e21dd ece07d3 0a92159 653d14b 34e21dd 653d14b 34e21dd 653d14b ece07d3 0a92159 ece07d3 0a92159 dd482d8 0a92159 a65f71f 0a92159 a65f71f 0a92159 a65f71f 0a92159 a65f71f 0a92159 9ccd94a 0a92159 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | 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]},
} |