logdetective 2.0.1__py3-none-any.whl → 2.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- logdetective/extractors.py +134 -23
- logdetective/logdetective.py +39 -23
- logdetective/models.py +26 -0
- logdetective/prompts-summary-first.yml +0 -2
- logdetective/prompts.yml +0 -3
- logdetective/server/compressors.py +7 -10
- logdetective/server/config.py +3 -2
- logdetective/server/database/base.py +31 -26
- logdetective/server/database/models/__init__.py +2 -2
- logdetective/server/database/models/exceptions.py +4 -0
- logdetective/server/database/models/koji.py +47 -30
- logdetective/server/database/models/merge_request_jobs.py +205 -186
- logdetective/server/database/models/metrics.py +87 -61
- logdetective/server/emoji.py +57 -55
- logdetective/server/exceptions.py +4 -0
- logdetective/server/gitlab.py +18 -11
- logdetective/server/llm.py +19 -10
- logdetective/server/metric.py +18 -13
- logdetective/server/models.py +65 -48
- logdetective/server/plot.py +13 -11
- logdetective/server/server.py +52 -30
- logdetective/server/templates/base_response.html.j2 +59 -0
- logdetective/server/templates/gitlab_full_comment.md.j2 +58 -53
- logdetective/server/templates/gitlab_short_comment.md.j2 +52 -47
- logdetective/server/utils.py +15 -27
- logdetective/utils.py +115 -49
- {logdetective-2.0.1.dist-info → logdetective-2.11.0.dist-info}/METADATA +95 -21
- logdetective-2.11.0.dist-info/RECORD +40 -0
- {logdetective-2.0.1.dist-info → logdetective-2.11.0.dist-info}/WHEEL +1 -1
- logdetective-2.0.1.dist-info/RECORD +0 -39
- {logdetective-2.0.1.dist-info → logdetective-2.11.0.dist-info}/entry_points.txt +0 -0
- {logdetective-2.0.1.dist-info → logdetective-2.11.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,25 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import enum
|
|
2
3
|
import datetime
|
|
3
|
-
from typing import Optional, List, Tuple, Self
|
|
4
|
+
from typing import Optional, List, Tuple, Self, TYPE_CHECKING
|
|
4
5
|
|
|
5
6
|
import backoff
|
|
6
7
|
|
|
7
8
|
from sqlalchemy import (
|
|
8
9
|
Enum,
|
|
9
|
-
Column,
|
|
10
10
|
BigInteger,
|
|
11
11
|
DateTime,
|
|
12
12
|
String,
|
|
13
13
|
ForeignKey,
|
|
14
14
|
UniqueConstraint,
|
|
15
15
|
desc,
|
|
16
|
+
select,
|
|
16
17
|
)
|
|
17
|
-
from sqlalchemy.orm import relationship
|
|
18
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
18
19
|
from sqlalchemy.engine import Row
|
|
19
20
|
from sqlalchemy.exc import OperationalError
|
|
20
21
|
from logdetective.server.database.base import Base, transaction, DB_MAX_RETRIES
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from .metrics import AnalyzeRequestMetrics
|
|
26
|
+
|
|
27
|
+
|
|
23
28
|
class Forge(str, enum.Enum):
|
|
24
29
|
"""List of forges managed by logdetective"""
|
|
25
30
|
|
|
@@ -34,21 +39,26 @@ class GitlabMergeRequestJobs(Base):
|
|
|
34
39
|
|
|
35
40
|
__tablename__ = "gitlab_merge_request_jobs"
|
|
36
41
|
|
|
37
|
-
id =
|
|
38
|
-
forge
|
|
39
|
-
|
|
42
|
+
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
43
|
+
forge: Mapped[Forge] = mapped_column(
|
|
44
|
+
Enum(Forge),
|
|
45
|
+
nullable=False,
|
|
46
|
+
index=True,
|
|
47
|
+
comment="The forge name"
|
|
48
|
+
)
|
|
49
|
+
project_id: Mapped[int] = mapped_column(
|
|
40
50
|
BigInteger,
|
|
41
51
|
nullable=False,
|
|
42
52
|
index=True,
|
|
43
53
|
comment="The project gitlab id",
|
|
44
54
|
)
|
|
45
|
-
mr_iid =
|
|
55
|
+
mr_iid: Mapped[int] = mapped_column(
|
|
46
56
|
BigInteger,
|
|
47
57
|
nullable=False,
|
|
48
58
|
index=False,
|
|
49
59
|
comment="The merge request gitlab iid",
|
|
50
60
|
)
|
|
51
|
-
job_id =
|
|
61
|
+
job_id: Mapped[int] = mapped_column(
|
|
52
62
|
BigInteger,
|
|
53
63
|
nullable=False,
|
|
54
64
|
index=True,
|
|
@@ -62,15 +72,18 @@ class GitlabMergeRequestJobs(Base):
|
|
|
62
72
|
),
|
|
63
73
|
)
|
|
64
74
|
|
|
65
|
-
comment = relationship(
|
|
75
|
+
comment: Mapped[List["Comments"]] = relationship(
|
|
66
76
|
"Comments", back_populates="merge_request_job", uselist=False
|
|
67
77
|
) # 1 comment for 1 job
|
|
68
78
|
|
|
69
|
-
request_metrics
|
|
79
|
+
request_metrics: Mapped[List["AnalyzeRequestMetrics"]] = relationship(
|
|
80
|
+
"AnalyzeRequestMetrics",
|
|
81
|
+
back_populates="mr_job"
|
|
82
|
+
)
|
|
70
83
|
|
|
71
84
|
@classmethod
|
|
72
85
|
@backoff.on_exception(backoff.expo, OperationalError, max_tries=DB_MAX_RETRIES)
|
|
73
|
-
def create(
|
|
86
|
+
async def create(
|
|
74
87
|
cls,
|
|
75
88
|
forge: Forge,
|
|
76
89
|
project_id: int,
|
|
@@ -86,28 +99,30 @@ class GitlabMergeRequestJobs(Base):
|
|
|
86
99
|
mr_iid: merge request forge iid
|
|
87
100
|
job_id: forge job id
|
|
88
101
|
"""
|
|
89
|
-
with transaction(commit=True) as session:
|
|
102
|
+
async with transaction(commit=True) as session:
|
|
90
103
|
mr = cls()
|
|
91
104
|
mr.forge = forge
|
|
92
105
|
mr.project_id = project_id
|
|
93
106
|
mr.mr_iid = mr_iid
|
|
94
107
|
mr.job_id = job_id
|
|
95
108
|
session.add(mr)
|
|
96
|
-
session.flush()
|
|
109
|
+
await session.flush()
|
|
97
110
|
return mr.id
|
|
98
111
|
|
|
99
112
|
@classmethod
|
|
100
|
-
def get_by_id(
|
|
113
|
+
async def get_by_id(
|
|
101
114
|
cls,
|
|
102
115
|
id_: int,
|
|
103
116
|
) -> Optional["GitlabMergeRequestJobs"]:
|
|
104
117
|
"""Search for a given PostgreSQL id"""
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
query = select(cls).where(cls.id == id_)
|
|
119
|
+
async with transaction(commit=False) as session:
|
|
120
|
+
query_result = await session.execute(query)
|
|
121
|
+
mr = query_result.scalars().first()
|
|
107
122
|
return mr
|
|
108
123
|
|
|
109
124
|
@classmethod
|
|
110
|
-
def get_by_details(
|
|
125
|
+
async def get_by_details(
|
|
111
126
|
cls,
|
|
112
127
|
forge: Forge,
|
|
113
128
|
project_id: int,
|
|
@@ -122,36 +137,34 @@ class GitlabMergeRequestJobs(Base):
|
|
|
122
137
|
mr_iid: merge request forge iid
|
|
123
138
|
job_id: forge job id
|
|
124
139
|
"""
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
)
|
|
140
|
+
query = select(cls).where(
|
|
141
|
+
cls.forge == forge,
|
|
142
|
+
cls.project_id == project_id,
|
|
143
|
+
cls.mr_iid == mr_iid,
|
|
144
|
+
cls.job_id == job_id,
|
|
145
|
+
)
|
|
146
|
+
async with transaction(commit=False) as session:
|
|
147
|
+
query_result = await session.execute(query)
|
|
148
|
+
mr = query_result.scalars().first()
|
|
133
149
|
return mr
|
|
134
150
|
|
|
135
151
|
@classmethod
|
|
136
|
-
def get_by_mr_iid(
|
|
137
|
-
cls, forge: Forge, project_id: int, mr_iid
|
|
138
|
-
) -> List[Self]:
|
|
152
|
+
async def get_by_mr_iid(cls, forge: Forge, project_id: int, mr_iid) -> List[Self]:
|
|
139
153
|
"""Get all the mr jobs saved for the specified mr iid and project id."""
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
)
|
|
154
|
+
query = select(cls).where(
|
|
155
|
+
GitlabMergeRequestJobs.forge == forge,
|
|
156
|
+
GitlabMergeRequestJobs.project_id == project_id,
|
|
157
|
+
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
async with transaction(commit=False) as session:
|
|
161
|
+
query_result = await session.execute(query)
|
|
162
|
+
comments = query_result.scalars().all()
|
|
150
163
|
|
|
151
164
|
return comments
|
|
152
165
|
|
|
153
166
|
@classmethod
|
|
154
|
-
def get_or_create(
|
|
167
|
+
async def get_or_create(
|
|
155
168
|
cls,
|
|
156
169
|
forge: Forge,
|
|
157
170
|
project_id: int,
|
|
@@ -167,10 +180,12 @@ class GitlabMergeRequestJobs(Base):
|
|
|
167
180
|
mr_iid: merge request forge iid
|
|
168
181
|
job_id: forge job id
|
|
169
182
|
"""
|
|
170
|
-
mr = GitlabMergeRequestJobs.get_by_details(
|
|
183
|
+
mr = await GitlabMergeRequestJobs.get_by_details(
|
|
184
|
+
forge, project_id, mr_iid, job_id
|
|
185
|
+
)
|
|
171
186
|
if mr is None:
|
|
172
|
-
id_ = GitlabMergeRequestJobs.create(forge, project_id, mr_iid, job_id)
|
|
173
|
-
mr = GitlabMergeRequestJobs.get_by_id(id_)
|
|
187
|
+
id_ = await GitlabMergeRequestJobs.create(forge, project_id, mr_iid, job_id)
|
|
188
|
+
mr = await GitlabMergeRequestJobs.get_by_id(id_)
|
|
174
189
|
return mr
|
|
175
190
|
|
|
176
191
|
|
|
@@ -180,8 +195,8 @@ class Comments(Base):
|
|
|
180
195
|
|
|
181
196
|
__tablename__ = "comments"
|
|
182
197
|
|
|
183
|
-
id =
|
|
184
|
-
merge_request_job_id =
|
|
198
|
+
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
199
|
+
merge_request_job_id: Mapped[int] = mapped_column(
|
|
185
200
|
BigInteger,
|
|
186
201
|
ForeignKey("gitlab_merge_request_jobs.id"),
|
|
187
202
|
nullable=False,
|
|
@@ -189,27 +204,37 @@ class Comments(Base):
|
|
|
189
204
|
index=True,
|
|
190
205
|
comment="The associated merge request job (db) id",
|
|
191
206
|
)
|
|
192
|
-
forge
|
|
193
|
-
|
|
207
|
+
forge: Mapped[Forge] = mapped_column(
|
|
208
|
+
Enum(Forge),
|
|
209
|
+
nullable=False,
|
|
210
|
+
index=True,
|
|
211
|
+
comment="The forge name"
|
|
212
|
+
)
|
|
213
|
+
comment_id: Mapped[str] = mapped_column(
|
|
194
214
|
String(50), # e.g. 'd5a3ff139356ce33e37e73add446f16869741b50'
|
|
195
215
|
nullable=False,
|
|
196
216
|
index=True,
|
|
197
217
|
comment="The comment gitlab id",
|
|
198
218
|
)
|
|
199
|
-
created_at =
|
|
200
|
-
DateTime
|
|
219
|
+
created_at: Mapped[datetime.datetime] = mapped_column(
|
|
220
|
+
DateTime(timezone=True),
|
|
221
|
+
nullable=False,
|
|
222
|
+
comment="Timestamp when the comment was created",
|
|
201
223
|
)
|
|
202
224
|
|
|
203
225
|
__table_args__ = (
|
|
204
226
|
UniqueConstraint("forge", "comment_id", name="uix_forge_comment_id"),
|
|
205
227
|
)
|
|
206
228
|
|
|
207
|
-
merge_request_job
|
|
208
|
-
|
|
229
|
+
merge_request_job: Mapped["GitlabMergeRequestJobs"] = relationship(
|
|
230
|
+
"GitlabMergeRequestJobs",
|
|
231
|
+
back_populates="comment"
|
|
232
|
+
)
|
|
233
|
+
reactions: Mapped[list["Reactions"]] = relationship("Reactions", back_populates="comment")
|
|
209
234
|
|
|
210
235
|
@classmethod
|
|
211
236
|
@backoff.on_exception(backoff.expo, OperationalError, max_tries=DB_MAX_RETRIES)
|
|
212
|
-
def create( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
237
|
+
async def create( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
213
238
|
cls,
|
|
214
239
|
forge: Forge,
|
|
215
240
|
project_id: int,
|
|
@@ -230,8 +255,8 @@ class Comments(Base):
|
|
|
230
255
|
job_id: forge job id
|
|
231
256
|
comment_id: forge comment id
|
|
232
257
|
"""
|
|
233
|
-
with transaction(commit=True) as session:
|
|
234
|
-
mr_job = GitlabMergeRequestJobs.get_or_create(
|
|
258
|
+
async with transaction(commit=True) as session:
|
|
259
|
+
mr_job = await GitlabMergeRequestJobs.get_or_create(
|
|
235
260
|
forge, project_id, mr_iid, job_id
|
|
236
261
|
)
|
|
237
262
|
|
|
@@ -245,21 +270,23 @@ class Comments(Base):
|
|
|
245
270
|
comment.created_at = datetime.datetime.now(datetime.timezone.utc)
|
|
246
271
|
comment.merge_request_job_id = mr_job.id
|
|
247
272
|
session.add(comment)
|
|
248
|
-
session.flush()
|
|
273
|
+
await session.flush()
|
|
249
274
|
return comment.id
|
|
250
275
|
|
|
251
276
|
@classmethod
|
|
252
|
-
def get_by_id(
|
|
277
|
+
async def get_by_id(
|
|
253
278
|
cls,
|
|
254
279
|
id_: int,
|
|
255
280
|
) -> Optional["Comments"]:
|
|
256
281
|
"""Search for a given PostgreSQL id"""
|
|
257
|
-
|
|
258
|
-
|
|
282
|
+
query = select(cls).where(cls.id == id_)
|
|
283
|
+
async with transaction(commit=False) as session:
|
|
284
|
+
query_result = await session.execute(query)
|
|
285
|
+
comment = query_result.scalars().first()
|
|
259
286
|
return comment
|
|
260
287
|
|
|
261
288
|
@classmethod
|
|
262
|
-
def get_by_gitlab_id(
|
|
289
|
+
async def get_by_gitlab_id(
|
|
263
290
|
cls,
|
|
264
291
|
forge: Forge,
|
|
265
292
|
comment_id: str,
|
|
@@ -271,24 +298,21 @@ class Comments(Base):
|
|
|
271
298
|
forge: forge name
|
|
272
299
|
comment_id: forge comment id
|
|
273
300
|
"""
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
cls.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
280
|
-
)
|
|
281
|
-
.filter(
|
|
282
|
-
GitlabMergeRequestJobs.forge == forge,
|
|
283
|
-
cls.comment_id == comment_id,
|
|
284
|
-
)
|
|
285
|
-
.first()
|
|
301
|
+
query = (
|
|
302
|
+
select(cls)
|
|
303
|
+
.join(
|
|
304
|
+
GitlabMergeRequestJobs,
|
|
305
|
+
cls.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
286
306
|
)
|
|
287
|
-
|
|
307
|
+
.filter(GitlabMergeRequestJobs.forge == forge, cls.comment_id == comment_id)
|
|
308
|
+
)
|
|
309
|
+
async with transaction(commit=False) as session:
|
|
310
|
+
query_result = await session.execute(query)
|
|
311
|
+
comment = query_result.scalars().first()
|
|
288
312
|
return comment
|
|
289
313
|
|
|
290
314
|
@classmethod
|
|
291
|
-
def get_latest_comment(
|
|
315
|
+
async def get_latest_comment(
|
|
292
316
|
cls,
|
|
293
317
|
forge: Forge,
|
|
294
318
|
project_id: int,
|
|
@@ -301,26 +325,26 @@ class Comments(Base):
|
|
|
301
325
|
project_id: forge project id
|
|
302
326
|
mr_iid: merge request forge iid
|
|
303
327
|
"""
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
cls.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
310
|
-
)
|
|
311
|
-
.filter(
|
|
312
|
-
GitlabMergeRequestJobs.forge == forge,
|
|
313
|
-
GitlabMergeRequestJobs.project_id == project_id,
|
|
314
|
-
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
315
|
-
)
|
|
316
|
-
.order_by(desc(cls.created_at))
|
|
317
|
-
.first()
|
|
328
|
+
query = (
|
|
329
|
+
select(cls)
|
|
330
|
+
.join(
|
|
331
|
+
GitlabMergeRequestJobs,
|
|
332
|
+
cls.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
318
333
|
)
|
|
319
|
-
|
|
334
|
+
.filter(
|
|
335
|
+
GitlabMergeRequestJobs.forge == forge,
|
|
336
|
+
GitlabMergeRequestJobs.project_id == project_id,
|
|
337
|
+
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
338
|
+
)
|
|
339
|
+
.order_by(desc(cls.created_at))
|
|
340
|
+
)
|
|
341
|
+
async with transaction(commit=False) as session:
|
|
342
|
+
query_result = await session.execute(query)
|
|
343
|
+
comment = query_result.scalars().first()
|
|
320
344
|
return comment
|
|
321
345
|
|
|
322
346
|
@classmethod
|
|
323
|
-
def get_mr_comments(
|
|
347
|
+
async def get_mr_comments(
|
|
324
348
|
cls,
|
|
325
349
|
forge: Forge,
|
|
326
350
|
project_id: int,
|
|
@@ -333,26 +357,26 @@ class Comments(Base):
|
|
|
333
357
|
project_id: forge project id
|
|
334
358
|
mr_iid: merge request forge iid
|
|
335
359
|
"""
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
cls.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
342
|
-
)
|
|
343
|
-
.filter(
|
|
344
|
-
GitlabMergeRequestJobs.forge == forge,
|
|
345
|
-
GitlabMergeRequestJobs.project_id == project_id,
|
|
346
|
-
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
347
|
-
)
|
|
348
|
-
.order_by(desc(cls.created_at))
|
|
349
|
-
.all()
|
|
360
|
+
query = (
|
|
361
|
+
select(cls)
|
|
362
|
+
.join(
|
|
363
|
+
GitlabMergeRequestJobs,
|
|
364
|
+
cls.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
350
365
|
)
|
|
351
|
-
|
|
366
|
+
.filter(
|
|
367
|
+
GitlabMergeRequestJobs.forge == forge,
|
|
368
|
+
GitlabMergeRequestJobs.project_id == project_id,
|
|
369
|
+
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
370
|
+
)
|
|
371
|
+
.order_by(desc(cls.created_at))
|
|
372
|
+
)
|
|
373
|
+
async with transaction(commit=False) as session:
|
|
374
|
+
query_result = await session.execute(query)
|
|
375
|
+
comments = query_result.scalars().all()
|
|
352
376
|
return comments
|
|
353
377
|
|
|
354
378
|
@classmethod
|
|
355
|
-
def get_or_create( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
379
|
+
async def get_or_create( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
356
380
|
cls,
|
|
357
381
|
forge: Forge,
|
|
358
382
|
project_id: int,
|
|
@@ -370,40 +394,31 @@ class Comments(Base):
|
|
|
370
394
|
job_id: forge job id
|
|
371
395
|
comment_id: forge comment id
|
|
372
396
|
"""
|
|
373
|
-
comment = Comments.get_by_gitlab_id(forge, comment_id)
|
|
397
|
+
comment = await Comments.get_by_gitlab_id(forge, comment_id)
|
|
374
398
|
if comment is None:
|
|
375
|
-
id_ = Comments.create(forge, project_id, mr_iid, job_id, comment_id)
|
|
376
|
-
comment = Comments.get_by_id(id_)
|
|
399
|
+
id_ = await Comments.create(forge, project_id, mr_iid, job_id, comment_id)
|
|
400
|
+
comment = await Comments.get_by_id(id_)
|
|
377
401
|
return comment
|
|
378
402
|
|
|
379
403
|
@classmethod
|
|
380
|
-
def get_since(cls, time: datetime.datetime) -> List[Self]:
|
|
404
|
+
async def get_since(cls, time: datetime.datetime) -> List[Self]:
|
|
381
405
|
"""Get all the comments created after the given time."""
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
Comments.created_at > time,
|
|
387
|
-
)
|
|
388
|
-
.all()
|
|
389
|
-
)
|
|
406
|
+
query = select(cls).filter(Comments.created_at > time)
|
|
407
|
+
async with transaction(commit=False) as session:
|
|
408
|
+
query_result = await session.execute(query)
|
|
409
|
+
comments = query_result.scalars().all()
|
|
390
410
|
|
|
391
411
|
return comments
|
|
392
412
|
|
|
393
413
|
@classmethod
|
|
394
|
-
def get_by_mr_job(
|
|
414
|
+
async def get_by_mr_job(
|
|
395
415
|
cls, merge_request_job: GitlabMergeRequestJobs
|
|
396
416
|
) -> Optional["Comments"]:
|
|
397
417
|
"""Get the comment added for the specified merge request's job."""
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
Comments.merge_request_job == merge_request_job,
|
|
403
|
-
)
|
|
404
|
-
.first() # just one
|
|
405
|
-
)
|
|
406
|
-
|
|
418
|
+
query = select(cls).filter(Comments.merge_request_job == merge_request_job)
|
|
419
|
+
async with transaction(commit=False) as session:
|
|
420
|
+
query_result = await session.execute(query)
|
|
421
|
+
comments = query_result.scalars().first()
|
|
407
422
|
return comments
|
|
408
423
|
|
|
409
424
|
|
|
@@ -413,20 +428,20 @@ class Reactions(Base):
|
|
|
413
428
|
|
|
414
429
|
__tablename__ = "reactions"
|
|
415
430
|
|
|
416
|
-
id =
|
|
417
|
-
comment_id =
|
|
431
|
+
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
432
|
+
comment_id: Mapped[int] = mapped_column(
|
|
418
433
|
BigInteger,
|
|
419
434
|
ForeignKey("comments.id"),
|
|
420
435
|
nullable=False,
|
|
421
436
|
index=True,
|
|
422
437
|
comment="The associated comment (db) id",
|
|
423
438
|
)
|
|
424
|
-
reaction_type =
|
|
439
|
+
reaction_type: Mapped[str] = mapped_column(
|
|
425
440
|
String(127), # e.g. 'thumbs-up'
|
|
426
441
|
nullable=False,
|
|
427
442
|
comment="The type of reaction",
|
|
428
443
|
)
|
|
429
|
-
count =
|
|
444
|
+
count: Mapped[int] = mapped_column(
|
|
430
445
|
BigInteger,
|
|
431
446
|
nullable=False,
|
|
432
447
|
comment="The number of reactions, of this type, given in the comment",
|
|
@@ -436,11 +451,11 @@ class Reactions(Base):
|
|
|
436
451
|
UniqueConstraint("comment_id", "reaction_type", name="uix_comment_reaction"),
|
|
437
452
|
)
|
|
438
453
|
|
|
439
|
-
comment = relationship("Comments", back_populates="reactions")
|
|
454
|
+
comment: Mapped["Comments"] = relationship("Comments", back_populates="reactions")
|
|
440
455
|
|
|
441
456
|
@classmethod
|
|
442
457
|
@backoff.on_exception(backoff.expo, OperationalError, max_tries=DB_MAX_RETRIES)
|
|
443
|
-
def create_or_update( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
458
|
+
async def create_or_update( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
444
459
|
cls,
|
|
445
460
|
forge: Forge,
|
|
446
461
|
project_id: int,
|
|
@@ -462,11 +477,13 @@ class Reactions(Base):
|
|
|
462
477
|
reaction_type: a str, ex. thumb_up
|
|
463
478
|
count: number of reactions, of this type, given in the comment
|
|
464
479
|
"""
|
|
465
|
-
comment = Comments.get_or_create(
|
|
466
|
-
|
|
480
|
+
comment = await Comments.get_or_create(
|
|
481
|
+
forge, project_id, mr_iid, job_id, comment_id
|
|
482
|
+
)
|
|
483
|
+
reaction = await cls.get_reaction_by_type(
|
|
467
484
|
forge, project_id, mr_iid, job_id, comment_id, reaction_type
|
|
468
485
|
)
|
|
469
|
-
with transaction(commit=True) as session:
|
|
486
|
+
async with transaction(commit=True) as session:
|
|
470
487
|
if reaction:
|
|
471
488
|
reaction.count = count # just update
|
|
472
489
|
else:
|
|
@@ -475,11 +492,11 @@ class Reactions(Base):
|
|
|
475
492
|
reaction.reaction_type = reaction_type
|
|
476
493
|
reaction.count = count
|
|
477
494
|
session.add(reaction)
|
|
478
|
-
session.flush()
|
|
495
|
+
await session.flush()
|
|
479
496
|
return reaction.id
|
|
480
497
|
|
|
481
498
|
@classmethod
|
|
482
|
-
def get_all_reactions( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
499
|
+
async def get_all_reactions( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
483
500
|
cls,
|
|
484
501
|
forge: Forge,
|
|
485
502
|
project_id: int,
|
|
@@ -496,28 +513,28 @@ class Reactions(Base):
|
|
|
496
513
|
job_id: forge job id
|
|
497
514
|
comment_id: forge comment id
|
|
498
515
|
"""
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
Comments.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
506
|
-
)
|
|
507
|
-
.filter(
|
|
508
|
-
Comments.comment_id == comment_id,
|
|
509
|
-
GitlabMergeRequestJobs.forge == forge,
|
|
510
|
-
GitlabMergeRequestJobs.project_id == project_id,
|
|
511
|
-
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
512
|
-
GitlabMergeRequestJobs.job_id == job_id,
|
|
513
|
-
)
|
|
514
|
-
.all()
|
|
516
|
+
query = (
|
|
517
|
+
select(cls)
|
|
518
|
+
.join(Comments, cls.comment_id == Comments.id)
|
|
519
|
+
.join(
|
|
520
|
+
GitlabMergeRequestJobs,
|
|
521
|
+
Comments.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
515
522
|
)
|
|
516
|
-
|
|
523
|
+
.filter(
|
|
524
|
+
Comments.comment_id == comment_id,
|
|
525
|
+
GitlabMergeRequestJobs.forge == forge,
|
|
526
|
+
GitlabMergeRequestJobs.project_id == project_id,
|
|
527
|
+
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
528
|
+
GitlabMergeRequestJobs.job_id == job_id,
|
|
529
|
+
)
|
|
530
|
+
)
|
|
531
|
+
async with transaction(commit=False) as session:
|
|
532
|
+
query_result = await session.execute(query)
|
|
533
|
+
reactions = query_result.scalars().all()
|
|
517
534
|
return reactions
|
|
518
535
|
|
|
519
536
|
@classmethod
|
|
520
|
-
def get_reaction_by_type( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
537
|
+
async def get_reaction_by_type( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
521
538
|
cls,
|
|
522
539
|
forge: Forge,
|
|
523
540
|
project_id: int,
|
|
@@ -537,30 +554,30 @@ class Reactions(Base):
|
|
|
537
554
|
comment_id: forge comment id
|
|
538
555
|
reaction_type: str like "thumb-up"
|
|
539
556
|
"""
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
Comments.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
547
|
-
)
|
|
548
|
-
.filter(
|
|
549
|
-
Comments.comment_id == comment_id,
|
|
550
|
-
GitlabMergeRequestJobs.forge == forge,
|
|
551
|
-
GitlabMergeRequestJobs.project_id == project_id,
|
|
552
|
-
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
553
|
-
GitlabMergeRequestJobs.job_id == job_id,
|
|
554
|
-
Reactions.reaction_type == reaction_type,
|
|
555
|
-
)
|
|
556
|
-
.first()
|
|
557
|
+
query = (
|
|
558
|
+
select(cls)
|
|
559
|
+
.join(Comments, cls.comment_id == Comments.id)
|
|
560
|
+
.join(
|
|
561
|
+
GitlabMergeRequestJobs,
|
|
562
|
+
Comments.merge_request_job_id == GitlabMergeRequestJobs.id,
|
|
557
563
|
)
|
|
558
|
-
|
|
564
|
+
.filter(
|
|
565
|
+
Comments.comment_id == comment_id,
|
|
566
|
+
GitlabMergeRequestJobs.forge == forge,
|
|
567
|
+
GitlabMergeRequestJobs.project_id == project_id,
|
|
568
|
+
GitlabMergeRequestJobs.mr_iid == mr_iid,
|
|
569
|
+
GitlabMergeRequestJobs.job_id == job_id,
|
|
570
|
+
Reactions.reaction_type == reaction_type,
|
|
571
|
+
)
|
|
572
|
+
)
|
|
573
|
+
async with transaction(commit=False) as session:
|
|
574
|
+
query_result = await session.execute(query)
|
|
575
|
+
reaction = query_result.scalars().first()
|
|
559
576
|
return reaction
|
|
560
577
|
|
|
561
578
|
@classmethod
|
|
562
579
|
@backoff.on_exception(backoff.expo, OperationalError, max_tries=DB_MAX_RETRIES)
|
|
563
|
-
def delete( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
580
|
+
async def delete( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
564
581
|
cls,
|
|
565
582
|
forge: Forge,
|
|
566
583
|
project_id: int,
|
|
@@ -580,25 +597,27 @@ class Reactions(Base):
|
|
|
580
597
|
reaction_type: a str iterable, ex. ['thumbsup', 'thumbsdown']
|
|
581
598
|
"""
|
|
582
599
|
for reaction_type in reaction_types:
|
|
583
|
-
reaction = cls.get_reaction_by_type(
|
|
600
|
+
reaction = await cls.get_reaction_by_type(
|
|
584
601
|
forge, project_id, mr_iid, job_id, comment_id, reaction_type
|
|
585
602
|
)
|
|
586
|
-
with transaction(commit=True) as session:
|
|
587
|
-
session.delete(reaction)
|
|
588
|
-
session.flush()
|
|
603
|
+
async with transaction(commit=True) as session:
|
|
604
|
+
await session.delete(reaction)
|
|
605
|
+
await session.flush()
|
|
589
606
|
|
|
590
607
|
@classmethod
|
|
591
|
-
def get_since(
|
|
608
|
+
async def get_since(
|
|
592
609
|
cls, time: datetime.datetime
|
|
593
610
|
) -> List[Row[Tuple[datetime.datetime, Self]]]:
|
|
594
611
|
"""Get all the reactions on comments created after the given time
|
|
595
612
|
and the comment creation time."""
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
613
|
+
query = (
|
|
614
|
+
select(Comments.created_at, cls)
|
|
615
|
+
.join(Comments, cls.comment_id == Comments.id)
|
|
616
|
+
.filter(Comments.created_at > time)
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
async with transaction(commit=False) as session:
|
|
620
|
+
query_results = await session.execute(query)
|
|
621
|
+
reactions = query_results.all()
|
|
603
622
|
|
|
604
623
|
return reactions
|