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
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']}.")
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)
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
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
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.')
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)
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}')
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()))
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())
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
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)
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
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]
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.')
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.')
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)