def test_stream_note_attachment(self, app, fake_auth): with mock_legacy_note_attachment(app): fake_auth.login(coe_advisor) stream = get_legacy_attachment_stream('9000000000_00002_1.pdf')['stream'] body = b'' for chunk in stream: body += chunk assert body == b'When in the course of human events, it becomes necessarf arf woof woof woof'
def test_stream_appointment_attachment(self, app, fake_auth): with mock_legacy_appointment_attachment(app): fake_auth.login(coe_advisor) stream = get_legacy_attachment_stream('9100000000_00010_1.pdf')['stream'] body = b'' for chunk in stream: body += chunk assert body == b'01001000 01100101 01101100 01101100 01101111 00100000 01010111 01101111 01110010 01101100 01100100'
def download_legacy_appointment_attachment(attachment_id): stream_data = get_legacy_attachment_stream(attachment_id) if not stream_data or not stream_data['stream']: return Response('Sorry, attachment not available.', mimetype='text/html', status=404) r = Response(stream_data['stream']) r.headers['Content-Type'] = 'application/octet-stream' encoding_safe_filename = urllib.parse.quote(stream_data['filename'].encode('utf8')) r.headers['Content-Disposition'] = f"attachment; filename*=UTF-8''{encoding_safe_filename}" return r
def download_attachment(attachment_id): is_legacy = not is_int(attachment_id) id_ = attachment_id if is_legacy else int(attachment_id) if is_legacy: stream_data = get_legacy_attachment_stream(id_) else: attachment = NoteAttachment.find_by_id(id_) note = attachment and attachment.note if note and note.is_private and not current_user.can_access_private_notes: raise ForbiddenRequestError('Unauthorized') stream_data = get_boa_attachment_stream(attachment) if not stream_data or not stream_data['stream']: return Response('Sorry, attachment not available.', mimetype='text/html', status=404) r = Response(stream_data['stream']) r.headers['Content-Type'] = 'application/octet-stream' encoding_safe_filename = urllib.parse.quote( stream_data['filename'].encode('utf8')) r.headers[ 'Content-Disposition'] = f"attachment; filename*=UTF-8''{encoding_safe_filename}" return r
def test_stream_attachment_handles_file_not_in_s3(self, app, fake_auth, caplog): with mock_legacy_note_attachment(app): fake_auth.login(coe_advisor) assert get_legacy_attachment_stream('11667051_00001_1.pdf')['stream'] is None assert "the s3 key 'sis-attachment-path/11667051/11667051_00001_1.pdf' does not exist, or is forbidden" in caplog.text
def test_stream_attachment_handles_file_not_in_database(self, app, fake_auth, caplog): with mock_legacy_note_attachment(app): fake_auth.login(coe_advisor) assert get_legacy_attachment_stream('11667051_00002_1.pdf') is None
def test_stream_attachment_handles_malformed_filename(self, app): with mock_legacy_note_attachment(app): assert get_legacy_attachment_stream('h0ax.lol') is None
def get_zip_stream(filename, notes, student): app_timezone = pytz.timezone(app.config['TIMEZONE']) def iter_csv(): def csv_line(_list): csv_output = io.StringIO() csv.writer(csv_output).writerow(_list) return csv_output.getvalue().encode('utf-8') csv_output.close() yield csv_line([ 'date_created', 'student_sid', 'student_name', 'author_uid', 'author_csid', 'author_name', 'subject', 'topics', 'attachments', 'body', 'is_private', 'late_change_request_action', 'late_change_request_status', 'late_change_request_term', 'late_change_request_course', ]) supplemental_calnet_advisor_feeds = get_calnet_users_for_csids( app, list( set([ note['author']['sid'] for note in notes if note['author']['sid'] and not note['author']['name'] ])), ) for note in notes: calnet_author = supplemental_calnet_advisor_feeds.get( note['author']['sid']) if calnet_author: calnet_author_name =\ calnet_author.get('name') or join_if_present(' ', [calnet_author.get('firstName'), calnet_author.get('lastName')]) calnet_author_uid = calnet_author.get('uid') else: calnet_author_name = None calnet_author_uid = None # strptime expects a timestamp without timezone; ancient date-only legacy notes get a bogus time appended. timestamp_created = f"{note['createdAt']}T12:00:00" if len( note['createdAt']) == 10 else note['createdAt'][:19] datetime_created = pytz.utc.localize( datetime.strptime(timestamp_created, '%Y-%m-%dT%H:%M:%S')) date_local = datetime_created.astimezone(app_timezone).strftime( '%Y-%m-%d') e_form = note.get('eForm') or {} omit_note_body = note.get( 'isPrivate') and not current_user.can_access_private_notes yield csv_line([ date_local, student['sid'], join_if_present(' ', [ student.get('first_name', ''), student.get('last_name', '') ]), (note['author']['uid'] or calnet_author_uid), note['author']['sid'], (note['author']['name'] or calnet_author_name), note['subject'], '; '.join([t for t in note['topics'] or []]), '' if omit_note_body else '; '.join( [a['displayName'] for a in note['attachments'] or []]), '' if omit_note_body else note['body'], note.get('isPrivate'), e_form.get('action'), e_form.get('status'), term_name_for_sis_id(e_form.get('term')), f"{e_form['sectionId']} {e_form['courseName']} - {e_form['courseTitle']} {e_form['section']}" if e_form.get('sectionId') else None, ]) z = zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED) csv_filename = f'{filename}.csv' z.write_iter(csv_filename, iter_csv()) if notes: all_attachment_filenames = {csv_filename} for note in notes: if not note.get( 'isPrivate') or current_user.can_access_private_notes: for attachment in note['attachments'] or []: is_legacy_attachment = not is_int(attachment['id']) id_ = attachment['id'] if is_legacy_attachment else int( attachment['id']) if is_legacy_attachment: stream_data = get_legacy_attachment_stream(id_) else: attachment = NoteAttachment.find_by_id(id_) stream_data = get_boa_attachment_stream(attachment) if stream_data: attachment_filename = stream_data['filename'] basename, extension = path.splitext( attachment_filename) suffix = 1 while attachment_filename in all_attachment_filenames: attachment_filename = f'{basename} ({suffix}){extension}' suffix += 1 all_attachment_filenames.add(attachment_filename) z.write_iter(attachment_filename, stream_data['stream']) return z