|
|
import asyncio |
|
|
import json |
|
|
from pathlib import Path |
|
|
from typing import Any |
|
|
|
|
|
from pydantic import BaseModel |
|
|
|
|
|
from vsp.app.main import VspDataEnrichment |
|
|
from vsp.app.model.linkedin.linkedin_models import LinkedinProfile |
|
|
from vsp.app.model.vsp.vsp_models import VSPProfile |
|
|
from vsp.shared import logger_factory |
|
|
|
|
|
logger = logger_factory.get_logger(__name__) |
|
|
|
|
|
|
|
|
class ComparisonResult(BaseModel): |
|
|
profile_name: str |
|
|
correct_enums: int |
|
|
total_enums: int |
|
|
accuracy: float |
|
|
additional_enums_in_actual: int |
|
|
comparisons: dict[str, Any] |
|
|
|
|
|
|
|
|
def load_profiles() -> dict[str, tuple[LinkedinProfile, VSPProfile]]: |
|
|
sample_profiles_dir = Path("tests/test_data/sample_profiles") |
|
|
classified_profiles_dir = Path("tests/test_data/sample_profiles_classified") |
|
|
profiles = {} |
|
|
|
|
|
for profile_file in sample_profiles_dir.glob("*.json"): |
|
|
name = profile_file.stem |
|
|
with profile_file.open() as f: |
|
|
linkedin_data = json.load(f) |
|
|
linkedin_profile = LinkedinProfile.model_validate(linkedin_data) |
|
|
|
|
|
classified_file = classified_profiles_dir / f"{name}.json" |
|
|
if classified_file.exists(): |
|
|
with classified_file.open() as f: |
|
|
classified_data = json.load(f) |
|
|
classified_profile = VSPProfile.model_validate(classified_data) |
|
|
|
|
|
profiles[name] = (linkedin_profile, classified_profile) |
|
|
|
|
|
return profiles |
|
|
|
|
|
|
|
|
async def compare_profiles(linkedin_profile: LinkedinProfile, classified_profile: VSPProfile) -> ComparisonResult: |
|
|
vsp_enrichment = VspDataEnrichment() |
|
|
result = await vsp_enrichment.process_linkedin_profile(linkedin_profile) |
|
|
comparisons = {} |
|
|
correct_enums = 0 |
|
|
total_enums = 0 |
|
|
additional_enums_in_actual = 0 |
|
|
|
|
|
|
|
|
for classified_edu, result_edu in zip(classified_profile.education.education_history, result.classified_educations): |
|
|
comparisons[f"Education: {classified_edu.school}"] = { |
|
|
"expected": classified_edu.degree_characterization, |
|
|
"actual": result_edu.classification.output.value, |
|
|
"confidence": result_edu.classification.confidence, |
|
|
"reasoning": result_edu.classification.reasoning, |
|
|
} |
|
|
if classified_edu.degree_characterization is not None: |
|
|
total_enums += 1 |
|
|
if classified_edu.degree_characterization == result_edu.classification.output.value: |
|
|
correct_enums += 1 |
|
|
elif result_edu.classification.output.value is not None: |
|
|
additional_enums_in_actual += 1 |
|
|
|
|
|
|
|
|
for classified_exp, result_exp in zip( |
|
|
classified_profile.professional_experience.experience_history, result.classified_work_experiences |
|
|
): |
|
|
exp_key = f"Job: {classified_exp.title} at {classified_exp.company}" |
|
|
comparisons[exp_key] = { |
|
|
"primary_job_type": { |
|
|
"expected": classified_exp.primary_job_type, |
|
|
"actual": result_exp.work_experience_classification.primary_job_type.value, |
|
|
"confidence": result_exp.work_experience_classification.confidence, |
|
|
"reasoning": result_exp.work_experience_classification.reasoning, |
|
|
}, |
|
|
"secondary_job_type": { |
|
|
"expected": classified_exp.secondary_job_type, |
|
|
"actual": result_exp.work_experience_classification.secondary_job_type.value, |
|
|
"confidence": result_exp.work_experience_classification.confidence, |
|
|
"reasoning": result_exp.work_experience_classification.reasoning, |
|
|
}, |
|
|
} |
|
|
if classified_exp.primary_job_type is not None: |
|
|
total_enums += 1 |
|
|
if classified_exp.primary_job_type == result_exp.work_experience_classification.primary_job_type.value: |
|
|
correct_enums += 1 |
|
|
elif result_exp.work_experience_classification.primary_job_type.value is not None: |
|
|
additional_enums_in_actual += 1 |
|
|
|
|
|
if classified_exp.secondary_job_type is not None: |
|
|
total_enums += 1 |
|
|
if classified_exp.secondary_job_type == result_exp.work_experience_classification.secondary_job_type.value: |
|
|
correct_enums += 1 |
|
|
elif result_exp.work_experience_classification.secondary_job_type.value is not None: |
|
|
additional_enums_in_actual += 1 |
|
|
|
|
|
|
|
|
if result_exp.investment_banking_classification: |
|
|
comparisons[exp_key]["investment_banking_group"] = { |
|
|
"expected": classified_exp.investment_banking_focus, |
|
|
"actual": result_exp.investment_banking_classification.investment_banking_group.value, |
|
|
"confidence": result_exp.investment_banking_classification.confidence, |
|
|
"reasoning": result_exp.investment_banking_classification.reasoning, |
|
|
} |
|
|
if classified_exp.investment_banking_focus is not None: |
|
|
total_enums += 1 |
|
|
if ( |
|
|
classified_exp.investment_banking_focus |
|
|
== result_exp.investment_banking_classification.investment_banking_group.value |
|
|
): |
|
|
correct_enums += 1 |
|
|
elif result_exp.investment_banking_classification.investment_banking_group.value is not None: |
|
|
additional_enums_in_actual += 1 |
|
|
|
|
|
if result_exp.investing_focus_asset_class_classification: |
|
|
comparisons[exp_key]["investing_focus_asset_class"] = { |
|
|
"expected": classified_exp.investing_focus_stage, |
|
|
"actual": result_exp.investing_focus_asset_class_classification.investing_focus_asset_class.value, |
|
|
"confidence": result_exp.investing_focus_asset_class_classification.confidence, |
|
|
"reasoning": result_exp.investing_focus_asset_class_classification.reasoning, |
|
|
} |
|
|
if classified_exp.investing_focus_stage is not None: |
|
|
total_enums += 1 |
|
|
if ( |
|
|
classified_exp.investing_focus_stage |
|
|
== result_exp.investing_focus_asset_class_classification.investing_focus_asset_class.value |
|
|
): |
|
|
correct_enums += 1 |
|
|
elif result_exp.investing_focus_asset_class_classification.investing_focus_asset_class.value is not None: |
|
|
additional_enums_in_actual += 1 |
|
|
|
|
|
if result_exp.investing_focus_sector_classification: |
|
|
comparisons[exp_key]["investing_focus_sector"] = { |
|
|
"expected": classified_exp.investing_focus_sector, |
|
|
"actual": result_exp.investing_focus_sector_classification.investing_focus_sector.value, |
|
|
"confidence": result_exp.investing_focus_sector_classification.confidence, |
|
|
"reasoning": result_exp.investing_focus_sector_classification.reasoning, |
|
|
} |
|
|
if classified_exp.investing_focus_sector is not None: |
|
|
total_enums += 1 |
|
|
if ( |
|
|
classified_exp.investing_focus_sector |
|
|
== result_exp.investing_focus_sector_classification.investing_focus_sector.value |
|
|
): |
|
|
correct_enums += 1 |
|
|
elif result_exp.investing_focus_sector_classification.investing_focus_sector.value is not None: |
|
|
additional_enums_in_actual += 1 |
|
|
|
|
|
accuracy = correct_enums / total_enums if total_enums > 0 else 0 |
|
|
return ComparisonResult( |
|
|
profile_name=f"{linkedin_profile.first_name} {linkedin_profile.last_name}", |
|
|
correct_enums=correct_enums, |
|
|
total_enums=total_enums, |
|
|
accuracy=accuracy, |
|
|
additional_enums_in_actual=additional_enums_in_actual, |
|
|
comparisons=comparisons, |
|
|
) |
|
|
|
|
|
|
|
|
async def run_tests() -> None: |
|
|
profiles = load_profiles() |
|
|
results = [] |
|
|
|
|
|
for _, (linkedin_profile, classified_profile) in profiles.items(): |
|
|
result = await compare_profiles(linkedin_profile, classified_profile) |
|
|
results.append(result) |
|
|
logger.info( |
|
|
f"Processed {result.profile_name}: Accuracy {result.accuracy:.2%}, " |
|
|
f"Additional enums in actual: {result.additional_enums_in_actual}" |
|
|
) |
|
|
|
|
|
overall_accuracy = sum(r.accuracy for r in results) / len(results) |
|
|
total_additional_enums = sum(r.additional_enums_in_actual for r in results) |
|
|
logger.info(f"Overall accuracy: {overall_accuracy:.2%}") |
|
|
logger.info(f"Total additional enums in actual: {total_additional_enums}") |
|
|
|
|
|
|
|
|
with open("enum_classifier_results.json", "w") as f: |
|
|
json.dump([r.model_dump() for r in results], f, indent=2) |
|
|
|
|
|
logger.info("Detailed results saved to enum_classifier_results.json") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
asyncio.run(run_tests()) |
|
|
|