File size: 4,679 Bytes
73b614e
 
 
 
 
46610a8
 
 
73b614e
40eae15
46610a8
73b614e
 
 
 
46610a8
73b614e
 
46610a8
 
 
 
73b614e
 
46610a8
 
 
 
73b614e
61542b7
46610a8
 
 
 
73b614e
46610a8
 
73b614e
46610a8
73b614e
46610a8
73b614e
 
 
 
 
 
46610a8
73b614e
 
 
46610a8
 
 
73b614e
 
46610a8
456ba4c
 
b363350
d6f95a2
 
 
b363350
d6f95a2
 
456ba4c
73b614e
 
 
 
 
 
46610a8
 
456ba4c
 
46610a8
73b614e
46610a8
73b614e
 
46610a8
73b614e
 
46610a8
 
 
 
73b614e
 
46610a8
 
 
 
 
 
 
 
 
 
 
 
 
 
73b614e
 
46610a8
 
 
 
73b614e
 
46610a8
73b614e
 
 
 
46610a8
 
 
 
 
73b614e
 
46610a8
73b614e
46610a8
 
 
 
 
 
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
from geopy.distance import geodesic
import json
import os
from geopy.geocoders import Nominatim

from ..core.models import tfidf_vectorizer, le, rf_model, sentence_model
from ..schemas.recommendation import RecommendationRequest
from ..utils.text import clean_resume

geolocator = Nominatim(user_agent="student_recommendation_api_v1")

GEO_CACHE_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "GEO_CACHE.txt")
GEO_CACHE = {}

def load_geo_cache():
    global GEO_CACHE
    if os.path.exists(GEO_CACHE_FILE):
        with open(GEO_CACHE_FILE, "r") as f:
            try:
                GEO_CACHE = json.load(f)
            except json.JSONDecodeError:
                GEO_CACHE = {}

def save_geo_cache():
    with open(GEO_CACHE_FILE, "w") as f:
        json.dump(GEO_CACHE, f)

load_geo_cache()

def geo_coords(city_name: str) -> tuple:
    """

    Geocodes a city name to (latitude, longitude).

    Uses an in-memory cache to avoid repeated API calls.

    """
    city_name = city_name.lower().strip()
    if city_name in GEO_CACHE:
        return GEO_CACHE[city_name]
    try:
        print(f"--- Geocoding and caching new city: {city_name} ---")
        location = geolocator.geocode(f"{city_name}, Indonesia")

        if location:
            coords = (location.latitude, location.longitude)
            GEO_CACHE[city_name] = coords
            save_geo_cache()
            return coords
        else:
            print(f"Location not found for {city_name}")
            GEO_CACHE[city_name] = None
            save_geo_cache()
            return None
    except Exception as e:
        print(f"Error geocoding {city_name}: {e}")
        return None

def get_category_prediction(profile_text: str) -> str:
    """Processes text and predicts the job category."""
    if not all([tfidf_vectorizer, le, rf_model]):
        raise RuntimeError("Classification models are not properly loaded")
    
    print("debug")
    print(profile_text)
    
    cleaned_text = clean_resume(profile_text)
    print("cleaned text:")
    print(cleaned_text)

    cleaned_text = profile_text.lower()
    vectorized_text = tfidf_vectorizer.transform([cleaned_text])
    prediction_encoded = rf_model.predict(vectorized_text)[0]
    category = le.inverse_transform([prediction_encoded])[0]
    return category

def  get_ranked_internships(request: RecommendationRequest) -> list[int]:
    """Performs two-stage ranking with dynamic geocoding."""
    if not sentence_model:
        raise RuntimeError("SentenceTransformer model is not properly loaded")

    profile_text_to_encode = request.profile_text

    if request.predicted_category:
        profile_text_to_encode = f"The user's predicted job category is {request.predicted_category}. Based on that, consider their profile: {request.profile_text}"

    profile_embedding = sentence_model.encode(profile_text_to_encode)
    internship_texts = [internship.internship_text for internship in request.internships]

    if not internship_texts:
        return []

    internship_embeddings = sentence_model.encode(internship_texts)
    cosine_score = sentence_model.similarity(profile_embedding, internship_embeddings)[0].tolist()

    print("--- FastAPI Debugging ---")
    print(f"Received {len(internship_texts)} internships to rank.")
    print(f"Calculated Cosine Scores: {cosine_score}")
    print("--------------------------")

    ranked_by_similarity = []
    for i, internship in enumerate(request.internships):
        ranked_by_similarity.append({
            "id": internship.id,
            "similarity_score": cosine_score[i],
            "location": internship.location,
        })

    final_ranked_list = []
    user_coords = geo_coords(request.preferred_location)

    print(user_coords, request.preferred_location)


    for internship in ranked_by_similarity:
        final_score = internship['similarity_score']

        if user_coords:
            internship_coords = geo_coords(internship['location'])
            if internship_coords:
                distance_km = geodesic(user_coords, internship_coords).kilometers
                if distance_km < 1:
                    final_score += 2.0
                elif distance_km < 150:
                    final_score += 0.75

        internship['final_score'] = final_score
        final_ranked_list.append(internship)

    final_ranked_list.sort(key=lambda x: x['final_score'], reverse=True)

    final_ids = [item['id'] for item in final_ranked_list]

    print(final_ranked_list)

    return final_ids