예제 #1
0
def delete_asset(asset_id):
    asset = Asset.find_by_id(asset_id) if asset_id else None
    if not asset:
        raise ResourceNotFoundError('Asset not found.')
    if not can_update_asset(asset=asset, user=current_user):
        raise BadRequestError(
            'To delete this asset you must own it or be a teacher in the course.'
        )
    Asset.delete(asset_id=asset_id)
    return tolerant_jsonify({'message': f'Asset {asset_id} deleted'}), 200
예제 #2
0
def previews_callback():
    if not verify_preview_service_authorization(
            request.headers.get('authorization')):
        raise UnauthorizedRequestError(
            'Missing or invalid authorization header.')

    params = request.form
    if not (params.get('id', None) and params.get('status', None)):
        raise BadRequestError('Id and status fields required.')
    metadata = None
    try:
        if params.get('metadata'):
            metadata = json.loads(params['metadata'])
    except Exception as e:
        app.logger.error('Failed to parse JSON preview metadata.')
        app.logger.exception(e)
        raise BadRequestError('Could not parse JSON metadata.')

    asset = Asset.find_by_id(params['id'])
    if not asset:
        raise BadRequestError(f"Asset {params['id']} not found.")

    if asset.update_preview(
            preview_status=params.get('status'),
            thumbnail_url=params.get('thumbnail'),
            image_url=params.get('image'),
            pdf_url=params.get('pdf'),
            metadata=metadata,
    ):
        return tolerant_jsonify({'status': 'success'})
    else:
        raise InternalServerError(
            f"Unable to update preview data (asset_id={params['id']}.")
예제 #3
0
 def create_link_submission_asset(self, course, user, category, assignment, submission, link_submission_tracker):
     try:
         existing_submission_asset = link_submission_tracker.get(submission.url, None)
         if existing_submission_asset:
             app.logger.debug(f'Adding new user to existing link asset: user {user.canvas_user_id}, asset {existing_submission_asset.id}.')
             existing_submission_asset.users.append(user)
             db.session.add(existing_submission_asset)
             std_commit()
         else:
             app.logger.info(
                 f'Will create link asset for submission: '
                 f'user {user.canvas_user_id}, submission {submission.id}, assignment {assignment.id}, {_format_course(course)}')
             link_submission_tracker[submission.url] = Asset.create(
                 asset_type='link',
                 canvas_assignment_id=assignment.id,
                 categories=[category],
                 course_id=course.id,
                 source=submission.url,
                 title=submission.url,
                 url=submission.url,
                 users=[user],
                 create_activity=False,
             )
     except Exception as e:
         app.logger.error(
             f'Failed to create link asset for an assignment submission: '
             f'user {user.canvas_user_id}, submission {submission.id}, assignment {assignment.id}, {_format_course(course)}')
         app.logger.exception(e)
예제 #4
0
def _create_assets(courses, users):
    course_id = courses[0].id

    def _create_category(visible):
        return Category.create(
            canvas_assignment_id=98765,
            canvas_assignment_name=
            f'Linger on your pale blue eyes (visible={visible})',
            course_id=course_id,
            title=f'Thought of you as my mountain top (visible={visible})',
            visible=visible,
        )

    category_visible = _create_category(True)
    category_hidden = _create_category(False)
    std_commit(allow_test_environment=True)

    assets = []
    for a in _test_assets:
        asset = Asset.create(
            asset_type=a['asset_type'],
            categories=[category_hidden, category_visible],
            course_id=course_id,
            description=None,
            title=a['title'],
            url=a['url'],
            users=[users[0]],
        )
        db.session.add(asset)
        assets.append(asset)
    std_commit(allow_test_environment=True)
    return assets
예제 #5
0
def _get_asset_for_like(asset_id):
    asset = Asset.find_by_id(asset_id=asset_id)
    if not asset or not can_view_asset(asset=asset, user=current_user):
        raise ResourceNotFoundError(f'No asset found with id: {asset_id}')
    elif current_user.user in asset.users:
        raise BadRequestError('You cannot like your own asset.')
    else:
        return asset
예제 #6
0
def get_comments(asset_id):
    asset = Asset.find_by_id(asset_id=asset_id)
    if asset and can_view_asset(asset=asset, user=current_user):
        return tolerant_jsonify(
            _decorate_comments(Comment.get_comments(asset.id)))
    else:
        raise ResourceNotFoundError(
            'Asset is either unavailable or non-existent.')
예제 #7
0
def mock_asset(app, db_session):
    course = Course.create(
        canvas_api_domain='bcourses.berkeley.edu',
        canvas_course_id=randrange(1000000),
    )
    category_hidden = Category.create(
        canvas_assignment_name='Just look into her false colored eyes',
        course_id=course.id,
        title='What a clown (visible=False)',
        canvas_assignment_id=98765,
        visible=False,
    )
    category_visible = Category.create(
        canvas_assignment_name='Just look into her false colored eyes',
        course_id=course.id,
        title='What a clown (visible=True)',
        canvas_assignment_id=98765,
        visible=True,
    )
    course = Course.query.order_by(Course.name).all()[0]
    canvas_user_id = str(randint(1000000, 9999999))
    user = User.create(
        canvas_course_role='Student',
        canvas_course_sections=[],
        canvas_email=f'{canvas_user_id}@berkeley.edu',
        canvas_enrollment_state='active',
        canvas_full_name=f'Student {canvas_user_id}',
        canvas_user_id=canvas_user_id,
        course_id=course.id,
    )
    unique_token = datetime.now().isoformat()
    asset = Asset.create(
        asset_type='link',
        categories=[category_hidden, category_visible],
        course_id=course.id,
        description=None,
        download_url=
        f"s3://{app.config['AWS_S3_BUCKET_FOR_ASSETS']}/asset/{course.id}_{canvas_user_id}_{unique_token}.pdf",
        title=f'Mock Asset created at {unique_token}',
        url=f'https://en.wikipedia.org/wiki/{unique_token}',
        users=[user],
    )
    for test_comment in _get_mock_comments():
        comment = Comment.create(asset=asset,
                                 body=test_comment['body'],
                                 user_id=user.id)
        for reply in test_comment.get('replies', []):
            reply = Comment.create(
                asset=asset,
                body=reply['body'],
                parent_id=comment.id,
                user_id=user.id,
            )
    std_commit(allow_test_environment=True)
    yield asset
    db_session.delete(asset)
    std_commit(allow_test_environment=True)
예제 #8
0
def get_asset(asset_id):
    asset = Asset.find_by_id(asset_id=asset_id)
    if asset and can_view_asset(asset=asset, user=current_user):
        if current_user.user not in asset.users:
            asset.increment_views(current_user.user)
        return tolerant_jsonify(
            asset.to_api_json(user_id=current_user.get_id()))
    else:
        raise ResourceNotFoundError(f'No asset found with id: {asset_id}')
예제 #9
0
def update_asset():
    params = request.get_json()
    asset_id = params.get('assetId')
    category_id = params.get('categoryId')
    description = params.get('description')
    title = params.get('title')
    asset = Asset.find_by_id(asset_id) if asset_id else None
    if not asset or not title:
        raise BadRequestError('Asset update requires a valid ID and title.')
    if not can_update_asset(asset=asset, user=current_user):
        raise BadRequestError(
            'To update an asset you must own it or be a teacher in the course.'
        )
    asset = Asset.update(
        asset_id=asset_id,
        categories=category_id and [Category.find_by_id(category_id)],
        description=description,
        title=title,
    )
    return tolerant_jsonify(asset.to_api_json(user_id=current_user.get_id()))
예제 #10
0
def create_asset():
    params = request.get_json() or request.form
    asset_type = params.get('type')
    category_id = params.get('categoryId')
    description = params.get('description')
    source = params.get('source')
    url = params.get('url')
    title = params.get('title', url)
    visible = params.get('visible', True)
    if not asset_type or not title:
        raise BadRequestError('Asset creation requires title and type.')

    if asset_type == 'link' and not url:
        raise BadRequestError('Link asset creation requires url.')

    if not current_user.course:
        raise BadRequestError('Course data not found')

    s3_attrs = {}
    if asset_type == 'file':
        file_upload = _get_upload_from_http_post()
        s3_attrs = Asset.upload_to_s3(
            filename=file_upload['name'],
            byte_stream=file_upload['byte_stream'],
            course_id=current_user.course.id,
        )

    asset = Asset.create(
        asset_type=asset_type,
        categories=category_id and [Category.find_by_id(category_id)],
        course_id=current_user.course.id,
        description=description,
        download_url=s3_attrs.get('download_url', None),
        mime=s3_attrs.get('content_type', None),
        source=source,
        title=title,
        url=url,
        users=[User.find_by_id(current_user.get_id())],
        visible=visible,
    )
    return tolerant_jsonify(asset.to_api_json())
예제 #11
0
 def test_asset_creation_invisible_no_activities(self,
                                                 authorized_user_session):
     asset = Asset.create(
         asset_type='link',
         categories=None,
         course_id=authorized_user_session.course.id,
         title='Riding in a Stutz Bear Cat, Jim',
         url='https://genius.com/The-velvet-underground-sweet-jane-lyrics',
         users=[authorized_user_session.user],
         visible=False,
     )
     activities = Activity.query.filter_by(asset_id=asset.id).all()
     assert len(activities) == 0
예제 #12
0
 def create_file_submission_assets(self, course, user, category, assignment, submission, file_submission_tracker):
     app.logger.info(
         f'Will create file assets for submission attachments: '
         f'user {user.canvas_user_id}, submission {submission.id}, assignment {assignment.id}, {_format_course(course)}')
     for attachment in getattr(submission, 'attachments', []):
         try:
             if attachment['size'] > 10485760:
                 app.logger.debug('Attachment too large, will not process.')
                 continue
             existing_submission_asset = file_submission_tracker.get(attachment['id'], None)
             if existing_submission_asset:
                 app.logger.debug(f'Adding new user to existing file asset: user {user.canvas_user_id}, asset {existing_submission_asset.id}.')
                 existing_submission_asset.users.append(user)
                 db.session.add(existing_submission_asset)
                 std_commit()
             else:
                 s3_attrs = Asset.upload_to_s3(
                     filename=attachment['filename'],
                     byte_stream=urlopen(attachment['url']).read(),
                     course_id=course.id,
                 )
                 file_submission_tracker[attachment['id']] = Asset.create(
                     asset_type='file',
                     canvas_assignment_id=assignment.id,
                     categories=[category],
                     course_id=course.id,
                     download_url=s3_attrs.get('download_url', None),
                     mime=s3_attrs.get('content_type', None),
                     title=attachment['filename'],
                     users=[user],
                     create_activity=False,
                 )
         except Exception as e:
             app.logger.error(
                 f'Failed to create file asset for an attachment: '
                 f'user {user.canvas_user_id}, submission {submission.id}, assignment {assignment.id}, {_format_course(course)}')
             app.logger.exception(e)
예제 #13
0
 def test_asset_creation_activity(self, authorized_user_session):
     asset = Asset.create(
         asset_type='link',
         categories=None,
         course_id=authorized_user_session.course.id,
         title='Riding in a Stutz Bear Cat, Jim',
         url='https://genius.com/The-velvet-underground-sweet-jane-lyrics',
         users=[authorized_user_session.user],
     )
     activities = Activity.query.filter_by(asset_id=asset.id).all()
     assert len(activities) == 1
     assert activities[0].activity_type == 'asset_add'
     assert activities[0].course_id == authorized_user_session.course.id
     assert activities[0].object_type == 'asset'
     assert activities[0].object_id == asset.id
     assert activities[0].asset_id == asset.id
     assert activities[0].user_id == authorized_user_session.user.id
예제 #14
0
    def test_assets_for_course(self, authorized_user_session):
        asset_feed = Asset.get_assets(authorized_user_session,
                                      sort='recent',
                                      offset=0,
                                      limit=20,
                                      filters={})
        # Feed shape
        assert asset_feed['offset'] == 0
        assert asset_feed['total'] == 2
        assert len(asset_feed['results']) == 2
        # Ordering
        assert asset_feed['results'][0]['id'] > asset_feed['results'][1]['id']
        # Asset structure
        for asset in asset_feed['results']:
            assert asset['body'] is None
            assert asset['canvasAssignmentId'] is None
            assert asset['commentCount'] == 0
            assert asset['courseId'] == authorized_user_session.course.id
            assert asset['createdAt'] is not None
            assert asset['deletedAt'] is None
            assert asset['description'] is None
            assert asset['downloadUrl'] is None
            assert asset['imageUrl'] is None
            assert asset['liked'] is False
            assert asset['likes'] == 0
            assert asset['mime'] is None
            assert asset['pdfUrl'] is None
            assert asset['previewMetadata'] == '{}'
            assert asset['previewStatus'] == 'pending'
            assert asset['source'] is None
            assert asset['thumbnailUrl'] is None
            assert asset['title'] is not None
            assert asset['type'] == 'link'
            assert asset['createdAt'] is not None
            assert asset['updatedAt'] is not None
            assert asset['views'] == 0
            assert asset['visible'] is True

            assert len(asset['users']) == 1
            assert asset['users'][0]['id'] == authorized_user_session.user.id
            assert asset['users'][0]['canvasFullName'] == 'Oliver Heyer'
            assert asset['users'][0]['canvasUserId'] == 9876543
            assert asset['users'][0]['canvasCourseRole'] == 'Teacher'
            assert asset['users'][0]['canvasEnrollmentState'] == 'active'
            assert 'canvasCourseSections' in asset['users'][0]
            assert 'canvasImage' in asset['users'][0]
예제 #15
0
def download(asset_id):
    asset = Asset.find_by_id(asset_id)
    s3_url = asset.download_url
    if asset and s3_url and can_view_asset(asset=asset, user=current_user):
        stream = stream_object(s3_url)
        if stream:
            now = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
            name = re.sub(r'[^a-zA-Z0-9]', '_', asset.title)
            extension = s3_url.rsplit('.', 1)[-1]
            return Response(
                stream,
                headers={
                    'Content-disposition':
                    f'attachment; filename="{name}_{now}.{extension}"',
                },
            )
    raise ResourceNotFoundError(f'Asset {asset_id} not found.')
예제 #16
0
def create_comment():
    params = request.get_json()
    asset_id = params.get('assetId')
    asset = Asset.find_by_id(asset_id=asset_id)
    if asset and can_view_asset(asset=asset, user=current_user):
        body = params.get('body', '').strip()
        if not body:
            raise BadRequestError('Comment body is required.')
        parent_id = params.get('parentId')
        comment = Comment.create(
            asset=asset,
            user_id=current_user.user_id,
            body=body,
            parent_id=parent_id and int(parent_id),
        )
        return tolerant_jsonify(_decorate_comments([comment.to_api_json()])[0])
    else:
        raise ResourceNotFoundError(
            'Asset is either unavailable or non-existent.')
예제 #17
0
def get_assets():
    params = request.get_json()
    sort = _get(params, 'sort', None)
    offset = params.get('offset')
    limit = params.get('limit')
    filters = {
        'asset_type': _get(params, 'assetType', None),
        'category_id': _get(params, 'categoryId', None),
        'has_comments': _get(params, 'hasComments', None),
        'has_likes': _get(params, 'hasLikes', None),
        'has_views': _get(params, 'hasViews', None),
        'keywords': _get(params, 'keywords', None),
        'order_by': _get(params, 'orderBy', 'recent'),
        'owner_id': _get(params, 'userId', None),
        'section_id': _get(params, 'sectionId', None),
    }
    results = Asset.get_assets(session=current_user,
                               sort=sort,
                               offset=offset,
                               limit=limit,
                               filters=filters)
    return tolerant_jsonify(results)