# automatically recalculates match scores whenever a User is saved.
# only runs for users with enough profile data, and skips ADMINs.
from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver
from users.models import User
from .models import Score
from .scoring import calculate_match_score
from .utils import _has_enough_profile_data
def _build_mentee_scores(mentee):
mentors = [
m for m in User.objects.filter(role=User.Role.MENTOR)
if _has_enough_profile_data(m)
]
return [
Score(
mentee_email=mentee.email,
mentor_email=mentor.email,
score=calculate_match_score(mentee, mentor),
)
for mentor in mentors
]
def _build_mentor_scores(mentor):
mentees = [
m for m in User.objects.filter(role=User.Role.MENTEE)
if _has_enough_profile_data(m)
]
return [
Score(
mentee_email=mentee.email,
mentor_email=mentor.email,
score=calculate_match_score(mentee, mentor),
)
for mentee in mentees
]
[docs]
def calculate_and_store_scores_for_user(user):
if user.role == User.Role.MENTEE:
scores = _build_mentee_scores(user)
elif user.role == User.Role.MENTOR:
scores = _build_mentor_scores(user)
else:
return
if scores:
Score.objects.bulk_create(scores, ignore_conflicts=True)
[docs]
def recalculate_scores_for_user(user):
if user.role == User.Role.MENTEE:
scores = _build_mentee_scores(user)
elif user.role == User.Role.MENTOR:
scores = _build_mentor_scores(user)
else:
return
for score_obj in scores:
Score.objects.update_or_create(
mentee_email=score_obj.mentee_email,
mentor_email=score_obj.mentor_email,
defaults={'score': score_obj.score},
)
[docs]
@receiver(post_save, sender=User)
def handle_user_profile_completion(sender, instance, created, **kwargs):
print(f"Signal fired for {instance.email}, created={created}")
print(f" major={instance.major}, year={instance.year}")
print(f" has_enough_data={_has_enough_profile_data(instance)}")
print(f" role={instance.role}")
if instance.role == User.Role.ADMIN:
return
if not _has_enough_profile_data(instance):
return
if created:
calculate_and_store_scores_for_user(instance)
else:
# defer until after the transaction commits so the updated profile data is visible to the scoring query
transaction.on_commit(
lambda: recalculate_scores_for_user(instance)
)