Coverage for middle_layer/review/application_layer/services/update_review_rules_service.py: 62.50%

32 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# 

18from typing import NamedTuple 

19 

20 

21class PPRUpdatePermission(NamedTuple): 

22 """ 

23 Represents the kinds of update that are permitted for a PPR update request. 

24 

25 can_update: True if the update can be performed 

26 can_update_score: True if the score can also be updated 

27 message: a message describing the reason why the update cannot be performed 

28 """ 

29 

30 can_update: bool 

31 can_update_score: bool 

32 message: str 

33 

34 

35def check_ppr_update_permitted( 

36 is_tta_member: bool, is_chair: bool, is_conflicted: bool, review_type: str, review_state: str 

37) -> PPRUpdatePermission: 

38 """ 

39 Ascertain whether or not this user is allowed to update a PPR Proposal Review, based on their access level, 

40 whether or not they are conflicted, the chair, a TTA member, what sort of review they have done and what state 

41 that review is in. 

42 

43 :param is_tta_member: True if this is on behalf of a TTA member 

44 :param is_chair: True if this is on behalf of the chair of the relevant SRP 

45 :param is_conflicted: True if this is on behalf of a conflicted reviewer 

46 :param review_type: ReviewType, probably "Primary," "Secondary," or "Tertiary" 

47 :param review_state: "Completed" or some other state from ReviewStates ("Blank," "Saved," "Closed," "Finalized") 

48 

49 :return: an UpdateResponse indicating whether the update is permitted, whether the score can also be updated, 

50 and if the update is not permitted, a reason why. 

51 """ 

52 # This method, while short, covers a lot of edge cases. There are two other ways to get a feel for the solution 

53 # set of this in the tests: 

54 # 

55 # 1. The first set of tests, which describe the expected behavior declaratively and use parameterization 

56 # 2. The second set of tests, which compare the behavior to a spreadsheet of expected behaviors. 

57 is_primary = review_type == "Primary" 

58 is_secondary = review_type == "Secondary" 

59 is_unconflicted_chair = is_chair and not is_conflicted 

60 

61 # User requesting update doesn't have permissions to do so - must be tta, prim, sec, or unconflicted chair 

62 if not is_tta_member and not (is_primary or is_secondary or is_unconflicted_chair): 

63 return PPRUpdatePermission(False, False, "not authorized to update") 

64 

65 # User requesting update is conflicted and therefore cannot update 

66 if not is_tta_member and is_conflicted: 

67 return PPRUpdatePermission(False, False, "is conflicted") 

68 

69 if not is_tta_member and review_state == "Closed": 

70 return PPRUpdatePermission(False, False, "is Closed") 

71 

72 # If PPRProposalReview is completed, primary and secondary reviewers can not update it 

73 if review_state == "Completed" and not (is_tta_member or is_unconflicted_chair): 

74 return PPRUpdatePermission(False, False, "is already completed") 

75 

76 # Only TTA members can update a finalized PPR review 

77 if review_state == "Finalized" and not is_tta_member: 

78 return PPRUpdatePermission(False, False, "is not authorized to update finalized") 

79 

80 return PPRUpdatePermission( 

81 True, is_chair or is_tta_member, "can update because TTA Member" if is_tta_member else "" 

82 ) 

83 

84 

85def generate_all_ppr_update_scenarios_csv(filename: str): 

86 import csv 

87 

88 with open(filename, "w", newline="") as outputfile: 

89 out = csv.writer(outputfile) 

90 out.writerow( 

91 [ 

92 "is_tta_member", 

93 "is_chair", 

94 "is_conflicted", 

95 "review_type", 

96 "review_state", 

97 "can update?", 

98 "can update score?", 

99 "reason", 

100 ] 

101 ) 

102 for is_tta_member in [True, False]: 

103 for is_chair in [True, False]: 

104 for is_conflicted in [True, False]: 

105 for review_type in ["Primary", "Secondary", "Tertiary", "Blank"]: 

106 for review_state in ["Blank", "Saved", "Completed", "Finalized"]: 

107 result = check_ppr_update_permitted( 

108 is_tta_member, is_chair, is_conflicted, review_type, review_state 

109 ) 

110 out.writerow( 

111 [ 

112 "TTA MEMBER" if is_tta_member else "", 

113 "CHAIR" if is_chair else "", 

114 "CONFLICTED" if is_conflicted else "", 

115 review_type, 

116 review_state, 

117 result.can_update, 

118 result.can_update_score, 

119 result.message, 

120 ] 

121 )