Beispiel #1
0
    def test_stream_zipped_bundle(self, app, fake_auth):
        with mock_legacy_note_attachment(app):
            stream = get_zip_stream_for_sid('9000000000')['stream']
            body = b''
            for chunk in stream:
                body += chunk
            zipfile = ZipFile(io.BytesIO(body), 'r')
            contents = {}
            for name in zipfile.namelist():
                contents[name] = zipfile.read(name)

            assert len(contents) == 2
            assert contents[
                'dog_eaten_homework.pdf'] == b'When in the course of human events, it becomes necessarf arf woof woof woof'
            today = localize_datetime(utc_now()).strftime('%Y%m%d')
            csv_rows = contents[
                f"advising_notes_wolfgang_pauli-o'rourke_{today}.csv"].decode(
                    'utf-8').strip().split('\r\n')
            assert len(csv_rows) == 4
            assert csv_rows[
                0] == 'date_created,student_sid,student_name,author_uid,author_csid,author_name,subject,topics,attachments,\
body,late_change_request_action,late_change_request_status,late_change_request_term,late_change_request_course'
            assert csv_rows[1] ==\
                "2017-11-02,9000000000,Wolfgang Pauli-O'Rourke,,700600500,,,,dog_eaten_homework.pdf,I am confounded by this confounding student,,,,"
            assert csv_rows[
                2] == "2017-11-02,9000000000,Wolfgang Pauli-O'Rourke,,600500400,,,Ne Scéaw,,Is this student even on campus?,,,,"
            assert csv_rows[
                3] == "2020-12-05,9000000000,Wolfgang Pauli-O'Rourke,,,,,,,,Late Grading Basis Change,In Error,Fall 2020,24460 PSYCH 110 - \
Beispiel #2
0
def download_notes_and_attachments(sid):
    students = data_loch.get_basic_student_data([sid])
    student = students[0] if students else None
    notes = get_advising_notes(sid) if student else None
    if not student or not notes:
        return Response('Not found', status=404)

    filename = '_'.join([
        'advising_notes',
        student.get('first_name', '').lower(),
        student.get('last_name', '').lower(),
        localize_datetime(utc_now()).strftime('%Y%m%d'),
    ])

    def generator():
        for chunk in get_zip_stream(filename=filename,
                                    notes=notes,
                                    student=student):
            yield chunk

    response = Response(stream_with_context(generator()),
                        mimetype='application/zip')
    encoding_safe_filename = urllib.parse.quote(
        f'{filename}.zip'.encode('utf8'))
    response.headers[
        'Content-Disposition'] = f'attachment; filename={encoding_safe_filename}'
    return response
Beispiel #3
0
 def _assert_zip_download(self, app, client):
     today = localize_datetime(utc_now()).strftime('%Y%m%d')
     with mock_legacy_note_attachment(app):
         response = client.get('/api/notes/download_for_sid/9000000000')
         assert response.status_code == 200
         assert response.headers['Content-Type'] == 'application/zip'
         assert response.headers['Content-Disposition'] == f"attachment; filename=advising_notes_wolfgang_pauli-o'rourke_{today}.zip"
         assert response.data
Beispiel #4
0
 def get_drop_in_waitlist(cls, dept_code, statuses=()):
     local_today = localize_datetime(datetime.now()).strftime('%Y-%m-%d')
     start_of_today = localized_timestamp_to_utc(f'{local_today}T00:00:00')
     criterion = and_(
         cls.created_at >= start_of_today,
         cls.appointment_type == 'Drop-in',
         cls.status.in_(statuses),
         cls.deleted_at == None,  # noqa: E711
         cls.dept_code == dept_code,
     )
     return cls.query.filter(criterion).order_by(cls.created_at).all()
def get_today_scheduled_appointments(dept_code):
    def _is_current_user_authorized():
        return current_user.is_admin or dept_code in _dept_codes_with_scheduler_privilege()

    dept_code = dept_code.upper()
    if dept_code not in BERKELEY_DEPT_CODE_TO_NAME:
        raise ResourceNotFoundError(f'Unrecognized department code: {dept_code}')
    elif _is_current_user_authorized():
        local_today = localize_datetime(utc_now())
        advisor_uid = request.args.get('advisorUid')
        scheduled_for_today = Appointment.get_scheduled(dept_code, local_today, advisor_uid)
        appointments = [a.to_api_json(current_user.get_id()) for a in scheduled_for_today]
        openings = AppointmentAvailability.get_openings(dept_code, local_today, appointments)
        _put_student_profile_per_appointment(appointments)
        return tolerant_jsonify({
            'appointments': appointments,
            'openings': openings,
        })
    else:
        raise ForbiddenRequestError(f'You are unauthorized to manage {dept_code} appointments.')
Beispiel #6
0
def get_zip_stream_for_sid(sid):
    z = zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED)
    notes = get_advising_notes(sid)
    if not notes:
        return None

    filename = 'advising_notes'
    student_data = data_loch.get_basic_student_data([sid])
    if student_data:
        student_row = student_data[0]
        student_name = join_if_present(
            ' ', [student_row.get('first_name'),
                  student_row.get('last_name')])
        filename = '_'.join([
            filename,
            student_row.get('first_name', '').lower(),
            student_row.get('last_name', '').lower()
        ])
    else:
        student_name = ''
    filename = '_'.join(
        [filename, localize_datetime(utc_now()).strftime('%Y%m%d')])

    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']
            ])),
    )

    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',
            'late_change_request_action',
            'late_change_request_status',
            'late_change_request_term',
            'late_change_request_course',
        ])
        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 {}
            yield csv_line([
                date_local,
                sid,
                student_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 []]),
                '; '.join(
                    [a['displayName'] for a in note['attachments'] or []]),
                note['body'],
                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.write_iter(f'{filename}.csv', iter_csv())

    all_attachment_filenames = set()
    all_attachment_filenames.add(f'{filename}.csv')
    for note in 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'])
            stream_data = get_legacy_attachment_stream(
                id_) if is_legacy_attachment else get_boa_attachment_stream(
                    id_)
            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 {
        'filename': f'{filename}.zip',
        'stream': z,
    }