blender-id/bid_main/signals.py
Anna Sirota ba1f69eced Avatars: replace image column with the new one
From this change onwards only pre-generated avatar thumbnails
are used anywhere in Blender ID.
No implicit on-demand thumbnail generation or caching (or trashing of
the cache table on each lookup of a thumbnail) should be going on.
2023-12-14 13:02:44 +01:00

103 lines
3.7 KiB
Python

import logging
from django.contrib.auth.signals import user_logged_in
from django.core.signals import got_request_exception
from django.db.models import F
from django.db.models.signals import m2m_changed, pre_save, post_save, post_delete
from django.dispatch import receiver
from . import models
import bid_main.utils as utils
import bid_main.file_utils
log = logging.getLogger(__name__)
@receiver(got_request_exception)
def log_exception(sender, **kwargs):
log.exception("uncaught exception occurred")
@receiver(user_logged_in)
def update_user_for_login(sender, request, user, **kwargs):
"""Updates user fields upon login.
Only saves specific fields, so that the webhook trigger knows what changed.
"""
log.debug("User %s logged in, storing login information", user.email)
user.login_count = F("login_count") + 1
fields = {"login_count"}
# Only move 'current' to 'last' login IP if the IP address is different.
request_ip = utils.get_client_ip(request)
if request_ip and user.current_login_ip != request_ip:
user.last_login_ip = F("current_login_ip")
user.current_login_ip = request_ip
fields.update({"last_login_ip", "current_login_ip"})
user.save(update_fields=fields)
@receiver(m2m_changed)
def modified_user_role(sender, instance, action, reverse, model, **kwargs):
my_log = log.getChild("modified_user_role")
if not action.startswith("post_"):
my_log.debug("Ignoring m2m %r on %s - %s", action, type(instance), model)
return
if not isinstance(instance, models.User) or not issubclass(model, models.Role):
my_log.debug("Ignoring m2m %r on %s - %s", action, type(instance), model)
return
if not instance.id:
my_log.debug("Ignoring m2m %r on %s (no ID) - %s", action, type(instance), model)
return
# User's roles changed, so we have to update their public_roles_as_string.
new_roles = " ".join(sorted(instance.public_roles()))
if new_roles != instance.public_roles_as_string:
instance.public_roles_as_string = new_roles
my_log.debug(" saving user again for new roles %r", new_roles)
instance.save(update_fields=["public_roles_as_string"])
else:
my_log.debug(" new roles are old roles: %r", new_roles)
@receiver(pre_save, sender=models.User)
def _set_avatar_changed_flag(sender, instance, **kwargs):
if not instance.pk:
return
try:
old_avatar = models.User.objects.get(pk=instance.pk).avatar
except models.User.DoesNotExist:
return
new_avatar = instance.avatar
_was_avatar_changed = old_avatar and bool(old_avatar) != bool(new_avatar)
if _was_avatar_changed:
instance._was_avatar_changed = _was_avatar_changed
instance._old_avatar = old_avatar.name
log.info('User pk=%s changed avatar from "%s" to "%s"', instance.pk, old_avatar, new_avatar)
@receiver(post_save, sender=models.User)
def delete_avatar_files_on_change(sender, instance, **kwargs):
"""Delete old avatar files from storage when User avatar is changed."""
if kwargs.get('created'):
return
if not getattr(instance, '_was_avatar_changed', False):
return
log.info('Avatar changed for User pk=%s, removing %s', instance.pk, instance._old_avatar)
bid_main.file_utils.delete_avatar_files(instance._old_avatar)
@receiver(post_delete, sender=models.User)
def delete_orphaned_avatar_files(sender, instance, **kwargs):
"""Delete avatar files from storage when User record is deleted."""
if not instance.avatar:
log.info('User pk=%s has no avatar, nothing to clean up')
return
bid_main.file_utils.delete_avatar_files(instance.avatar.name)