Source code for matching.signals

# 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) )