128 lines
4 KiB
Python
128 lines
4 KiB
Python
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)
|