Coverage for middle_layer/review/domain_layer/services/finalize_prop_reviews_service.py: 100.00%

25 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2026-03-09 06:13 +0000

1# Copyright 2024 Associated Universities, Inc. 

2# 

3# This file is part of Telescope Time Allocation Tools (TTAT). 

4# 

5# TTAT is free software: you can redistribute it and/or modify 

6# it under the terms of the GNU General Public License as published by 

7# the Free Software Foundation, either version 3 of the License, or 

8# any later version. 

9# 

10# TTAT is distributed in the hope that it will be useful, 

11# but WITHOUT ANY WARRANTY; without even the implied warranty of 

12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

13# GNU General Public License for more details. 

14# 

15# You should have received a copy of the GNU General Public License 

16# along with TTAT. If not, see <https://www.gnu.org/licenses/>. 

17 

18import numpy 

19 

20from common.application_layer.orm_repositories.orm_repository import ORMRepository 

21from review.domain_layer.entities.ppr_proposal_review import PPRProposalReview 

22from review.domain_layer.entities.science_review_panel import ScienceReviewPanel 

23from review.domain_layer.services.validate_ppr_prop_rev_score_service import ( 

24 PPR_PROP_REV_SCORE_RANGE_MESSAGE, 

25 validate_ppr_prop_review_score, 

26) 

27from review.domain_layer.services.validate_pprpr_state_change_service import validate_pprpr_state_change 

28 

29 

30def calculate_normalized_linear_rank_score(scores: list[float], index: int) -> float: 

31 """ 

32 Calculate the Normalized Linear Rank Score for a given element of a list of SRP scores 

33 

34 The formula is: 10R/n 

35 where R is the ordinal rank of the SRP Score to be processed (in ascending order) 

36 and n is the number of proposals reviewed by the Science Review Panel (len(scores) here). 

37 

38 :param scores: the list of SRP scores to normalize against, including the one to be processed 

39 :param index: the index, in scores, of the SRP score to be processed 

40 :return: the Normalized Linear Rank Score for scores[index] given scores, rounded to the nearest tenth. 

41 :raises IndexError: When index isn't in [0, len(scores)) 

42 """ 

43 R = sorted(scores).index(scores[index]) + 1 

44 n = len(scores) 

45 nlr_score = 10 * R / n 

46 return numpy.round(nlr_score, 1) 

47 

48 

49def finalize_ppr_proposal_reviews( 

50 srp: ScienceReviewPanel, repo: ORMRepository, is_tta_member: bool = False 

51) -> list[PPRProposalReview]: 

52 """Validate and finalize all PPRProposalReviews for a given ScienceReviewPanel 

53 

54 :param srp: ScienceReviewPanel to finalize PPRProposalReviews for 

55 :param repo: Repository to query the database 

56 :param is_tta_member: Whether or not the user requesting this state change is a TTA Member 

57 :return: List of srp's PPRProposalReviews, finalized 

58 :raises ValueError: When srp's PPRProposalReviews can't be finalized 

59 """ 

60 proposal_reviews = repo.ppr_proposal_review_repo.list_by_srp_id(srp.science_review_panel_id) 

61 srp_scores = [prop_rev.calculated_srp_score for prop_rev in proposal_reviews] 

62 for i, proposal_review in enumerate(proposal_reviews): 

63 if not proposal_review.external_science_review_comments: 

64 raise ValueError(f"PPRProposalReview for Proposal {proposal_review.proposal_id} has no comment for the PI") 

65 if not validate_ppr_prop_review_score(proposal_review.calculated_srp_score): 

66 raise ValueError( 

67 f"{PPR_PROP_REV_SCORE_RANGE_MESSAGE}, found {proposal_review.calculated_srp_score}" 

68 f" for PPRProposalReview of Proposal {proposal_review.proposal_id}" 

69 ) 

70 validation_message = validate_pprpr_state_change( 

71 proposal_review.review_state, "Finalized", is_tta_member=is_tta_member 

72 ) 

73 if validation_message: 

74 raise ValueError(f"{validation_message} for PPRProposalReview for Proposal {proposal_review.proposal_id}") 

75 proposal_review.review_state = "Finalized" 

76 proposal_review.scientific_merit_metric = calculate_normalized_linear_rank_score(srp_scores, i) 

77 return proposal_reviews