Skip to content
Snippets Groups Projects
Verified Commit 2e4f8623 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

See #212: record user last activity date

parent 307959df
No related branches found
No related tags found
No related merge requests found
...@@ -146,6 +146,7 @@ MIDDLEWARE = ( ...@@ -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
......
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
# 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 @@ ...@@ -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 ...@@ -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): ...@@ -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): ...@@ -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 ...@@ -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(): ...@@ -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
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): ...@@ -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()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment