diff --git a/backend/analytics_server/mhq/api/pull_requests.py b/backend/analytics_server/mhq/api/pull_requests.py index 30238746..459dd3fb 100644 --- a/backend/analytics_server/mhq/api/pull_requests.py +++ b/backend/analytics_server/mhq/api/pull_requests.py @@ -167,3 +167,29 @@ def get_team_lead_time_trends( week.isoformat(): adapt_lead_time_metrics(average_lead_time_metrics) for week, average_lead_time_metrics in weekly_lead_time_metrics_avg_map.items() } + + +@app.route("/teams//prs/merged_without_review", methods={"GET"}) +@queryschema( + Schema( + { + Required("from_time"): All(str, Coerce(datetime.fromisoformat)), + Required("to_time"): All(str, Coerce(datetime.fromisoformat)), + Optional("pr_filter"): All(str, Coerce(json.loads)), + } + ), +) +def merged_without_review( + team_id: str, from_time: datetime, to_time: datetime, pr_filter: Dict = None +): + query_validator = get_query_validator() + team: Team = query_validator.team_validator(team_id) + interval: Interval = query_validator.interval_validator(from_time, to_time) + pr_filter: PRFilter = apply_pr_filter( + pr_filter, EntityType.TEAM, team_id, [SettingType.EXCLUDED_PRS_SETTING] + ) + pr_analytics = get_pr_analytics_service() + repos = pr_analytics.get_team_repos(team_id) + prs = pr_analytics.get_prs_merged_without_review(team.id, interval, pr_filter) + repo_id_repo_map = {repo.id: repo for repo in repos} + return get_non_paginated_pr_response(prs, repo_id_repo_map, len(prs)) diff --git a/backend/analytics_server/mhq/service/code/pr_analytics.py b/backend/analytics_server/mhq/service/code/pr_analytics.py index b2a765d0..c4fd1862 100644 --- a/backend/analytics_server/mhq/service/code/pr_analytics.py +++ b/backend/analytics_server/mhq/service/code/pr_analytics.py @@ -1,7 +1,9 @@ from mhq.store.models.code import OrgRepo, PullRequest from mhq.store.repos.code import CodeRepoService + from typing import List, Optional +from mhq.utils.time import Interval class PullRequestAnalyticsService: @@ -11,6 +13,16 @@ def __init__(self, code_repo_service: CodeRepoService): def get_prs_by_ids(self, pr_ids: List[str]) -> List[PullRequest]: return self.code_repo_service.get_prs_by_ids(pr_ids) + def get_prs_merged_without_review( + self, team_id: str, interval: Interval, pr_filter: dict + ) -> List[PullRequest]: + team_repos = self.code_repo_service.get_team_repos(team_id) + repo_ids = [team_repo.id for team_repo in team_repos] + + return self.code_repo_service.get_prs_merged_without_review( + repo_ids, interval, pr_filter + ) + def get_team_repos(self, team_id: str) -> List[OrgRepo]: return self.code_repo_service.get_team_repos(team_id) diff --git a/backend/analytics_server/mhq/store/repos/code.py b/backend/analytics_server/mhq/store/repos/code.py index 115ad675..54faf344 100644 --- a/backend/analytics_server/mhq/store/repos/code.py +++ b/backend/analytics_server/mhq/store/repos/code.py @@ -367,6 +367,21 @@ def get_team_repos(self, team_id) -> List[OrgRepo]: team_repo_ids = [tr.org_repo_id for tr in team_repos] return self.get_repos_by_ids(team_repo_ids) + @rollback_on_exc + def get_prs_merged_without_review( + self, repo_ids: List[str], interval: Interval, pr_filter: PRFilter = None + ) -> List[PullRequest]: + query = ( + self._db.session.query(PullRequest) + .filter(PullRequest.repo_id.in_(repo_ids)) + .filter(PullRequest.state == PullRequestState.MERGED) + .filter(PullRequest.merge_time.is_(None)) + ) + query = self._filter_prs_merged_in_interval(query, interval) + query = self._filter_prs(query, pr_filter) + + return query.all() + @rollback_on_exc def get_team_repos_by_team_id(self, team_id: str) -> List[TeamRepos]: return (