def add_like(self, user): like_activity = Activity.create_unless_exists( activity_type='asset_like', course_id=self.course_id, user_id=user.get_id(), object_type='asset', object_id=self.id, asset_id=self.id, ) if like_activity: for asset_owner in self.users: Activity.create_unless_exists( activity_type='get_asset_like', course_id=self.course_id, user_id=asset_owner.id, object_type='asset', object_id=self.id, asset_id=self.id, actor_id=user.get_id(), reciprocal_id=like_activity.id, ) self.likes = Activity.query.filter_by( asset_id=self.id, activity_type='asset_like').count() db.session.add(self) std_commit() return True
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( cls, activity_type, course_id, user_id, object_type, object_id=None, asset_id=None, actor_id=None, reciprocal_id=None, activity_metadata=None, ): activity = cls( activity_type=activity_type, course_id=course_id, user_id=user_id, object_type=object_type, object_id=object_id, asset_id=asset_id, actor_id=actor_id, reciprocal_id=reciprocal_id, activity_metadata=activity_metadata, ) db.session.add(activity) std_commit() return activity
def increment_views(self, user): view_activity = Activity.create_unless_exists( activity_type='asset_view', course_id=self.course_id, user_id=user.id, object_type='asset', object_id=self.id, asset_id=self.id, ) if view_activity: for asset_owner in self.users: Activity.create_unless_exists( activity_type='get_asset_view', course_id=self.course_id, user_id=asset_owner.id, object_type='asset', object_id=self.id, asset_id=self.id, actor_id=user.id, reciprocal_id=view_activity.id, ) self.views = Activity.query.filter_by( asset_id=self.id, activity_type='asset_view').count() db.session.add(self) std_commit() return True
def poll_tab_configuration(self, db_course, api_course): tabs = api_course.get_tabs() course_updates = {} has_active_tools = False if db_course.asset_library_url: asset_library_tab = next((t for t in tabs if db_course.asset_library_url.endswith(t.html_url)), None) if not asset_library_tab or getattr(asset_library_tab, 'hidden', None): app.logger.info(f'No active tab found for Asset Library, will remove URL from db: {_format_course(db_course)}') course_updates['asset_library_url'] = None else: has_active_tools = True if db_course.engagement_index_url: engagement_index_tab = next((t for t in tabs if db_course.engagement_index_url.endswith(t.html_url)), None) if not engagement_index_tab or getattr(engagement_index_tab, 'hidden', None): app.logger.info(f'No active tab found for Engagement Index, will remove URL from db: {_format_course(db_course)}') course_updates['engagement_index_url'] = None else: has_active_tools = True if not has_active_tools: app.logger.info(f'No active tools found for course, will mark inactive: {_format_course(db_course)}') course_updates['active'] = False if course_updates: for key, value in course_updates.items(): setattr(db_course, key, value) db.session.add(db_course) std_commit() return course_updates.get('active', True)
def run(self, canvas_api_domain, api_key): app.logger.info(f'New poller running for {canvas_api_domain}') api_url = f'https://{canvas_api_domain}' self.canvas = get_canvas(api_url, api_key) while True: course = db.session.query(Course) \ .filter_by(canvas_api_domain=canvas_api_domain, active=True) \ .order_by(nullsfirst(Course.last_polled.asc())) \ .with_for_update() \ .first() if not course: app.logger.info(f'No active courses found, poller exiting: {canvas_api_domain}') break app.logger.info(f"Will poll {_format_course(course)}, last polled {course.last_polled or 'never'}") course.last_polled = datetime.now() db.session.add(course) std_commit() try: self.poll_course(course) except Exception as e: app.logger.error(f'Failed to poll course {_format_course(course)}') app.logger.exception(e) sleep(5)
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 handle_submission_activities(self, course, user, category, assignment, submission, activity_index): submission_metadata = { 'submission_id': submission.id, 'attempt': getattr(submission, 'attempt', None), 'file_sync_enabled': category.visible, } activity_index[user.canvas_user_id] = activity_index.get(user.canvas_user_id, {}) activity_index[user.canvas_user_id]['assignment_submit'] = activity_index[user.canvas_user_id].get('assignment_submit', {}) existing_activity = activity_index[user.canvas_user_id]['assignment_submit'].get(assignment.id, None) if existing_activity: # If we've already seen this submission attempt and there's been no change in visibility settings, # skip submission processing. if ( existing_activity.activity_metadata and existing_activity.activity_metadata.get('attempt', None) == submission_metadata['attempt'] and existing_activity.activity_metadata.get('file_sync_enabled', None) == submission_metadata['file_sync_enabled'] ): return False # Otherwise the existing activity needs updating and the submission should be processed. else: existing_activity.activity_metadata = submission_metadata db.session.add(existing_activity) std_commit() return True else: activity_index[user.canvas_user_id]['assignment_submit'][assignment.id] = Activity.create( activity_type='assignment_submit', course_id=course.id, user_id=user.id, object_type='canvas_submission', object_id=assignment.id, activity_metadata=submission_metadata, ) return True
def create(cls, canvas_api_domain, api_key): api_key = cls( canvas_api_domain=canvas_api_domain, api_key=api_key, ) db.session.add(api_key) std_commit() return api_key
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 create(cls, activity_type, course_id, enabled=True, points=None): activity_type_instance = cls( activity_type=activity_type, course_id=course_id, enabled=enabled, points=points, ) db.session.add(activity_type_instance) std_commit() return activity_type_instance
def delete(cls, comment_id): comment = cls.query.filter_by(id=comment_id).first() if comment: asset = comment.asset db.session.delete(comment) Activity.delete_by_object_id(object_type='comment', object_id=comment_id) std_commit(allow_test_environment=True) if asset: asset.refresh_comments_count()
def _create_activity_types(courses): course = courses[0] for test_activity_type in _test_activity_types: ActivityType.create( course_id=course.id, activity_type=test_activity_type['type'], enabled=test_activity_type['enabled'], points=test_activity_type['points'], ) std_commit(allow_test_environment=True)
def test_update_comment_by_owner(self, client, fake_auth, mock_asset): """Comment author can update comment.""" comment = Comment.query.first() fake_auth.login(comment.user_id) body = 'I, me, mine.' self._api_update_comment(client, body=body, comment_id=comment.id) std_commit(allow_test_environment=True) # Verify update comments = _api_get_comments(asset_id=comment.asset_id, client=client) updated_comment = next(c for c in comments if c['id'] == comment.id) assert updated_comment['body'] == body
def update( cls, category_id, title, visible, ): category = cls.find_by_id(category_id) category.title = title category.visible = visible db.session.add(category) std_commit() return category
def update( cls, asset_library_url, course_id, engagement_index_url, ): course = cls.find_by_id(course_id=course_id) course.asset_library_url = asset_library_url course.engagement_index_url = engagement_index_url db.session.add(course) std_commit() return course
def create(cls, asset, user_id, body, parent_id=None): comment = cls( asset_id=asset.id, user_id=user_id, body=body, parent_id=parent_id, ) db.session.add(comment) std_commit(allow_test_environment=True) _create_activities_per_new_comment(asset=asset, comment=comment) asset.refresh_comments_count() return comment
def _create_activities(assets, users): asset = assets[0] user = users[0] for test_activity in _test_activities: Activity.create( activity_type=test_activity['type'], course_id=asset.course_id, user_id=user.id, object_type=test_activity['object_type'], object_id=asset.id, asset_id=asset.id, ) std_commit(allow_test_environment=True)
def update_preview(self, **kwargs): if kwargs.get('preview_status'): self.preview_status = kwargs['preview_status'] if kwargs.get('thumbnail_url'): self.thumbnail_url = kwargs['thumbnail_url'] if kwargs.get('image_url'): self.image_url = kwargs['image_url'] if kwargs.get('pdf_url'): self.pdf_url = kwargs['pdf_url'] if kwargs.get('metadata'): self.preview_metadata = kwargs['metadata'] db.session.add(self) std_commit() return True
def update( cls, asset_id, title, categories=None, description=None, ): asset = Asset.find_by_id(asset_id) asset.title = title asset.description = description asset.categories = categories db.session.add(asset) std_commit() return asset
def create( cls, asset_type, course_id, title, users, canvas_assignment_id=None, categories=None, description=None, download_url=None, mime=None, source=None, url=None, visible=True, create_activity=True, ): asset = cls( asset_type=asset_type, canvas_assignment_id=canvas_assignment_id, categories=categories, course_id=course_id, description=description, download_url=download_url, mime=mime, source=source, title=title, url=url, users=users, visible=visible, ) db.session.add(asset) std_commit() preview_url = download_url if asset_type == 'file' else url generate_previews(asset.id, preview_url) # Invisible assets generate no activities. if visible and create_activity is not False: for user in users: Activity.create( activity_type='asset_add', course_id=course_id, user_id=user.id, object_type='asset', object_id=asset.id, asset_id=asset.id, ) return asset
def _verify_delete_comment(self, asset, client): asset_feed = _api_get_asset(asset_id=asset.id, client=client) initial_comment_count = asset_feed['commentCount'] assert initial_comment_count > 0 comment = Comment.get_comments(asset_id=asset.id)[0] comment_id = comment['id'] self._api_delete_comment(comment_id=comment_id, client=client) std_commit(allow_test_environment=True) comments = _api_get_comments(asset_id=asset.id, client=client) comment_ids = list(map(lambda c: c['id'], comments)) assert comment_id not in comment_ids asset_feed = _api_get_asset(asset_id=asset.id, client=client) assert asset_feed['commentCount'] == initial_comment_count - 1 activities = Activity.find_by_object_id(object_type='comment', object_id=comment_id) assert len(activities) == 0
def _create_users(courses): course = courses[0] users = [] for test_user in _test_users: user = User.create( canvas_course_role=test_user['canvas_course_role'], canvas_course_sections=[], canvas_email=test_user['canvas_email'], canvas_enrollment_state=test_user['canvas_enrollment_state'], canvas_full_name=test_user['canvas_full_name'], canvas_user_id=test_user['canvas_user_id'], course_id=course.id, ) users.append(user) std_commit(allow_test_environment=True) return users
def create( cls, canvas_assignment_name, course_id, title, canvas_assignment_id=None, visible=True, ): category = cls( canvas_assignment_id=canvas_assignment_id, canvas_assignment_name=canvas_assignment_name, course_id=course_id, title=title, visible=visible, ) db.session.add(category) std_commit() return category
def remove_like(self, user): db.session.query(Activity).filter_by( object_id=self.id, object_type='asset', activity_type='asset_like', user_id=user.get_id(), ).delete() db.session.query(Activity).filter_by( object_id=self.id, object_type='asset', activity_type='get_asset_like', actor_id=user.get_id(), ).delete() self.likes = Activity.query.filter_by( object_id=self.id, object_type='asset', activity_type='asset_like').count() db.session.add(self) std_commit() return True
def create( cls, canvas_api_domain, canvas_course_id, asset_library_url=None, engagement_index_url=None, name=None, ): course = cls( active=True, asset_library_url=asset_library_url, canvas_api_domain=canvas_api_domain, canvas_course_id=canvas_course_id, engagement_index_url=engagement_index_url, name=name, ) db.session.add(course) std_commit() return course
def _create_courses(): for c in _test_canvas: canvas = Canvas( canvas_api_domain=c['canvas_api_domain'], api_key=c['api_key'], lti_key=c['lti_key'], lti_secret=c['lti_secret'], name=c['name'], ) db.session.add(canvas) std_commit(allow_test_environment=True) courses = [] for c in _test_courses: course = Course( active=c['active'], canvas_api_domain=c['canvas_api_domain'], canvas_course_id=c['canvas_course_id'], ) db.session.add(course) courses.append(course) std_commit(allow_test_environment=True) return courses
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 create( cls, canvas_course_role, canvas_enrollment_state, canvas_full_name, canvas_user_id, course_id, canvas_course_sections=None, canvas_email=None, canvas_image=None, ): user = cls( canvas_course_role=canvas_course_role, canvas_course_sections=canvas_course_sections, canvas_email=canvas_email, canvas_enrollment_state=canvas_enrollment_state, canvas_full_name=canvas_full_name, canvas_image=canvas_image, canvas_user_id=canvas_user_id, course_id=course_id, ) db.session.add(user) std_commit() return user
def _create_user_at_lti_launch(): _response = self._api_auth_lti_launch( client=client, custom_canvas_api_domain=canvas.canvas_api_domain, custom_canvas_course_id=canvas_course_id, custom_canvas_user_id=canvas_user_id, custom_external_tool_url=external_tool_url, lis_person_name_full=full_name, oauth_consumer_key=canvas.lti_key, roles='Student', tool_id=TOOL_ID_ASSET_LIBRARY, ) std_commit(allow_test_environment=True) user = User.find_by_canvas_user_id(canvas_user_id) assert user assert user.canvas_full_name == full_name assert user.canvas_user_id == canvas_user_id course = user.course assert course.canvas_course_id == canvas_course_id assert course.engagement_index_url is None assert course.asset_library_url == external_tool_url return _response