Coverage for rfpy/vendor/api/attachments.py: 100%
71 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-24 10:52 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-24 10:52 +0000
1from typing import List
2import logging
4from rfpy.suxint import http
5from rfpy.api import fetch, update, attachments
6from rfpy.web.response import XAccelAttachmentResponse
7from ..validation import validate
8from rfpy.auth import perms
9from rfpy.web import serial
10from rfpy.model import AAttachment, IssueAttachment
12log = logging.getLogger(__name__)
15@http
16def post_issue_attachment(
17 session, effective_user, issue_id, attachment_upload, attachment_description
18) -> serial.Id:
19 """
20 Upload an Issue attachment together with metadata (description etc)
22 @param Attachment
23 """
24 issue = fetch.issue(session, issue_id)
25 validate(
26 effective_user, issue=issue, action=perms.ISSUE_MANAGE_RESPONDENT_ATTACHMENTS
27 )
29 att = attachments.save_issue_attachment(
30 session, issue_id, effective_user, attachment_upload, attachment_description
31 )
32 return serial.Id(id=att.id)
35@http
36def post_issue_answer_attachment(
37 session, effective_user, issue_id, attachment_upload, el_id
38) -> serial.AnswerAttachmentIds:
39 issue = fetch.issue(session, issue_id)
40 qelement = fetch.qelement(session, el_id)
41 question_instance = qelement.get_question_instance(issue.project_id)
43 validate(
44 effective_user,
45 issue=issue,
46 question=question_instance,
47 action=perms.ISSUE_SAVE_QUESTION_RESPONSE,
48 )
50 answers_lookup = {el_id: attachment_upload.filename}
52 # First, save and lookup an Answer - this is needed to find the disc
53 # file path for the AAttachment. Don't use upload.save_answers()
54 # because we don't want to log an event until we have the correct filename
55 # with the filesize
56 question_instance.validate_and_save_answers(answers_lookup, issue)
57 session.flush()
58 answer = question_instance._answers.filter_by(
59 issue_id=issue.id, element_id=el_id
60 ).one()
62 answer_attachment = AAttachment()
63 answer_attachment.answer = answer
64 answer_attachment.filename = attachment_upload.filename
65 answer_attachment.guess_set_mimetype(attachment_upload.filename)
66 session.add(answer_attachment)
67 session.flush() # Need to flush to get the ID values set
69 attachments.save_to_disc(answer_attachment, attachment_upload.file)
70 answer_txt = f"{answer_attachment.filename} ({answer_attachment.size})"
71 answer_lookup = {el_id: answer_txt}
73 update.save_answers(
74 session, effective_user, question_instance, answer_lookup, issue
75 )
77 return serial.AnswerAttachmentIds(
78 attachment_id=answer_attachment.id, answer_id=answer.id
79 )
82@http
83def get_issue_answer_attachment(session, effective_user, issue_id, attachment_id):
84 """Download a single Issue Attachment file"""
85 issue = fetch.issue(session, issue_id)
86 supporting_attachment_element = fetch.qelement(session, attachment_id)
87 answer = supporting_attachment_element.get_answer(issue)
89 validate(
90 effective_user, issue=issue, action=perms.ISSUE_VIEW_ANSWERS, answer=answer
91 )
93 return XAccelAttachmentResponse(answer.attachment)
96@http
97def get_issue_attachment(session, effective_user, issue_id, attachment_id):
98 """Download a single Issue Attachment file"""
99 issue = fetch.issue(session, issue_id)
100 validate(effective_user, issue=issue, action=perms.ISSUE_VIEW_ANSWERS)
101 ia = issue.attachments.filter_by(id=attachment_id).one()
102 return XAccelAttachmentResponse(ia)
105@http
106def get_issue_attachments(session, effective_user, issue_id) -> List[serial.Attachment]:
107 """List Issue Attachments for the given Issue ID"""
108 issue = fetch.issue(session, issue_id)
109 validate(effective_user, issue=issue, action=perms.ISSUE_VIEW_ANSWERS)
110 return [ia.as_dict() for ia in issue.attachments]
113@http
114def delete_issue_attachments(session, effective_user, issue_id, ids_doc):
115 """
116 Delete those Issue attachments whose IDs are given in the JSON body
117 document
118 """
120 issue = fetch.issue(session, issue_id)
121 validate(
122 effective_user, issue=issue, action=perms.ISSUE_MANAGE_RESPONDENT_ATTACHMENTS
123 )
125 att_id_list = ids_doc.ids
126 atts = (
127 session.query(IssueAttachment)
128 .filter(IssueAttachment.id.in_(att_id_list))
129 .filter(IssueAttachment.issue_id == issue_id)
130 )
132 for att in atts:
133 session.delete(att)
134 attachments.delete_from_disc(att)
137@http
138def get_issue_question_attachment(
139 session, effective_user, issue_id, question_id, attachment_id
140):
141 """
142 Download a Question Attachment.
143 @attachment_id is the ID of the QuestionAttachment *Element*,
144 not QAttachment
145 """
146 issue = fetch.issue(session, issue_id)
147 qi = fetch.question(session, question_id)
149 validate(effective_user, issue=issue, question=qi)
151 q_element = qi.question_def.get_element(attachment_id)
152 return XAccelAttachmentResponse(q_element.attachment)