import datetime import hashlib import logging from abc import ABC, abstractmethod from typing import Optional import redis from django.conf import settings from django.contrib.auth.models import User from django.utils import timezone from console import conslog from core.utils import dtnow from users.models import UserInfo _log = logging.getLogger() def get_active_users_query(): return User.objects.exclude(username__startswith='deleted_') def create_user(*args, **kwargs): user_info_fields_names = (ui.name for ui in UserInfo._meta.get_fields()) user_info_kwargs = {name: kwargs.pop(name) for name in user_info_fields_names if name in kwargs} if isinstance(user_info_kwargs.get('expire_date', None), datetime.date): kwargs['is_active'] = kwargs.get('is_active', True) and user_info_kwargs['expire_date'] >= dtnow().date() if 'user' not in user_info_kwargs: user_info_kwargs['user'] = User.objects.create_user(*args, **kwargs) user_info_kwargs['user'].save() UserInfo.objects.create(**user_info_kwargs) return user_info_kwargs['user'] def delete_user(user_deleting, log_deletion=True): """ Method for deleting user by changing his is_active status to False and renaming him """ now = datetime.datetime.now() hash = hashlib.sha256() hash.update(str(now).encode('utf-8')) try: username_before = user_deleting.username user_deleting.is_active = False user_deleting.username = f'deleted_{user_deleting.username}_{hash.hexdigest()[:10]}' user_deleting.save() username_after = user_deleting.username if log_deletion: log_message = f"User [{user_deleting}] deleted [{username_before}]. " \ f"new username: [{username_after}]" _log.info(log_message) except Exception as e: log_message = f"Exception {e} appears during deleting user" conslog.add_error_log(log_message, _log) class UserStatusStrategyABC(ABC): @abstractmethod def set_date(self, date): ... @abstractmethod def get_date(self): ... class InUserInfoStrategy(UserStatusStrategyABC): def __init__(self, user): self.user = user def set_date(self, date) -> None: self.user.userinfo.last_seen = date self.user.userinfo.save() def get_date(self) -> str: return self.user.userinfo.last_seen class InRedisStrategy(UserStatusStrategyABC): user_status_field = 'USER_STATUS_{}' def __init__(self, user): self.user = user self.redis_instance = redis.StrictRedis( host=getattr(settings, 'REDIS_HOST', 'redis'), port=getattr(settings, 'REDIS_PORT', 6379) ) def set_date(self, date): field = self.user_status_field.format(self.user.pk) data = str(date.timestamp()) self.redis_instance.set(field, data) def get_date(self): field = self.user_status_field.format(self.user.pk) timestamp = self.redis_instance.get(field) if not timestamp: return return datetime.datetime.fromtimestamp(float(timestamp.decode())) class UserStatusService: """User status service.""" timeout_minute = 1 def __init__(self, user, *, strategy: Optional[UserStatusStrategyABC] = None) -> None: self._status = None if not strategy: strategy = InUserInfoStrategy self._strategy = strategy(user) self.user = user def get_status(self) -> str: """Return user status online or offline""" last_date = self._strategy.get_date() if not last_date: return 'offline' last_date = last_date.replace(tzinfo=timezone.utc) now = dtnow() delta = datetime.timedelta(minutes=self.timeout_minute) if now - last_date > delta: return 'offline' return 'online' def set_status(self) -> None: """Set last date seen in db.""" now = dtnow() self._strategy.set_date(now)