See #212: record user last activity date
This commit is contained in:
parent
307959dfa6
commit
2e4f862387
|
@ -146,6 +146,7 @@ MIDDLEWARE = (
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
"funkwhale_api.users.middleware.RecordActivityMiddleware",
|
||||||
)
|
)
|
||||||
|
|
||||||
# MIGRATIONS CONFIGURATION
|
# MIGRATIONS CONFIGURATION
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class RecordActivityMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
response = self.get_response(request)
|
||||||
|
if hasattr(request, "user") and request.user.is_authenticated:
|
||||||
|
request.user.record_activity()
|
||||||
|
return response
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.6 on 2018-06-17 15:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0007_auto_20180524_2009'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='last_activity',
|
||||||
|
field=models.DateTimeField(blank=True, default=None, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='permission_library',
|
||||||
|
field=models.BooleanField(default=False, help_text='Manage library, delete files, tracks, artists, albums...', verbose_name='Manage library'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -9,6 +10,7 @@ from django.conf import settings
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -75,6 +77,8 @@ class User(AbstractUser):
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
last_activity = models.DateTimeField(default=None, null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
|
@ -117,3 +121,16 @@ class User(AbstractUser):
|
||||||
|
|
||||||
def get_activity_url(self):
|
def get_activity_url(self):
|
||||||
return settings.FUNKWHALE_URL + "/@{}".format(self.username)
|
return settings.FUNKWHALE_URL + "/@{}".format(self.username)
|
||||||
|
|
||||||
|
def record_activity(self):
|
||||||
|
"""
|
||||||
|
Simply update the last_activity field if current value is too old
|
||||||
|
than a threshold. This is useful to keep a track of inactive accounts.
|
||||||
|
"""
|
||||||
|
current = self.last_activity
|
||||||
|
delay = 60 * 15 # fifteen minutes
|
||||||
|
now = timezone.now()
|
||||||
|
|
||||||
|
if current is None or current < now - datetime.timedelta(seconds=delay):
|
||||||
|
self.last_activity = now
|
||||||
|
self.save(update_fields=["last_activity"])
|
||||||
|
|
|
@ -7,6 +7,7 @@ import pytest
|
||||||
import requests_mock
|
import requests_mock
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.core.cache import cache as django_cache
|
from django.core.cache import cache as django_cache
|
||||||
|
from django.utils import timezone
|
||||||
from django.test import client
|
from django.test import client
|
||||||
from dynamic_preferences.registries import global_preferences_registry
|
from dynamic_preferences.registries import global_preferences_registry
|
||||||
from rest_framework import fields as rest_fields
|
from rest_framework import fields as rest_fields
|
||||||
|
@ -250,3 +251,10 @@ def to_api_date():
|
||||||
raise ValueError("Invalid value: {}".format(value))
|
raise ValueError("Invalid value: {}".format(value))
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def now(mocker):
|
||||||
|
now = timezone.now()
|
||||||
|
mocker.patch("django.utils.timezone.now", return_value=now)
|
||||||
|
return now
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
from funkwhale_api.users import middleware
|
||||||
|
|
||||||
|
|
||||||
|
def test_record_activity_middleware(factories, api_request, mocker):
|
||||||
|
m = middleware.RecordActivityMiddleware(lambda request: None)
|
||||||
|
user = factories["users.User"]()
|
||||||
|
record_activity = mocker.patch("funkwhale_api.users.models.User.record_activity")
|
||||||
|
request = api_request.get("/")
|
||||||
|
request.user = user
|
||||||
|
m(request)
|
||||||
|
|
||||||
|
record_activity.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
|
def test_record_activity_middleware_no_user(api_request):
|
||||||
|
m = middleware.RecordActivityMiddleware(lambda request: None)
|
||||||
|
request = api_request.get("/")
|
||||||
|
m(request)
|
|
@ -78,3 +78,20 @@ def test_has_permissions_and(args, perms, expected, factories):
|
||||||
def test_has_permissions_or(args, perms, expected, factories):
|
def test_has_permissions_or(args, perms, expected, factories):
|
||||||
user = factories["users.User"](**args)
|
user = factories["users.User"](**args)
|
||||||
assert user.has_permissions(*perms, operator="or") is expected
|
assert user.has_permissions(*perms, operator="or") is expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_record_activity(factories, now):
|
||||||
|
user = factories["users.User"]()
|
||||||
|
assert user.last_activity is None
|
||||||
|
|
||||||
|
user.record_activity()
|
||||||
|
|
||||||
|
assert user.last_activity == now
|
||||||
|
|
||||||
|
|
||||||
|
def test_record_activity_does_nothing_if_already(factories, now, mocker):
|
||||||
|
user = factories["users.User"](last_activity=now)
|
||||||
|
save = mocker.patch("funkwhale_api.users.models.User.save")
|
||||||
|
user.record_activity()
|
||||||
|
|
||||||
|
save.assert_not_called()
|
||||||
|
|
Loading…
Reference in New Issue