Skip to content

Commit 33d39a4

Browse files
Update review progress dynamically
1 parent 567be32 commit 33d39a4

File tree

7 files changed

+125
-30
lines changed

7 files changed

+125
-30
lines changed

src/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def launch_app() -> None:
2121
async def listen_for_messages() -> None:
2222
"""Listen for messages from the RabbitMQ queue."""
2323
if config.amqp_mode == "rabbitmq":
24-
logger.info("Starting AMQP consumer...")
24+
logger.info("Starting RabbitMQ consumer...")
2525
rabbitmq_client = AsyncRabbitMQClient()
2626
await rabbitmq_client.connect()
2727

src/reviews/constants.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
"""Constants for the reviews module."""
22

3-
from enum import StrEnum
3+
from enum import StrEnum, auto
44

55

66
class AMQPQueues(StrEnum):
77
"""Constants for the AMQP queues used by the reviews module."""
88

99
REVIEW_FILE_DIFF_QUEUE = "review-file-diffs"
10+
11+
12+
class ReviewStatus(StrEnum):
13+
"""Constants for the review status."""
14+
15+
queued = auto()
16+
processing = auto()
17+
available = auto()

src/reviews/dto/requests.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from pydantic import BaseModel
44

5-
from src.reviews.models.pull_requests import PullRequestFileChanges
5+
from src.reviews.models.pull_requests import FileReview, PullRequestFileChanges
66

77

88
class CreateReviewFromFileDiffRequest(BaseModel):
@@ -11,3 +11,18 @@ class CreateReviewFromFileDiffRequest(BaseModel):
1111
review_id: int
1212
review_status_id: int
1313
file_diffs: list[PullRequestFileChanges]
14+
15+
16+
class UpdateProgressRequest(BaseModel):
17+
"""Request to update the progress of a review."""
18+
19+
progress: int
20+
status: str
21+
22+
23+
class CompleteReviewRequest(BaseModel):
24+
"""Request to complete a review."""
25+
26+
review_id: int
27+
review_status_id: int
28+
file_reviews: list[FileReview]

src/reviews/dto/responses.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1 @@
11
"""Response DTOs for the reviews module."""
2-
3-
from pydantic import BaseModel
4-
5-
from src.reviews.models.pull_requests import FileReview
6-
7-
8-
class PRReviewResponse(BaseModel):
9-
"""Response for a pull request review."""
10-
11-
review_id: int
12-
review_status_id: int
13-
file_reviews: list[FileReview]

src/reviews/services/pull_requests_service.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
from src.common.tools.review_pull_request import ReviewPullRequest
77
from src.config import config
8-
from src.reviews.dto.responses import PRReviewResponse
8+
from src.reviews.constants import ReviewStatus
99
from src.reviews.models.pull_requests import FileReview, PullRequestFileChanges
10-
from src.utils.http import send_http_post_request
10+
from src.utils import api
1111

1212
logger = logging.getLogger(__name__)
1313

@@ -20,35 +20,51 @@ async def create_review_from_file_diffs(
2020
"""Create a pull request review from file diffs."""
2121
semaphore = asyncio.Semaphore(config.modules.reviews.max_concurrent_file_reviews)
2222

23+
total_files = len(file_diffs)
24+
reviewed_files = 0
25+
2326
review_tasks = [
2427
_review_file_diff(file_diff=file_diff, semaphore=semaphore)
2528
for file_diff in file_diffs
2629
]
27-
review_contents = await asyncio.gather(*review_tasks)
28-
file_names = [review.filename for review in file_diffs]
29-
patches = [review.patch for review in file_diffs]
30+
review_per_file: dict[str, str] = {}
31+
for task in asyncio.as_completed(review_tasks):
32+
review_per_file |= await task
33+
34+
reviewed_files += 1
35+
progress_value = int((reviewed_files / total_files) * 100)
36+
await api.update_progress(
37+
review_status_id,
38+
progress_value,
39+
ReviewStatus.processing,
40+
)
3041

31-
review = PRReviewResponse(
42+
patches_per_file = {file_diff.filename: file_diff.patch for file_diff in file_diffs}
43+
44+
file_reviews = [
45+
FileReview(
46+
filename=filename,
47+
content=content,
48+
patch=patches_per_file.get(filename, ""),
49+
)
50+
for filename, content in review_per_file.items()
51+
]
52+
await api.complete_review(
3253
review_id=review_id,
3354
review_status_id=review_status_id,
34-
file_reviews=[
35-
FileReview(filename=filename, content=content, patch=patch)
36-
for filename, content, patch in zip(file_names, review_contents, patches)
37-
],
55+
file_reviews=file_reviews,
3856
)
39-
url = f"{config.approved_api_url}/reviews/complete"
40-
await send_http_post_request(content=review.model_dump(), url=url)
4157

4258

4359
async def _review_file_diff(
4460
file_diff: PullRequestFileChanges,
4561
semaphore: asyncio.Semaphore,
46-
) -> str:
62+
) -> dict[str, str]:
4763
"""Create a review task for a single file.
4864
4965
:param file_changes: The pull request file changes.
5066
:param semaphore: The semaphore.
51-
:return: The review content.
67+
:return: A mapping from the file name to the review content.
5268
"""
5369
answer = ""
5470
async with semaphore:
@@ -57,4 +73,4 @@ async def _review_file_diff(
5773
async for review_content in review_content_iterator:
5874
answer += review_content
5975

60-
return answer
76+
return {file_diff.filename: answer}

src/utils/api.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Utility functions for making API requests."""
2+
3+
import logging
4+
5+
from src.config import config
6+
from src.reviews.constants import ReviewStatus
7+
from src.reviews.dto.requests import CompleteReviewRequest, UpdateProgressRequest
8+
from src.reviews.models.pull_requests import FileReview
9+
from src.utils.http import send_http_post_request, send_http_put_request
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
async def update_progress(
15+
review_status_id: int,
16+
progress: int,
17+
status: ReviewStatus,
18+
) -> None:
19+
"""Update progress of a review."""
20+
payload = UpdateProgressRequest(
21+
progress=progress,
22+
status=status,
23+
)
24+
25+
url = f"{config.approved_api_url}/review-status/{review_status_id}"
26+
logger.info(
27+
"Updating progress of review status %s to %s",
28+
review_status_id,
29+
progress,
30+
)
31+
await send_http_put_request(content=payload.model_dump(), url=url)
32+
33+
34+
async def complete_review(
35+
review_id: int,
36+
review_status_id: int,
37+
file_reviews: list[FileReview],
38+
) -> None:
39+
"""Complete a review."""
40+
review = CompleteReviewRequest(
41+
review_id=review_id,
42+
review_status_id=review_status_id,
43+
file_reviews=file_reviews,
44+
)
45+
url = f"{config.approved_api_url}/reviews/complete"
46+
logger.info("Completing review %s", review_id)
47+
await send_http_post_request(content=review.model_dump(), url=url)

src/utils/http.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,24 @@ async def send_http_post_request(content: dict, url: str) -> None:
2828
) from e
2929

3030
logger.info("Successfully sent HTTP request to %s", url)
31+
32+
33+
async def send_http_put_request(content: dict, url: str) -> None:
34+
"""Send."""
35+
timeout = httpx.Timeout(config.modules.common.request_timeout, read=None)
36+
37+
async with httpx.AsyncClient() as client:
38+
try:
39+
callback = await client.put(
40+
url,
41+
timeout=timeout,
42+
json=content,
43+
)
44+
callback.raise_for_status()
45+
except httpx.HTTPStatusError as e:
46+
msg = "Failed to send HTTP request message."
47+
raise ValueError(
48+
msg,
49+
) from e
50+
51+
logger.info("Successfully sent HTTP request to %s", url)

0 commit comments

Comments
 (0)