Skip to content

Commit 733ba56

Browse files
lunikaAntoLC
authored andcommitted
✨(backend) add Comment model
In order to store the comments on a document, we created a new model Comment. User is nullable because anonymous users can comment a Document is this one is public with a link_role commentator.
1 parent 18b05ac commit 733ba56

File tree

4 files changed

+472
-0
lines changed

4 files changed

+472
-0
lines changed

src/backend/core/factories.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,14 @@ class Meta:
256256
document = factory.SubFactory(DocumentFactory)
257257
role = factory.fuzzy.FuzzyChoice([role[0] for role in models.RoleChoices.choices])
258258
issuer = factory.SubFactory(UserFactory)
259+
260+
261+
class CommentFactory(factory.django.DjangoModelFactory):
262+
"""A factory to create comments for a document"""
263+
264+
class Meta:
265+
model = models.Comment
266+
267+
document = factory.SubFactory(DocumentFactory)
268+
user = factory.SubFactory(UserFactory)
269+
content = factory.Faker("text")
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Generated by Django 5.2.4 on 2025-08-26 08:11
2+
3+
import uuid
4+
5+
import django.db.models.deletion
6+
from django.conf import settings
7+
from django.db import migrations, models
8+
9+
10+
class Migration(migrations.Migration):
11+
dependencies = [
12+
("core", "0024_add_is_masked_field_to_link_trace"),
13+
]
14+
15+
operations = [
16+
migrations.AlterField(
17+
model_name="document",
18+
name="link_role",
19+
field=models.CharField(
20+
choices=[
21+
("reader", "Reader"),
22+
("commentator", "Commentator"),
23+
("editor", "Editor"),
24+
],
25+
default="reader",
26+
max_length=20,
27+
),
28+
),
29+
migrations.AlterField(
30+
model_name="documentaccess",
31+
name="role",
32+
field=models.CharField(
33+
choices=[
34+
("reader", "Reader"),
35+
("commentator", "Commentator"),
36+
("editor", "Editor"),
37+
("administrator", "Administrator"),
38+
("owner", "Owner"),
39+
],
40+
default="reader",
41+
max_length=20,
42+
),
43+
),
44+
migrations.AlterField(
45+
model_name="documentaskforaccess",
46+
name="role",
47+
field=models.CharField(
48+
choices=[
49+
("reader", "Reader"),
50+
("commentator", "Commentator"),
51+
("editor", "Editor"),
52+
("administrator", "Administrator"),
53+
("owner", "Owner"),
54+
],
55+
default="reader",
56+
max_length=20,
57+
),
58+
),
59+
migrations.AlterField(
60+
model_name="invitation",
61+
name="role",
62+
field=models.CharField(
63+
choices=[
64+
("reader", "Reader"),
65+
("commentator", "Commentator"),
66+
("editor", "Editor"),
67+
("administrator", "Administrator"),
68+
("owner", "Owner"),
69+
],
70+
default="reader",
71+
max_length=20,
72+
),
73+
),
74+
migrations.AlterField(
75+
model_name="templateaccess",
76+
name="role",
77+
field=models.CharField(
78+
choices=[
79+
("reader", "Reader"),
80+
("commentator", "Commentator"),
81+
("editor", "Editor"),
82+
("administrator", "Administrator"),
83+
("owner", "Owner"),
84+
],
85+
default="reader",
86+
max_length=20,
87+
),
88+
),
89+
migrations.CreateModel(
90+
name="Comment",
91+
fields=[
92+
(
93+
"id",
94+
models.UUIDField(
95+
default=uuid.uuid4,
96+
editable=False,
97+
help_text="primary key for the record as UUID",
98+
primary_key=True,
99+
serialize=False,
100+
verbose_name="id",
101+
),
102+
),
103+
(
104+
"created_at",
105+
models.DateTimeField(
106+
auto_now_add=True,
107+
help_text="date and time at which a record was created",
108+
verbose_name="created on",
109+
),
110+
),
111+
(
112+
"updated_at",
113+
models.DateTimeField(
114+
auto_now=True,
115+
help_text="date and time at which a record was last updated",
116+
verbose_name="updated on",
117+
),
118+
),
119+
("content", models.TextField()),
120+
(
121+
"document",
122+
models.ForeignKey(
123+
on_delete=django.db.models.deletion.CASCADE,
124+
related_name="comments",
125+
to="core.document",
126+
),
127+
),
128+
(
129+
"user",
130+
models.ForeignKey(
131+
blank=True,
132+
null=True,
133+
on_delete=django.db.models.deletion.SET_NULL,
134+
related_name="comments",
135+
to=settings.AUTH_USER_MODEL,
136+
),
137+
),
138+
],
139+
options={
140+
"verbose_name": "Comment",
141+
"verbose_name_plural": "Comments",
142+
"db_table": "impress_comment",
143+
"ordering": ("-created_at",),
144+
},
145+
),
146+
]

src/backend/core/models.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,48 @@ def send_ask_for_access_email(self, email, language=None):
12821282
self.document.send_email(subject, [email], context, language)
12831283

12841284

1285+
class Comment(BaseModel):
1286+
"""User comment on a document."""
1287+
1288+
document = models.ForeignKey(
1289+
Document,
1290+
on_delete=models.CASCADE,
1291+
related_name="comments",
1292+
)
1293+
user = models.ForeignKey(
1294+
User,
1295+
on_delete=models.SET_NULL,
1296+
related_name="comments",
1297+
null=True,
1298+
blank=True,
1299+
)
1300+
content = models.TextField()
1301+
1302+
class Meta:
1303+
db_table = "impress_comment"
1304+
ordering = ("-created_at",)
1305+
verbose_name = _("Comment")
1306+
verbose_name_plural = _("Comments")
1307+
1308+
def __str__(self):
1309+
author = self.user or _("Anonymous")
1310+
return f"{author!s} on {self.document!s}"
1311+
1312+
def get_abilities(self, user):
1313+
"""Compute and return abilities for a given user."""
1314+
role = self.document.get_role(user)
1315+
can_comment = self.document.get_abilities(user)["comment"]
1316+
return {
1317+
"destroy": self.user == user
1318+
or role in [RoleChoices.OWNER, RoleChoices.ADMIN],
1319+
"update": self.user == user
1320+
or role in [RoleChoices.OWNER, RoleChoices.ADMIN],
1321+
"partial_update": self.user == user
1322+
or role in [RoleChoices.OWNER, RoleChoices.ADMIN],
1323+
"retrieve": can_comment,
1324+
}
1325+
1326+
12851327
class Template(BaseModel):
12861328
"""HTML and CSS code used for formatting the print around the MarkDown body."""
12871329

0 commit comments

Comments
 (0)