-
-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Group submission access logs #5094
Conversation
kobo/settings/base.py
Outdated
@@ -1748,6 +1748,7 @@ def dj_stripe_request_callback_method(): | |||
'application/x-zip-compressed' | |||
] | |||
|
|||
ACCESS_LOG_SUBMISSION_GROUP_TIME_LIMIT_MINUTES=60 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would add a Constance settings for that. Superuser may want to customize this without redeploying a new release.
kobo/apps/audit_log/models.py
Outdated
class SubmissionGroupManager(AccessLogManager): | ||
def get_queryset(self): | ||
return ( | ||
super() | ||
.get_queryset() | ||
.filter(metadata__auth_type=ACCESS_LOG_SUBMISSION_GROUP_AUTH_TYPE) | ||
) | ||
|
||
def create(self, **kwargs): | ||
metadata = kwargs.pop('metadata', {}) | ||
metadata['auth_type'] = ACCESS_LOG_SUBMISSION_GROUP_AUTH_TYPE | ||
kwargs['metadata'] = metadata | ||
return super().create(**kwargs) | ||
|
||
|
||
class SubmissionGroup(AccessLog): | ||
objects = SubmissionGroupManager() | ||
|
||
class Meta: | ||
proxy = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move them after SubmissionAccessLogManager
.
G
comes after A
:-P
If it is because of the typing annotations in SubmissionAccessLog
, let's wrap it in single quotes.
kobo/apps/audit_log/serializers.py
Outdated
@@ -47,3 +47,35 @@ def get_date_created(self, audit_log): | |||
|
|||
def get_username(self, audit_log): | |||
return audit_log.user.username | |||
|
|||
|
|||
class UsernameHyperlinkField(serializers.HyperlinkedRelatedField): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about moving this in its own file in kpi/fields
?
kobo/apps/audit_log/serializers.py
Outdated
user = UsernameHyperlinkField(source='user__username') | ||
date_created = serializers.SerializerMethodField() | ||
username = serializers.CharField(source='user__username') | ||
object_id = serializers.IntegerField() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it has already fixed in other PR, but just in case, as per our discussion, we should get rid of object_id
.
kobo/apps/audit_log/signals.py
Outdated
latest_group = ( | ||
SubmissionGroup.objects.filter(user_uid=instance.user_uid) | ||
.order_by('-date_created') | ||
.first() | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this part is called each time a submission comes in.
I'll try to optimize as much as possible to avoid any bottlenecks. What do you thing about something like that?
1) Retrieve only fields we need
We only need pk
and user_uid
from latest_group
latest_group = (
SubmissionGroup.objects.only('pk', 'user_uid').filter(user_uid=instance.user_uid)
.order_by('-date_created')
.first()
)
I would rather use .values()
instead (to avoid loading the object, just retrieve the values) but it would involve to refactor for add_to_existing_submission_group
and latest_entry
to use only submission_group_id
below.
It won't be way faster, but at least, IMO, a little bit lighter on memory footprint.
kobo/apps/audit_log/models.py
Outdated
): | ||
if self.user_uid != submission_group.user_uid: | ||
logging.error( | ||
f'Cannot add submission log with user {self.user_uid} to group with user {submission_group.user_uid}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Way above 88 :-P
log.add_to_existing_submission_group(group) | ||
log.save() | ||
# make sure we reported an error | ||
patched_error.assert_called_once() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When can this happen in a real scenario? Should not we raise an exception instead of just logging the error and continue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should never happen in a real scenario. It's for future-proofing in case other code needs to use this method. I wasn't sure not adding to a group would be a big enough problem to raise an exception but I'm pretty ambivalent about it
group_query = SubmissionGroup.objects.filter(user=user) | ||
self.assertEqual(group_query.count(), 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should assert it equals 0 before submission access log is created?
new_submission = SubmissionAccessLog.objects.create(user=user) | ||
self.assertEqual(group_query.count(), 2) | ||
new_group = group_query.order_by('-date_created').first() | ||
self.assertEqual(new_submission.submission_group.id, new_group.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
even if group_query.count() == 2
, I think it could be great to validate new_group.id != old_group.id
just in case.
kobo/apps/audit_log/views.py
Outdated
|
||
> Example | ||
> | ||
> curl -X GET https://[kpi-url]/access-logs/all | ||
|
||
> Response 200 | ||
|
||
> { | ||
> "count": 10, | ||
> "next": null, | ||
> "previous": null, | ||
> "results": [ | ||
> { | ||
> "app_label": "kobo_auth", | ||
> "model_name": "User", | ||
> "object_id": 1, | ||
> "user": "http://localhost/api/v2/users/admin/", | ||
> "user_uid": "uBMZxx9tVfepvTRp3e9Twj", | ||
> "username": "admin", | ||
> "action": "AUTH", | ||
> "metadata": { | ||
> "source": "Firefox (Ubuntu)", | ||
> "auth_type": "Digest", | ||
> "ip_address": "172.18.0.6" | ||
> }, | ||
> "date_created": "2024-08-19T16:48:58Z", | ||
> "log_type": "access" | ||
> }, | ||
> ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove the example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accident from moving things around
While I was testing the logs with |
67579ff
to
55b66ea
Compare
Converting back to draft while I work on a different implementation |
5cc2fbc
to
039b7ee
Compare
Checklist
Description
Group submission access logs together to make it easier to view and understand large collections of access logs.
Notes
Model changes:
Signal changes:
Adds 2 new signals.
Api changes:
fields
andannotate
to group access logs by thesubmission_group
, which should only be set on SubmissionAccessLogs and SubmissionGroups. If there is nosubmission_group
, it groups by the id instead, so all other logs are effectively grouped with themselves.model_type
,app_name
,action
,andlog_type
). Removing these fields means the grouping clause doesn't have to check their equality each time.dict
s instead ofAuditLog
s./all
endpoint to be the username since that's the only really searchable field the requirements call for (besides date, which doesn't make sense for a text search)Replaces #5080
Depends on #5092