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.
Files changed (32) hide show
  1. logdetective/extractors.py +134 -23
  2. logdetective/logdetective.py +39 -23
  3. logdetective/models.py +26 -0
  4. logdetective/prompts-summary-first.yml +0 -2
  5. logdetective/prompts.yml +0 -3
  6. logdetective/server/compressors.py +7 -10
  7. logdetective/server/config.py +3 -2
  8. logdetective/server/database/base.py +31 -26
  9. logdetective/server/database/models/__init__.py +2 -2
  10. logdetective/server/database/models/exceptions.py +4 -0
  11. logdetective/server/database/models/koji.py +47 -30
  12. logdetective/server/database/models/merge_request_jobs.py +205 -186
  13. logdetective/server/database/models/metrics.py +87 -61
  14. logdetective/server/emoji.py +57 -55
  15. logdetective/server/exceptions.py +4 -0
  16. logdetective/server/gitlab.py +18 -11
  17. logdetective/server/llm.py +19 -10
  18. logdetective/server/metric.py +18 -13
  19. logdetective/server/models.py +65 -48
  20. logdetective/server/plot.py +13 -11
  21. logdetective/server/server.py +52 -30
  22. logdetective/server/templates/base_response.html.j2 +59 -0
  23. logdetective/server/templates/gitlab_full_comment.md.j2 +58 -53
  24. logdetective/server/templates/gitlab_short_comment.md.j2 +52 -47
  25. logdetective/server/utils.py +15 -27
  26. logdetective/utils.py +115 -49
  27. {logdetective-2.0.1.dist-info → logdetective-2.11.0.dist-info}/METADATA +95 -21
  28. logdetective-2.11.0.dist-info/RECORD +40 -0
  29. {logdetective-2.0.1.dist-info → logdetective-2.11.0.dist-info}/WHEEL +1 -1
  30. logdetective-2.0.1.dist-info/RECORD +0 -39
  31. {logdetective-2.0.1.dist-info → logdetective-2.11.0.dist-info}/entry_points.txt +0 -0
  32. {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 = Column(BigInteger, primary_key=True)
38
- forge = Column(Enum(Forge), nullable=False, index=True, comment="The forge name")
39
- project_id = Column(
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 = Column(
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 = Column(
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 = relationship("AnalyzeRequestMetrics", back_populates="mr_job")
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
- with transaction(commit=False) as session:
106
- mr = session.query(cls).filter_by(id=id_).first()
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
- with transaction(commit=False) as session:
126
- mr = (
127
- session.query(cls)
128
- .filter_by(
129
- forge=forge, project_id=project_id, mr_iid=mr_iid, job_id=job_id
130
- )
131
- .first()
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
- with transaction(commit=False) as session:
141
- comments = (
142
- session.query(cls)
143
- .filter(
144
- GitlabMergeRequestJobs.forge == forge,
145
- GitlabMergeRequestJobs.project_id == project_id,
146
- GitlabMergeRequestJobs.mr_iid == mr_iid,
147
- )
148
- .all()
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(forge, project_id, mr_iid, job_id)
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 = Column(BigInteger, primary_key=True)
184
- merge_request_job_id = Column(
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 = Column(Enum(Forge), nullable=False, index=True, comment="The forge name")
193
- comment_id = Column(
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 = Column(
200
- DateTime, nullable=False, comment="Timestamp when the comment was created"
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 = relationship("GitlabMergeRequestJobs", back_populates="comment")
208
- reactions = relationship("Reactions", back_populates="comment")
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
- with transaction(commit=False) as session:
258
- comment = session.query(cls).filter_by(id=id_).first()
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
- with transaction(commit=False) as session:
275
- comment = (
276
- session.query(cls)
277
- .join(
278
- GitlabMergeRequestJobs,
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
- with transaction(commit=False) as session:
305
- comment = (
306
- session.query(cls)
307
- .join(
308
- GitlabMergeRequestJobs,
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
- with transaction(commit=False) as session:
337
- comments = (
338
- session.query(cls)
339
- .join(
340
- GitlabMergeRequestJobs,
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
- with transaction(commit=False) as session:
383
- comments = (
384
- session.query(cls)
385
- .filter(
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
- with transaction(commit=False) as session:
399
- comments = (
400
- session.query(cls)
401
- .filter(
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 = Column(BigInteger, primary_key=True)
417
- comment_id = Column(
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 = Column(
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 = Column(
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(forge, project_id, mr_iid, job_id, comment_id)
466
- reaction = cls.get_reaction_by_type(
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
- with transaction(commit=False) as session:
500
- reactions = (
501
- session.query(cls)
502
- .join(Comments, cls.comment_id == Comments.id)
503
- .join(
504
- GitlabMergeRequestJobs,
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
- with transaction(commit=False) as session:
541
- reaction = (
542
- session.query(cls)
543
- .join(Comments, cls.comment_id == Comments.id)
544
- .join(
545
- GitlabMergeRequestJobs,
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
- with transaction(commit=False) as session:
597
- reactions = (
598
- session.query(Comments.created_at, cls)
599
- .join(Comments, cls.comment_id == Comments.id)
600
- .filter(Comments.created_at > time)
601
- .all()
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