See #212: record user last activity date

This commit is contained in:
Eliot Berriot 2018-06-17 17:53:40 +02:00
parent 307959dfa6
commit 2e4f862387
No known key found for this signature in database
GPG Key ID: DD6965E2476E5C27
7 changed files with 93 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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'),
),
]

View File

@ -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"])

View File

@ -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

View File

@ -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)

View File

@ -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()