Coverage for rfpy/api/endpoints/attachments.py: 100%
72 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-31 16:00 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-31 16:00 +0000
1'''
2Manage file attachments and uploads
3'''
4from typing import List
6from rfpy.suxint import http
7from rfpy.auth import perms
8from rfpy.api import fetch, validate, attachments
9from rfpy.web.response import XAccelAttachmentResponse
10from rfpy.web.mime import MimeTypes
11from rfpy.web import serial
12from rfpy.model import QuestionInstance
13from rfpy.model.audit import AuditEvent, evt_types
16safo = serial.Attachment.from_orm
19@http
20def get_project_attachments(session, user, project_id) -> List[serial.Attachment]:
21 '''
22 List details for all Project Attachments (attachments uploaded by the Project author - buyer)
23 '''
24 project = fetch.project(session, project_id)
25 validate.check(user, perms.PROJECT_ACCESS, project=project, deny_restricted=False)
26 return [safo(att) for att in project.list_attachments(user)]
29@http
30def get_project_issueattachments(session, user, project_id) -> List[serial.IssueAttachment]:
31 '''
32 List all Issue attachments (uploaded by Respondents) for the current Project
33 '''
34 project = fetch.project(session, project_id)
35 validate.check(user, perms.ISSUE_VIEW_ANSWERS,
36 project=project, section_id=project.section_id, deny_restricted=False)
37 return [serial.IssueAttachment.from_orm(att)
38 for att in project.list_issue_attachments(user)]
41@http
42def get_project_answerattachments(session, user, project_id) -> List[serial.AnswerAttachment]:
43 '''
44 List Answer Attachments for the current project - attachments uploaded as answers to
45 questions with File Upload question elements
46 '''
47 project = fetch.project(session, project_id)
48 validate.check(
49 user,
50 perms.ISSUE_VIEW_ANSWERS,
51 project=project,
52 deny_restricted=False,
53 section_id=project.section_id
54 )
55 return [serial.AnswerAttachment.from_orm(att)
56 for att in fetch.answer_attachments_q(project, user)]
59@http
60def get_project_attachment(session, user, project_id, attachment_id) -> MimeTypes.ANY:
61 '''
62 Download the Project Attachment with the given ID
63 '''
64 project = fetch.project(session, project_id)
65 validate.check(user, perms.PROJECT_ACCESS, project=project, deny_restricted=False)
66 attachment = project.get_attachment(user, attachment_id)
68 return XAccelAttachmentResponse(attachment)
71@http
72def post_project_attachment(session, user, project_id,
73 attachment_upload, attachment_description) -> serial.Id:
74 '''
75 Upload a file as an attachment to the project.
77 This action can be performed for Projects at status Draft or Live
78 '''
80 project = fetch.project(session, project_id)
81 validate.check(user, perms.PROJECT_EDIT_COSMETIC, project=project)
82 attachment = attachments.save_project_attachment(session, project_id, user, attachment_upload,
83 attachment_description)
84 session.flush()
85 changes = [
86 ("filename", attachment.filename, "")
87 ]
88 evt = AuditEvent.create(
89 evt_types.PROJECT_ATTACHMENT_ADDED,
90 project=project, user=user, object_id=attachment.id, change_list=changes
91 )
92 session.add(evt)
93 return {'id': attachment.id}
96@http
97def delete_project_attachment(session, user, project_id, attachment_id):
98 '''
99 Delete the Project Attachment with the given ID
101 This action can be performed for Projects at status Draft or Live
102 '''
103 project = fetch.project(session, project_id)
104 validate.check(user, perms.PROJECT_EDIT_COSMETIC, project=project)
105 attachment = project.get_attachment(user, attachment_id)
106 filename = attachment.filename
107 attachments.delete_project_attachment(session, attachment)
108 changes = [
109 ("filename", "", filename)
110 ]
111 evt = AuditEvent.create(
112 evt_types.PROJECT_ATTACHMENT_REMOVED,
113 project=project, user=user, object_id=attachment_id, change_list=changes
114 )
115 session.add(evt)
118@http
119def get_project_issue_attachment(session,
120 user,
121 project_id,
122 issue_id,
123 attachment_id) -> MimeTypes.ANY:
124 '''
125 Download the Issue Attachment with the given ID
126 '''
127 project = fetch.project(session, project_id)
128 issue = fetch.issue(session, issue_id)
129 validate.check(user, perms.ISSUE_VIEW_ANSWERS, issue=issue,
130 project=project, section_id=project.section_id)
131 attachment = issue.get_attachment(attachment_id)
133 return XAccelAttachmentResponse(attachment)
136@http
137def get_question_element_attachment(session, user, question_id, element_id) -> MimeTypes.ANY:
138 '''
139 Download the Question Attachment associated with the given Element ID
140 '''
141 q_instance = session.query(QuestionInstance).filter_by(id=question_id).one()
142 q_element = q_instance.question_def.get_element(element_id)
143 validate.check(user, perms.PROJECT_VIEW_QUESTIONNAIRE,
144 project=q_instance.project,
145 section_id=q_instance.section_id)
146 return XAccelAttachmentResponse(q_element.attachment)
149@http
150def get_answer_attachment(session, user, answer_id) -> MimeTypes.ANY:
151 '''
152 Download the Answer Attachment with the given ID
153 '''
154 answer = fetch.answer(session, answer_id)
155 issue = answer.issue
156 validate.check(user, perms.ISSUE_VIEW_ANSWERS,
157 issue=issue,
158 project=issue.project,
159 section_id=answer.question_instance.section_id)
161 if not answer.attachment:
162 raise ValueError(f'No attachment found for answer {answer_id}')
164 return XAccelAttachmentResponse(answer.attachment)