import logging import traceback from functools import wraps from django.dispatch import receiver from django.db.models.signals import post_save from social_django.models import UserSocialAuth from pingcap_sso.models import ( User, UserProfileCollections, TUGMemberApplication, OfficialContactUs, OrganizationApply ) from pingcap_sso.tasks.user_profile_collections import update_user_primary_profile logger = logging.getLogger(__name__) # signal recievers in this file is used to update user profile collections, so it's not that important, # we will catch all signal reciver's Exception to avoid break the whole system. # refer: https://docs.djangoproject.com/zh-hans/2.2/topics/signals/ # > send() does not catch any exceptions raised by receivers; it simply allows errors to propagate. # > Thus not all receivers may be notified of a signal in the face of an error. def mute_exception_and_log_error(func): @wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as e: logger.error(traceback.format_exc()) return wrapper @receiver(post_save, sender=TUGMemberApplication) @mute_exception_and_log_error def _sync_tug_member_application_to_profile_collections(sender, instance, created, **kwargs): if created: UserProfileCollections.objects.create( user=instance.user, name=instance.real_name, phone=instance.phone, email=instance.email, company=instance.company_name, job_title=instance.job_title_text, wechat_id=instance.wechat_id, tidb_use_stage=instance.stage_of_company_use_of_tidb, source=UserProfileCollections.SOURCE_TUG_APPLICATION, ) @receiver(post_save, sender=OfficialContactUs) @mute_exception_and_log_error def _sync_official_contact_us_to_profile_collections(sender, instance, created, **kwargs): if created and instance.user: UserProfileCollections.objects.create( user=instance.user, name=instance.name, phone=instance.phone, company=instance.company, job_title=instance.job_title_text, source=UserProfileCollections.SOURCE_CONTACT_US, ) @receiver(post_save, sender=OrganizationApply) @mute_exception_and_log_error def _sync_organization_apply_to_profile_collections(sender, instance, created, **kwargs): if created: UserProfileCollections.objects.create( user=instance.created_by_user, phone=instance.contact_phone, email=instance.email, company=instance.company_name, job_title=instance.job_title_text, industry=instance.industry_type_text, address=f'{instance.company_base_text} (公司所在地)', source=UserProfileCollections.SOURCE_ORGANIZATION, ) @receiver(post_save, sender=User) @mute_exception_and_log_error def _sync_user_to_profile_collections(sender, instance, created, **kwargs): upc, _ = UserProfileCollections.objects.get_or_create( user=instance, source=UserProfileCollections.SOURCE_PROFILE ) upc.name = instance.name if instance.gender: upc.gender = '男' if instance.gender == User.GENDER_MALE else '女' else: upc.gender = None company_changed = upc.company != instance.company_name upc.company = instance.company_name upc.job_title = instance.job_title_text upc.address = instance.address upc.phone = instance.phone upc.email = instance.email or instance.signup_email try: github = UserSocialAuth.objects.get(user=instance, provider='github') upc.github_id = f"{github.extra_data.get('login')} id:{github.uid}" except UserSocialAuth.DoesNotExist: pass updated_fields = [ "name", "gender", "company", "job_title", "address", "phone", "email", "github_id", ] # Only update updated_at when company changed # We will use updated_at to determine if we need to update primary profile's company if company_changed: updated_fields.append("updated_at") upc.save(update_fields=updated_fields) @receiver(post_save, sender=UserProfileCollections) def _update_primary_profile(sender, instance, created, **kwargs): if not instance.is_primary: # TODO: sometime this task was called before user created in the database (need investigate) # sent a `countdown` to avoid this situation update_user_primary_profile.apply_async((instance.user.id, ), countdown=5)