justKevv's picture
fix
d6f95a2
Raw
History Blame Contribute Delete
4.68 kB
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