Skip to content

Commit de6b759

Browse files
andrepapotivictor-accarini
authored andcommitted
models: add attention set to patches
Patches that needs the attention of a specific user can now be linked to them by the attention set field. Attention flags by default are soft deleted. Signed-off-by: andrepapoti <[email protected]> Signed-off-by: Victor Accarini <[email protected]>
1 parent 1c3e4d4 commit de6b759

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Generated by Django 5.1.7 on 2025-03-22 05:06
2+
3+
import django.db.models.deletion
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
dependencies = [
10+
('patchwork', '0048_series_dependencies'),
11+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name='PatchAttentionSet',
17+
fields=[
18+
(
19+
'id',
20+
models.AutoField(
21+
auto_created=True,
22+
primary_key=True,
23+
serialize=False,
24+
verbose_name='ID',
25+
),
26+
),
27+
('last_updated', models.DateTimeField(auto_now=True)),
28+
('removed', models.BooleanField(default=False)),
29+
(
30+
'removed_reason',
31+
models.CharField(blank=True, max_length=50),
32+
),
33+
(
34+
'patch',
35+
models.ForeignKey(
36+
on_delete=django.db.models.deletion.CASCADE,
37+
to='patchwork.patch',
38+
),
39+
),
40+
(
41+
'user',
42+
models.ForeignKey(
43+
on_delete=django.db.models.deletion.CASCADE,
44+
to=settings.AUTH_USER_MODEL,
45+
),
46+
),
47+
],
48+
options={
49+
'unique_together': {('patch', 'user')},
50+
},
51+
),
52+
migrations.AddField(
53+
model_name='patch',
54+
name='attention_set',
55+
field=models.ManyToManyField(
56+
related_name='attention_set',
57+
through='patchwork.PatchAttentionSet',
58+
to=settings.AUTH_USER_MODEL,
59+
),
60+
),
61+
]

patchwork/models.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ class Patch(SubmissionMixin):
503503
null=True,
504504
on_delete=models.CASCADE,
505505
)
506+
attention_set = models.ManyToManyField(
507+
User, through='PatchAttentionSet', related_name='attention_set'
508+
)
506509
state = models.ForeignKey(State, null=True, on_delete=models.CASCADE)
507510
archived = models.BooleanField(default=False)
508511
hash = HashField(null=True, blank=True, db_index=True)
@@ -827,6 +830,104 @@ class Meta:
827830
]
828831

829832

833+
class PatchAttentionSetManager(models.Manager):
834+
def get_queryset(self):
835+
return super().get_queryset().filter(removed=False)
836+
837+
def upsert(self, patch, users):
838+
"""Add or updates deleted attention set entries
839+
840+
:param patch: patch object to be updated
841+
:type patch: Patch
842+
:param users: list of users to be added to the attention set list
843+
:type users: list[int]
844+
"""
845+
qs = super().get_queryset().filter(patch=patch)
846+
847+
existing = {
848+
obj.user.id: obj for obj in qs.filter(user__in=users).all()
849+
}
850+
update_list = []
851+
for obj in existing.values():
852+
if obj.removed:
853+
obj.removed = False
854+
obj.removed_reason = ''
855+
update_list.append(obj)
856+
insert_list = [user for user in users if user not in existing.keys()]
857+
858+
qs.bulk_create(
859+
[PatchAttentionSet(patch=patch, user_id=id) for id in insert_list]
860+
)
861+
qs.bulk_update(update_list, ['removed', 'removed_reason'])
862+
863+
def soft_delete(self, patch, users, reason=''):
864+
"""Mark attention set entries as deleted
865+
866+
:param patch: patch object to be updated
867+
:type patch: Patch
868+
:param users: list of users to be added to the attention set list
869+
:type users: list[int]
870+
:param reason: reason for removal
871+
:type reason: string
872+
"""
873+
qs = super().get_queryset().filter(patch=patch)
874+
875+
existing = {
876+
obj.user.id: obj for obj in qs.filter(user__in=users).all()
877+
}
878+
update_list = []
879+
for obj in existing.values():
880+
if not obj.removed:
881+
obj.removed = True
882+
obj.removed_reason = reason
883+
update_list.append(obj)
884+
885+
self.bulk_update(update_list, ['removed', 'removed_reason'])
886+
887+
888+
class PatchAttentionSet(models.Model):
889+
patch = models.ForeignKey(Patch, on_delete=models.CASCADE)
890+
user = models.ForeignKey(User, on_delete=models.CASCADE)
891+
last_updated = models.DateTimeField(auto_now=True)
892+
removed = models.BooleanField(default=False)
893+
removed_reason = models.CharField(max_length=50, blank=True)
894+
895+
objects = PatchAttentionSetManager()
896+
raw_objects = models.Manager()
897+
898+
def delete(self):
899+
"""Soft deletes an user from the patch attention set"""
900+
self.removed = True
901+
self.removed_reason = 'reviewed or commented on the patch'
902+
self.save()
903+
904+
def __str__(self):
905+
return f'<{self.user} - {self.user.email}>'
906+
907+
class Meta:
908+
unique_together = [('patch', 'user')]
909+
910+
911+
def _remove_user_from_patch_attention_set(sender, instance, created, **kwargs):
912+
if created:
913+
submitter = instance.submitter
914+
patch = instance.patch
915+
if submitter.user:
916+
try:
917+
# Don't use the RelatedManager since it will execute a hard
918+
# delete
919+
PatchAttentionSet.objects.get(
920+
patch=patch, user=submitter.user
921+
).delete()
922+
except PatchAttentionSet.DoesNotExist:
923+
pass
924+
925+
926+
models.signals.post_save.connect(
927+
_remove_user_from_patch_attention_set, sender=PatchComment
928+
)
929+
930+
830931
class Series(FilenameMixin, models.Model):
831932
"""A collection of patches."""
832933

0 commit comments

Comments
 (0)