Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.d/20250620_113902_sekachev.bs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Added

- CVAT server tracks `last_activity_date` of a user, the field is updated once a day
(<https://github.com/cvat-ai/cvat/pull/9554>)
28 changes: 28 additions & 0 deletions cvat/apps/engine/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
from typing import Protocol
from uuid import uuid4

from django.apps import apps
from django.conf import settings
from django.utils.timezone import now


class WithUUID(Protocol):
uuid: str
Expand All @@ -24,3 +28,27 @@ def __call__(self, request):
response.headers["X-Request-Id"] = request.uuid

return response


class LastActivityMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
response = self.get_response(request)

if request.user.is_authenticated:
profile = getattr(request.user, "profile", None)
if not profile:
return response

last_activity_date = profile.last_activity_date
if (
not last_activity_date
or (now() - last_activity_date) > settings.USER_LAST_ACTIVITY_UPDATE_MIN_INTERVAL
):
# such way we avoid failing and any db updates if the Profile was removed during the request
Profile = apps.get_model("engine", "Profile")
Profile.objects.filter(user_id=request.user.id).update(last_activity_date=now())

return response
18 changes: 18 additions & 0 deletions cvat/apps/engine/migrations/0091_profile_last_activity_date.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.21 on 2025-06-22 11:13

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("engine", "0090_asset_content_size_data_content_size"),
]

operations = [
migrations.AddField(
model_name="profile",
name="last_activity_date",
field=models.DateTimeField(blank=True, default=None, null=True),
),
]
1 change: 1 addition & 0 deletions cvat/apps/engine/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,7 @@ class TrackedShapeAttributeVal(AttributeVal):
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
rating = models.FloatField(default=0.0)
last_activity_date = models.DateTimeField(null=True, blank=True, default=None)
has_analytics_access = models.BooleanField(
_("has access to analytics"),
default=False,
Expand Down
3 changes: 3 additions & 0 deletions cvat/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def generate_secret_key():
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.middleware.gzip.GZipMiddleware",
"cvat.apps.engine.middleware.RequestTrackingMiddleware",
"cvat.apps.engine.middleware.LastActivityMiddleware",
"crum.CurrentRequestUserMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
Expand Down Expand Up @@ -787,3 +788,5 @@ class CVAT_QUEUES(Enum):
"cron_string": "0 8 * * *",
}
)

USER_LAST_ACTIVITY_UPDATE_MIN_INTERVAL = timedelta(days=1)
Loading