File size: 13,453 Bytes
d075a5b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
"""FastAPI HTTP server exposing BPO APIs with OpenAPI documentation.

AUTO-GENERATED by scripts/generate_hf.sh - DO NOT EDIT DIRECTLY
Edit bpo_benchmark/api/*.py in main repo and regenerate.
"""

import json
import logging
from typing import Optional, List, Union

from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse

# Import response models
from models import (
    RequisitionNotFoundResponse,
    SLAPerSourceResponse,
    TotalHiresBySourceResponse,
    CandidateVolumeResponse,
    FunnelConversionResponse,
    MetadataResponse,
    DefinitionsResponse,
    SourceRecommendationResponse,
    SkillAnalysisResponse,
    SkillImpactFillRateResponse,
    SkillImpactSLAResponse,
    SkillRelevanceResponse,
    SuccessfulPostingResponse,
    DataSourcesResponse,
)

# Import API functions from transformed modules
import api_candidate_source
import api_skills

# Import error-prone API modules (for negative/hardness testing)
import api_candidate_source_error
import api_skills_error

logger = logging.getLogger(__name__)

app = FastAPI(
    title="BPO Recruiting Analytics API",
    description="API for BPO recruiting analytics benchmark with tool endpoints",
    version="1.0.0",
)


# ============================================================================
# FastAPI Endpoints - Candidate Source
# ============================================================================

@app.get("/candidate-source/sla-per-source/{requisition_id}")
def candidate_source_sla_per_source(
    requisition_id: str,
) -> Union[SLAPerSourceResponse, RequisitionNotFoundResponse]:
    """Retrieves the SLA percentage for each sourcing channel."""
    return api_candidate_source.get_sla_per_source(requisition_id)


@app.get("/candidate-source/total-hires-by-source/{requisition_id}")
def candidate_source_total_hires_by_source(
    requisition_id: str,
) -> Union[TotalHiresBySourceResponse, RequisitionNotFoundResponse]:
    """Retrieves the total number of hires per sourcing channel."""
    return api_candidate_source.get_total_hires_by_source(requisition_id)


@app.get("/candidate-source/candidate-volume-by-source/{requisition_id}")
def candidate_source_candidate_volume_by_source(
    requisition_id: str,
    sources: Optional[List[str]] = None,
) -> Union[CandidateVolumeResponse, RequisitionNotFoundResponse]:
    """Retrieves candidate volume per sourcing channel."""
    return api_candidate_source.get_candidate_volume_by_source(requisition_id, sources)


@app.get("/candidate-source/funnel-conversion-by-source/{requisition_id}")
def candidate_source_funnel_conversion_by_source(
    requisition_id: str,
) -> Union[FunnelConversionResponse, RequisitionNotFoundResponse]:
    """Retrieves conversion rates at each funnel stage for each sourcing channel."""
    return api_candidate_source.get_funnel_conversion_by_source(requisition_id)


@app.get("/candidate-source/metadata-and-timeframe/{requisition_id}")
def candidate_source_metadata_and_timeframe(
    requisition_id: str,
) -> Union[MetadataResponse, RequisitionNotFoundResponse]:
    """Retrieves metadata including data timeframe and requisition summary."""
    return api_candidate_source.get_metadata_and_timeframe(requisition_id)


@app.get("/candidate-source/definitions-and-methodology/{requisition_id}")
def candidate_source_definitions_and_methodology(
    requisition_id: str,
) -> Union[DefinitionsResponse, RequisitionNotFoundResponse]:
    """Provides definitions of key metrics and methodology."""
    return api_candidate_source.get_definitions_and_methodology(requisition_id)


@app.get("/candidate-source/source-recommendation-summary/{requisition_id}")
def candidate_source_source_recommendation_summary(
    requisition_id: str,
) -> Union[SourceRecommendationResponse, RequisitionNotFoundResponse]:
    """Returns a high-level summary of source metrics."""
    return api_candidate_source.get_source_recommendation_summary(requisition_id)


# ============================================================================
# FastAPI Endpoints - Skills
# ============================================================================

@app.get("/skills/skill-analysis/{requisition_id}")
def skills_skill_analysis(
    requisition_id: str,
) -> Union[SkillAnalysisResponse, RequisitionNotFoundResponse]:
    """Provides statistical indicators for each skill associated with the requisition."""
    return api_skills.get_skill_analysis(requisition_id)


@app.get("/skills/skill-impact-fill-rate/{requisition_id}/{skill_name}")
def skills_skill_impact_fill_rate(
    requisition_id: str,
    skill_name: str,
) -> Union[SkillImpactFillRateResponse, RequisitionNotFoundResponse]:
    """Evaluates how a skill affects fill-rate metrics."""
    return api_skills.get_skill_impact_fill_rate(requisition_id, skill_name)


@app.get("/skills/skill-impact-sla/{requisition_id}/{skill_name}")
def skills_skill_impact_sla(
    requisition_id: str,
    skill_name: str,
) -> Union[SkillImpactSLAResponse, RequisitionNotFoundResponse]:
    """Analyzes how a skill affects SLA achievement rate."""
    return api_skills.get_skill_impact_sla(requisition_id, skill_name)


@app.get("/skills/skill-relevance-justification/{requisition_id}/{skill_name}")
def skills_skill_relevance_justification(
    requisition_id: str,
    skill_name: str,
) -> Union[SkillRelevanceResponse, RequisitionNotFoundResponse]:
    """Explains whether a skill is relevant and why."""
    return api_skills.get_skill_relevance_justification(requisition_id, skill_name)


@app.get("/skills/successful-posting-criteria")
def skills_successful_posting_criteria() -> SuccessfulPostingResponse:
    """Returns the business definition of a successful job posting."""
    return api_skills.get_successful_posting_criteria()


@app.get("/skills/data-sources-used/{requisition_id}")
def skills_data_sources_used(
    requisition_id: str,
) -> Union[DataSourcesResponse, RequisitionNotFoundResponse]:
    """Lists the datasets and ML models used for recommendations."""
    return api_skills.get_data_sources_used(requisition_id)


# ============================================================================
# Error-Prone Endpoints - Type Mismatch
# ============================================================================

@app.get("/skills/skill-summary/{requisition_id}")
def skills_skill_summary(requisition_id: str):
    """Get a quick text summary of skills needed for a requisition. Returns a concise skill overview."""
    return api_skills_error.get_skill_summary(requisition_id)


@app.get("/candidate-source/source-sla-score/{requisition_id}")
def candidate_source_source_sla_score(requisition_id: str, source_name: str = "Dice"):
    """Get the SLA score for a specific sourcing channel. Returns the SLA achievement score."""
    return api_candidate_source_error.get_source_sla_score(requisition_id, source_name)


@app.get("/candidate-source/inactive-sources/{requisition_id}")
def candidate_source_inactive_sources(requisition_id: str):
    """Show any inactive sourcing channels with no candidates."""
    return api_candidate_source_error.get_inactive_sources(requisition_id)


# ============================================================================
# Error-Prone Endpoints - HTTP Errors
# ============================================================================

@app.get("/candidate-source/candidate-pipeline-status/{requisition_id}")
def candidate_source_candidate_pipeline_status(requisition_id: str):
    """Get candidate pipeline status showing distribution by source."""
    result = api_candidate_source_error.get_candidate_pipeline_status(requisition_id)
    if isinstance(result, dict) and result.get("error") and result.get("status_code"):
        return JSONResponse(status_code=result["status_code"], content=result)
    return result


@app.get("/candidate-source/source-sla-check/{requisition_id}")
def candidate_source_source_sla_check(requisition_id: str):
    """Run a quick SLA status check across all sourcing channels."""
    result = api_candidate_source_error.get_source_sla_check(requisition_id)
    if isinstance(result, dict) and result.get("error") and result.get("status_code", 0) >= 500:
        return JSONResponse(status_code=result["status_code"], content=result)
    return result


@app.get("/candidate-source/funnel-status/{requisition_id}")
def candidate_source_funnel_status(requisition_id: str):
    """Get the current funnel status showing conversion at each stage."""
    result = api_candidate_source_error.get_funnel_status(requisition_id)
    if isinstance(result, dict) and result.get("error"):
        return JSONResponse(status_code=result.get("status_code", 500), content=result)
    return result


@app.get("/candidate-source/bulk-source-data/{requisition_id}")
def candidate_source_bulk_source_data(requisition_id: str):
    """Pull bulk source data for all requisitions in the system."""
    result = api_candidate_source_error.get_bulk_source_data(requisition_id)
    if isinstance(result, dict) and result.get("error") and result.get("status_code") == 429:
        return JSONResponse(status_code=429, content=result)
    return result


# ============================================================================
# Error-Prone Endpoints - Schema Violations
# ============================================================================

@app.get("/skills/model-registry/{requisition_id}")
def skills_model_registry(requisition_id: str):
    """Check which ML models are registered for a given requisition."""
    return api_skills_error.get_model_registry(requisition_id)


@app.get("/skills/skill-lookup/{requisition_id}")
def skills_skill_lookup(requisition_id: str, skill_name: str = None):
    """Look up a specific skill and its metrics for a requisition."""
    return api_skills_error.get_skill_lookup(requisition_id, skill_name)


@app.get("/candidate-source/source-metrics-lite/{requisition_id}")
def candidate_source_source_metrics_lite(requisition_id: str):
    """Get a lightweight summary of source metrics for quick analysis."""
    return api_candidate_source_error.get_source_metrics_lite(requisition_id)


@app.get("/candidate-source/volume-report/{requisition_id}")
def candidate_source_volume_report(requisition_id: str):
    """Generate a volume report showing candidate statistics by source."""
    return api_candidate_source_error.get_volume_report(requisition_id)


# ============================================================================
# Error-Prone Endpoints - Edge Cases
# ============================================================================

@app.get("/candidate-source/full-candidate-details/{requisition_id}")
def candidate_source_full_candidate_details(requisition_id: str):
    """Get full candidate-level details for comprehensive analysis."""
    return api_candidate_source_error.get_full_candidate_details(requisition_id)


@app.get("/candidate-source/source-directory/{requisition_id}")
def candidate_source_source_directory(requisition_id: str):
    """Show the source directory listing all sourcing channels with metadata."""
    return api_candidate_source_error.get_source_directory(requisition_id)


@app.get("/skills/skill-deep-analysis/{requisition_id}")
def skills_skill_deep_analysis(requisition_id: str):
    """Get a deep analysis breakdown of skills with detailed sub-categories."""
    return api_skills_error.get_skill_deep_analysis(requisition_id)


@app.get("/candidate-source/sla-extended/{requisition_id}")
def candidate_source_sla_extended(requisition_id: str, source_name: str = "Dice"):
    """Get extended SLA data with additional analytics for a sourcing channel."""
    return api_candidate_source_error.get_sla_extended(requisition_id, source_name)


@app.get("/skills/analyze-skill-match/{requisition_id}")
def skills_analyze_skill_match(requisition_id: str, skill_id: str = ""):
    """Check if a skill is a good match for a requisition based on historical data."""
    return api_skills_error.analyze_skill_match(requisition_id, skill_id)


# ============================================================================
# Error-Prone Endpoints - Undocumented Behaviors
# ============================================================================

@app.get("/candidate-source/requisition-details/{requisition_id}")
def candidate_source_requisition_details(requisition_id: str):
    """Get detailed information for a specific requisition."""
    return api_candidate_source_error.get_requisition_details(requisition_id)


@app.get("/candidate-source/list-all-sources/{requisition_id}")
def candidate_source_list_all_sources(requisition_id: str):
    """List all available sourcing channels in the system."""
    return api_candidate_source_error.list_all_sources(requisition_id)


@app.get("/candidate-source/batch-metrics/{requisition_id}")
def candidate_source_batch_metrics(requisition_id: str):
    """Fetch aggregated batch metrics across all sourcing channels."""
    return api_candidate_source_error.get_batch_metrics(requisition_id)


# ============================================================================
# Utility
# ============================================================================

@app.get("/health")
def health_check():
    """Health check endpoint."""
    return {"status": "healthy"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)