Coverage for rfpy/api/endpoints/notes.py: 100%

65 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-31 16:00 +0000

1''' 

2Manage Project Notes - messages between participants and respondents in a project. 

3''' 

4from typing import List 

5 

6from sqlalchemy.orm import Session 

7 

8from rfpy.model import ProjectNote, AuditEvent 

9from rfpy.model.humans import User 

10from rfpy.suxint import http 

11from rfpy.web import serial 

12from rfpy.api import fetch, validate 

13from rfpy.auth import perms 

14 

15 

16@http 

17def get_project_notes(session, user, project_id) -> List[serial.ReadNote]: 

18 ''' 

19 Get a list of ProjectNotes for the given project_id. 

20 

21 @raise AuthorisationFailue - if the user is not a standard user from the buying organisation 

22 @raise NoResultFound - if the specified project cannot be loaded 

23 ''' 

24 project = fetch.project(session, project_id) 

25 validate.check(user, perms.PROJECT_ACCESS, project=project) 

26 return [serial.ReadNote.from_orm(pn) for pn in fetch.participant_notes_query(project)] 

27 

28 

29def issue_if_target_set(project, note): 

30 if not note.target_org_id: 

31 return None 

32 

33 issue = project._issues.filter_by(respondent_id=note.target_org_id).first() 

34 if issue is None: 

35 m = f"Org '{note.target_org_id}'' is not a respondent in project {project.id}" 

36 raise ValueError(m) 

37 return issue 

38 

39 

40@http 

41def post_project_note(session: Session, 

42 user: User, 

43 project_id: int, 

44 note_doc: serial.ProjectNote) -> serial.Id: 

45 ''' 

46 Add a Note to the given Project 

47 

48 The visibility of Notes to different organisation is given by the table below - the last three 

49 columns indicate the visiblity of the message to each group. 

50 

51 |private|target_org_id defined|all respondents? |target respondent?|participants? | 

52 |-------|---------------------|-----------------|------------------|----------------| 

53 |false | true | no | yes | yes | 

54 |false | false | yes | not applicable | yes | 

55 |true | false | yes | not applicable | yes | 

56 |true | true | no | no | yes | 

57 

58 

59 Field 'target_org_id' is the ID of the Organisation to whom a Note is addressed. The 

60 Organisation must be a respondent for the current Project 

61 (i.e assigned as Respondent for an Issue). 

62 

63 Setting 'target_org_id' has no effect if the message is private (in which case only users in 

64 Particpant organisations can view the message) 

65 

66 @permissions PROJECT_ADD_NOTE 

67 ''' 

68 project = fetch.project(session, project_id) 

69 validate.check(user, perms.PROJECT_ADD_NOTE, project=project) 

70 note = ProjectNote(kind='IssuerNote', 

71 project_id=project_id, 

72 note_text=note_doc['note_text'], 

73 private=note_doc['private'], 

74 user_id=user.id, 

75 org_id=user.org_id) 

76 issue = None 

77 target_id = note_doc.get('target_org_id', None) 

78 if target_id is not None and target_id.strip() != '': 

79 note.target_org_id = note_doc['target_org_id'] 

80 issue = issue_if_target_set(project, note) 

81 

82 session.add(note) 

83 session.flush() 

84 evt = AuditEvent.create('PROJECT_NOTE_ADDED', 

85 project=project, 

86 object_id=note.id, 

87 user_id=user.id, 

88 org_id=user.org_id, 

89 issue=issue) 

90 evt.add_change('note_text', '', note.note_text) 

91 evt.add_change('private', '', note.private) 

92 evt.add_change('target_org_id', '', note.target_org_id) 

93 session.add(evt) 

94 return {'id': note.id} 

95 

96 

97@http 

98def put_project_note(session: Session, user, project_id, note_id, note_doc): 

99 ''' 

100 Update a Project Note. See Post Project Note for details of privacy and visibility 

101 

102 @permissions PROJECT_ADD_NOTE 

103 ''' 

104 project = fetch.project(session, project_id) 

105 validate.check(user, perms.PROJECT_ADD_NOTE, project=project) 

106 note = fetch.note(project, note_id) 

107 issue = issue_if_target_set(project, note) 

108 print('issue', issue) 

109 if issue is not None: 

110 evt = AuditEvent.create('PROJECT_NOTE_UPDATED', 

111 project=project, 

112 object_id=note.id, 

113 user_id=user.id, 

114 org_id=user.org_id, 

115 issue=issue) 

116 else: 

117 evt = AuditEvent.create('PROJECT_NOTE_UPDATED', 

118 project=project, 

119 object_id=note.id, 

120 user_id=user.id, 

121 org_id=user.org_id) 

122 for k in ('note_text', 'private', 'target_org_id'): 

123 if note_doc.get(k, None): 

124 if note_doc[k] != getattr(note, k): 

125 evt.add_change(k, getattr(note, k), note_doc[k]) 

126 setattr(note, k, note_doc[k]) 

127 

128 session.add(evt) 

129 

130 

131@http 

132def delete_project_note(session: Session, user, project_id, note_id): 

133 ''' 

134 Delete a Project Note 

135 

136 @permissions PROJECT_ADD_NOTE 

137 ''' 

138 project = fetch.project(session, project_id) 

139 validate.check(user, perms.PROJECT_ADD_NOTE, project=project) 

140 note = fetch.note(project, note_id) 

141 

142 issue = issue_if_target_set(project, note) 

143 evt = AuditEvent.create('PROJECT_NOTE_DELETED', 

144 project=project, 

145 object_id=note.id, 

146 user_id=user.id, 

147 org_id=user.org_id, 

148 issue=issue) 

149 session.add(evt) 

150 session.flush() 

151 session.delete(note)