1- from typing import Optional , Sequence
1+ from datetime import datetime
2+ from typing import List , Optional
23
34from pydantic import BaseModel , ConfigDict , Field
45
6+ from spark_history_mcp .models .spark_types import JobData , StageData
7+
58
69class JobSummary (BaseModel ):
7- """Summary of job execution counts for a SQL query."""
10+ job_id : Optional [int ] = Field (None , alias = "jobId" )
11+ name : str
12+ description : Optional [str ] = None
13+ status : str
14+ submission_time : Optional [datetime ] = Field (None , alias = "submissionTime" )
15+ completion_time : Optional [datetime ] = Field (None , alias = "completionTime" )
16+ duration_seconds : Optional [float ] = None
17+ succeeded_stage_ids : List [int ] = Field (
18+ default_factory = list , alias = "succeededStageIds"
19+ )
20+ failed_stage_ids : List [int ] = Field (default_factory = list , alias = "failedStageIds" )
21+ active_stage_ids : List [int ] = Field (default_factory = list , alias = "activeStageIds" )
22+ pending_stage_ids : List [int ] = Field (default_factory = list , alias = "pendingStageIds" )
23+ skipped_stage_ids : List [int ] = Field (default_factory = list , alias = "skippedStageIds" )
824
9- success_job_ids : Sequence [int ] = Field (..., alias = "successJobsIds" )
10- failed_job_ids : Sequence [int ] = Field (..., alias = "failedJobsIds" )
11- running_job_ids : Sequence [int ] = Field (..., alias = "runningJobsIds" )
25+ model_config = ConfigDict (populate_by_name = True , arbitrary_types_allowed = True )
1226
13- model_config = ConfigDict (populate_by_name = True )
27+ @classmethod
28+ def parse_datetime (cls , value ):
29+ if value is None :
30+ return None
31+ if isinstance (value , (int , float )):
32+ return datetime .fromtimestamp (value / 1000 )
33+ if isinstance (value , str ) and value .endswith ("GMT" ):
34+ try :
35+ dt_str = value .replace ("GMT" , "+0000" )
36+ return datetime .strptime (dt_str , "%Y-%m-%dT%H:%M:%S.%f%z" )
37+ except ValueError :
38+ pass
39+ return value
40+
41+ @classmethod
42+ def from_job_data (
43+ cls , job_data : JobData , stages : List [StageData ] = None
44+ ) -> "JobSummary" :
45+ """Create a JobSummary from full JobData and optional stage data."""
46+ duration = None
47+ if job_data .completion_time and job_data .submission_time :
48+ duration = (
49+ job_data .completion_time - job_data .submission_time
50+ ).total_seconds ()
51+
52+ # Initialize stage ID lists
53+ succeeded_stage_ids = []
54+ failed_stage_ids = []
55+ active_stage_ids = []
56+ pending_stage_ids = []
57+ skipped_stage_ids = []
58+
59+ # Group stage IDs by status if stage data is provided
60+ if stages and job_data .stage_ids :
61+ stage_status_map = {stage .stage_id : stage .status for stage in stages }
62+
63+ for stage_id in job_data .stage_ids :
64+ stage_status = stage_status_map .get (stage_id , "UNKNOWN" )
65+ if stage_status == "COMPLETE" :
66+ succeeded_stage_ids .append (stage_id )
67+ elif stage_status == "FAILED" :
68+ failed_stage_ids .append (stage_id )
69+ elif stage_status == "ACTIVE" :
70+ active_stage_ids .append (stage_id )
71+ elif stage_status == "PENDING" :
72+ pending_stage_ids .append (stage_id )
73+ elif stage_status == "SKIPPED" :
74+ skipped_stage_ids .append (stage_id )
75+
76+ return cls (
77+ job_id = job_data .job_id ,
78+ name = job_data .name ,
79+ description = job_data .description ,
80+ status = job_data .status ,
81+ submission_time = job_data .submission_time ,
82+ completion_time = job_data .completion_time ,
83+ duration_seconds = duration ,
84+ succeeded_stage_ids = succeeded_stage_ids ,
85+ failed_stage_ids = failed_stage_ids ,
86+ active_stage_ids = active_stage_ids ,
87+ pending_stage_ids = pending_stage_ids ,
88+ skipped_stage_ids = skipped_stage_ids ,
89+ )
1490
1591
1692class SqlQuerySummary (BaseModel ):
@@ -22,6 +98,8 @@ class SqlQuerySummary(BaseModel):
2298 status : str
2399 submission_time : Optional [str ] = Field (None , alias = "submissionTime" )
24100 plan_description : str = Field (..., alias = "planDescription" )
25- job_summary : JobSummary = Field (..., alias = "jobSummary" )
101+ success_job_ids : List [int ] = Field (..., alias = "successJobIds" )
102+ failed_job_ids : List [int ] = Field (..., alias = "failedJobIds" )
103+ running_job_ids : List [int ] = Field (..., alias = "runningJobIds" )
26104
27105 model_config = ConfigDict (populate_by_name = True )
0 commit comments