Skip to content

Commit d638e53

Browse files
committed
feat: Backend API for PRs Merged Without Review
1 parent 5471e35 commit d638e53

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

backend/analytics_server/mhq/api/pull_requests.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,38 @@ def get_team_lead_time_trends(
167167
week.isoformat(): adapt_lead_time_metrics(average_lead_time_metrics)
168168
for week, average_lead_time_metrics in weekly_lead_time_metrics_avg_map.items()
169169
}
170+
171+
@app.route("/teams/<team_id>/prs/merged_without_review", methods={"GET"})
172+
@queryschema(
173+
Schema({
174+
Required("from_time"): All(str, Coerce(datetime.fromisoformat)),
175+
Required("to_time"): All(str, Coerce(datetime.fromisoformat)),
176+
Optional("pr_filter"): All(str, Coerce(json.loads)),
177+
})
178+
)
179+
def get_prs_merged_without_review(
180+
team_id: str,
181+
from_time: datetime,
182+
to_time: datetime,
183+
pr_filter: Dict = None,
184+
) -> List[PullRequest]:
185+
query_validator = get_query_validator()
186+
187+
pr_analytics = get_pr_analytics_service()
188+
189+
interval: Interval = query_validator.interval_validator(from_time, to_time)
190+
191+
pr_filter: PRFilter = apply_pr_filter(
192+
pr_filter, EntityType.TEAM, team_id, [SettingType.EXCLUDED_PRS_SETTING]
193+
)
194+
195+
team_repos = pr_analytics.get_team_repos(team_id)
196+
if not team_repos:
197+
return []
198+
199+
repo_ids = [repo.id for repo in team_repos]
200+
201+
prs = pr_analytics.get_prs_merged_without_review(repo_ids, interval, pr_filter)
202+
203+
repo_id_repo_map = {repo.id: repo for repo in team_repos}
204+
return get_non_paginated_pr_response(prs, repo_id_repo_map, len(prs))

backend/analytics_server/mhq/service/code/pr_analytics.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from mhq.store.models.code.filter import PRFilter
2+
from mhq.store.models.core.teams import Team
3+
from mhq.utils.time import Interval
14
from mhq.store.models.code import OrgRepo, PullRequest
25
from mhq.store.repos.code import CodeRepoService
3-
46
from typing import List, Optional
57

68

@@ -17,6 +19,8 @@ def get_team_repos(self, team_id: str) -> List[OrgRepo]:
1719
def get_repo_by_id(self, repo_id: str) -> Optional[OrgRepo]:
1820
return self.code_repo_service.get_repo_by_id(repo_id)
1921

22+
def get_prs_merged_without_review(self, repo_ids: List[str], interval: Interval, pr_filter: PRFilter) -> List[PullRequest]:
23+
return self.code_repo_service.get_prs_merged_without_review(repo_ids, interval, pr_filter)
2024

2125
def get_pr_analytics_service():
2226
return PullRequestAnalyticsService(CodeRepoService())

backend/analytics_server/mhq/store/repos/code.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
from operator import and_
33
from typing import Optional, List
44

5-
from mhq.store.models.code.enums import CodeProvider
6-
from sqlalchemy import or_
5+
from mhq.store.models.code.enums import CodeProvider, PullRequestEventType
6+
from sqlalchemy import or_, not_, exists
77
from sqlalchemy.orm import defer
88
from mhq.store.models.core import Team
99

@@ -283,6 +283,35 @@ def get_active_org_repos_by_ids(self, repo_ids: List[str]) -> List[OrgRepo]:
283283
.all()
284284
)
285285

286+
@rollback_on_exc
287+
def get_prs_merged_without_review(
288+
self,
289+
repo_ids: List[str],
290+
interval: Interval,
291+
pr_filter: PRFilter = None,
292+
) -> List[PullRequest]:
293+
query = self._db.session.query(PullRequest).options(defer(PullRequest.data))
294+
295+
query = self._filter_prs_by_repo_ids(query, repo_ids)
296+
query = self._filter_prs_merged_in_interval(query, interval)
297+
298+
query = self._filter_prs(query, pr_filter)
299+
300+
query = query.filter(
301+
not_(
302+
exists().where(
303+
and_(
304+
PullRequestEvent.pull_request_id == PullRequest.id,
305+
PullRequestEvent.type == PullRequestEventType.REVIEW,
306+
)
307+
)
308+
)
309+
)
310+
311+
query = query.order_by(PullRequest.state_changed_at.asc())
312+
313+
return query.all()
314+
286315
@rollback_on_exc
287316
def get_prs_merged_in_interval(
288317
self,

0 commit comments

Comments
 (0)