File size: 3,701 Bytes
3b9a6b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
from dataclasses import dataclass
import uuid
import time
import asyncio

from utils.extract_calendar import extract_ical_entries
from factory.data_provider import generate_mcp_data
from services.schedule_service import ScheduleService


@dataclass
class MCPProcessingResult:
    user_message: str
    file: str
    calendar_entries: list = None
    error: str = None
    solved_task_df: object = None
    status: str = None
    score: object = None


async def process_message_and_attached_file(file_path: str, message_body: str) -> dict:
    """
    Store the last chat message and attached file, echo the message, extract calendar entries, generate tasks, solve, and poll for the solution.
    Args:
        file_path (str): Path to the attached file
        message_body (str): The body of the last chat message, which contains the task description
    Returns:
        dict: Contains confirmation, file info, calendar entries, error, and solved schedule info
    """
    try:
        with open(file_path, "rb") as f:
            file_bytes = f.read()
    except Exception as e:
        result = MCPProcessingResult(
            user_message="",
            file="",
            calendar_entries=[],
            error=f"Failed to read file: {e}",
        )
        return result.__dict__

    # Try to extract calendar entries
    entries, error = extract_ical_entries(file_bytes)
    if error:
        result = MCPProcessingResult(
            user_message=f"Received your message: {message_body}",
            file=os.path.basename(file_path),
            error=f"File is not a valid calendar file: {error}",
        )
        return result.__dict__

    # Generate MCP DataFrame
    df = await generate_mcp_data(entries, message_body)
    if df is None or df.empty:
        result = MCPProcessingResult(
            user_message=f"Received your message: {message_body}",
            file=os.path.basename(file_path),
            calendar_entries=entries,
            error="Failed to generate MCP data.",
        )
        return result.__dict__

    # Build state_data for the solver
    state_data = {
        "task_df_json": df.to_json(orient="split"),
        "employee_count": 1,
        "days_in_schedule": 365,
    }
    job_id = str(uuid.uuid4())
    (
        emp_df,
        solved_task_df,
        new_job_id,
        status,
        state_data,
    ) = await ScheduleService.solve_schedule_from_state(state_data, job_id, debug=True)

    # Poll for the solution until the status string does not contain 'Solving'
    max_wait = 30  # seconds
    interval = 0.5
    waited = 0
    final_task_df = None
    final_status = None
    final_score = None
    solved = False
    while waited < max_wait:
        (
            _,
            polled_task_df,
            _,
            polled_status,
            solved_schedule,
        ) = ScheduleService.poll_solution(new_job_id, None, debug=True)
        if polled_status and "Solving" not in polled_status:
            final_task_df = polled_task_df
            final_status = polled_status
            final_score = getattr(solved_schedule, "score", None)
            solved = True
            break
        await asyncio.sleep(interval)
        waited += interval

    result = MCPProcessingResult(
        user_message=f"Received your message: {message_body}",
        file=os.path.basename(file_path),
        calendar_entries=entries,
        solved_task_df=final_task_df.to_dict(orient="records")
        if final_task_df is not None
        else None,
        status=final_status,
        score=final_score,
        error=None if solved else "Solver did not finish within the timeout",
    )
    return result.__dict__