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]},
    }