class CanvasPollerApiKey(Base): __tablename__ = 'canvas_poller_api_keys' canvas_api_domain = db.Column(db.String(255), nullable=False, primary_key=True) api_key = db.Column(db.String(255), nullable=False, primary_key=True) def __init__( self, canvas_api_domain, api_key, ): self.api_key = api_key self.canvas_api_domain = canvas_api_domain def __repr__(self): return f"""<Course api_key={self.api_key}, canvas_api_domain={self.canvas_api_domain} """ @classmethod 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 @classmethod def find_by_domain(cls, canvas_api_domain): return cls.query.filter_by(canvas_api_domain=canvas_api_domain).all()
class AuthorizedUser(Base): __tablename__ = 'authorized_users' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 is_admin = True uid = db.Column(db.String(255), nullable=False, unique=True) def __init__(self, uid): self.uid = uid def __repr__(self): return f"""<AuthorizedUser uid={self.uid}, created_at={self.created_at}, updated_at={self.updated_at}> """ @classmethod def find_by_id(cls, db_id, include_deleted=False): return cls.query.filter_by(id=db_id).first() @classmethod def find_by_uid(cls, uid): return cls.query.filter_by(uid=uid).first() @classmethod def get_id_per_uid(cls, uid): query = text('SELECT id FROM authorized_users WHERE uid = :uid') result = db.session.execute(query, {'uid': uid}).first() return result and result['id']
class Base(db.Model): __abstract__ = True created_at = db.Column(db.DateTime, nullable=False, default=datetime.now) updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)
class ActivityType(Base): __tablename__ = 'activity_types' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 activity_type = db.Column('type', activities_type, nullable=False) course_id = db.Column(db.Integer, nullable=False) enabled = db.Column(db.Boolean, default=True, nullable=False) points = db.Column(db.Integer) def __init__( self, activity_type, course_id, enabled=True, points=None, ): self.activity_type = activity_type self.course_id = course_id self.enabled = enabled self.points = points def __repr__(self): return f"""<ActivityType type={self.activity_type}, course_id={self.course_id}, enabled={self.enabled}, points={self.points}> """ @classmethod 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 to_api_json(self): return { 'id': self.id, 'type': self.activity_type, 'courseId': self.course_id, 'enabled': self.enabled, 'points': self.points, 'createdAt': isoformat(self.created_at), 'updatedAt': isoformat(self.updated_at), }
class User(Base): __tablename__ = 'users' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 canvas_course_role = db.Column(db.String(255), nullable=False) canvas_course_sections = db.Column(ARRAY(db.String(255))) canvas_email = db.Column(db.String(255)) canvas_enrollment_state = db.Column('canvas_enrollment_state', canvas_enrollment_state_type, nullable=False) canvas_full_name = db.Column(db.String(255), nullable=False) canvas_image = db.Column(db.String(255)) canvas_user_id = db.Column(db.Integer, nullable=False) course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False) points = db.Column(db.Integer, default=0, nullable=False) share_points = db.Column(db.Boolean, default=False) last_activity = db.Column(db.DateTime, nullable=False, default=datetime.now) assets = db.relationship( 'Asset', secondary=asset_user_table, backref='users', lazy='dynamic', ) course = db.relationship(Course.__name__, back_populates='users') def __init__( self, canvas_course_role, canvas_enrollment_state, canvas_full_name, canvas_user_id, course_id, canvas_course_sections=None, canvas_email=None, canvas_image=None, points=0, share_points=False, ): self.canvas_course_role = canvas_course_role self.canvas_course_sections = canvas_course_sections self.canvas_email = canvas_email self.canvas_enrollment_state = canvas_enrollment_state self.canvas_full_name = canvas_full_name self.canvas_image = canvas_image self.canvas_user_id = canvas_user_id self.course_id = course_id self.points = points self.share_points = share_points def __repr__(self): return f"""<User canvas_course_role={self.canvas_course_role}, canvas_course_sections={self.canvas_course_sections}, canvas_email={self.canvas_email}, canvas_enrollment_state={self.canvas_enrollment_state}, canvas_full_name={self.canvas_full_name}, canvas_user_id={self.canvas_user_id}, course_id={self.course_id}, last_activity={self.last_activity}, points={self.points}, share_points={self.share_points}, created_at={self.created_at}, updated_at={self.updated_at}> """ @classmethod 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 @classmethod def find_by_course_id(cls, canvas_user_id, course_id): where_clause = and_(cls.course_id == course_id, cls.canvas_user_id == canvas_user_id) return cls.query.filter(where_clause).one_or_none() @classmethod def get_users_by_course_id(cls, course_id): return cls.query.filter_by(course_id=course_id).order_by( cls.canvas_full_name).all() @classmethod def find_by_canvas_user_id(cls, canvas_user_id): return cls.query.filter_by(canvas_user_id=canvas_user_id).first() @classmethod def find_by_id(cls, user_id): user_id = to_int(user_id) if not user_id: return None return cls.query.filter_by(id=user_id).first() @classmethod def find_by_ids(cls, user_ids): return cls.query.filter(cls.id.in_(user_ids)).all() def to_api_json(self): return { 'id': self.id, 'canvasApiDomain': self.course.canvas_api_domain, 'canvasCourseId': self.course.canvas_course_id, 'canvasCourseRole': self.canvas_course_role, 'canvasCourseSections': self.canvas_course_sections, 'canvasEmail': self.canvas_email, 'canvasFullName': self.canvas_full_name, 'canvasImage': self.canvas_image, 'canvasUserId': self.canvas_user_id, 'lastActivity': isoformat(self.last_activity), 'points': self.points, 'sharePoints': self.share_points, 'createdAt': isoformat(self.created_at), 'updatedAt': isoformat(self.updated_at), }
class Category(Base): __tablename__ = 'categories' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 canvas_assignment_id = db.Column(db.Integer) canvas_assignment_name = db.Column(db.String(255), nullable=False) course_id = db.Column(db.Integer, db.ForeignKey('courses.id')) deleted_at = db.Column(db.DateTime) title = db.Column(db.String(255), nullable=False) visible = db.Column(db.Boolean, nullable=True) def __init__( self, canvas_assignment_name, course_id, title, canvas_assignment_id=None, visible=True, ): self.canvas_assignment_id = canvas_assignment_id self.canvas_assignment_name = canvas_assignment_name self.course_id = course_id self.title = title self.visible = visible def __repr__(self): return f"""<Category id={self.id}, canvas_assignment_id={self.canvas_assignment_id}, canvas_assignment_name={self.canvas_assignment_name}, course_id={self.course_id}, deleted_at={self.deleted_at}, title={self.title}, visible={self.visible}, created_at={self.created_at}, updated_at={self.updated_at}> """ @classmethod 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 @classmethod def delete(cls, category_id): category = cls.query.filter_by(id=category_id).first() if category: db.session.delete(category) std_commit() @classmethod def find_by_id(cls, category_id): return cls.query.filter_by(id=category_id).first() @classmethod def get_categories_by_course_id(cls, course_id, include_hidden=False): query = cls.query.filter_by( course_id=course_id) if include_hidden else cls.query.filter_by( course_id=course_id, visible=True) return query.order_by(cls.title, cls.created_at).all() @classmethod 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 @classmethod def to_decorated_json(cls, categories): sql = """SELECT c.id, (SELECT COUNT(*)::int FROM asset_categories WHERE category_id = c.id) AS asset_count FROM categories AS c WHERE c.id = ANY(:category_ids) """ results = db.session.execute( sql, {'category_ids': [c.id for c in categories]}) asset_count_lookup = dict( (row['id'], row['asset_count']) for row in results) def _decorated_json(c): return { **c.to_api_json(), **{ 'assetCount': asset_count_lookup.get(c.id, None), }, } return [_decorated_json(category) for category in categories] def to_api_json(self): return { 'id': self.id, 'canvasAssignmentId': self.canvas_assignment_id, 'canvasAssignmentName': self.canvas_assignment_name, 'courseId': self.course_id, 'deletedAt': _isoformat(self.deleted_at), 'title': self.title, 'visible': self.visible, 'createdAt': _isoformat(self.created_at), 'updatedAt': _isoformat(self.updated_at), }
class Asset(Base): __tablename__ = 'assets' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 asset_type = db.Column('type', assets_type, nullable=False) body = db.Column(db.Text) canvas_assignment_id = db.Column(db.Integer) comment_count = db.Column(db.Integer, nullable=False, default=0) course_id = db.Column(db.Integer, nullable=False) deleted_at = db.Column(db.DateTime) description = db.Column(db.Text) dislikes = db.Column(db.Integer, nullable=False, default=0) download_url = db.Column(db.String(255)) image_url = db.Column(db.String(255)) likes = db.Column(db.Integer, nullable=False, default=0) mime = db.Column(db.String(255)) pdf_url = db.Column(db.String(255)) preview_metadata = db.Column(JSON) preview_status = db.Column(db.String(255), nullable=False, default='pending') source = db.Column(db.String(255)) thumbnail_url = db.Column(db.String(255)) title = db.Column(db.String(255)) url = db.Column(db.String(255)) views = db.Column(db.Integer, nullable=False, default=0) visible = db.Column(db.Boolean, nullable=False, default=True) categories = db.relationship( 'Category', secondary=asset_category_table, backref='assets', ) comments = db.relationship('Comment', back_populates='asset', cascade='all, delete-orphan') def __init__( self, asset_type, course_id, canvas_assignment_id=None, categories=None, description=None, download_url=None, mime=None, source=None, title=None, url=None, users=None, visible=True, ): self.asset_type = asset_type self.canvas_assignment_id = canvas_assignment_id self.categories = categories or [] self.course_id = course_id self.description = description self.download_url = download_url self.mime = mime self.source = source self.title = title self.url = url self.users = users or [] self.visible = visible def __repr__(self): return f"""<Asset asset_type={self.asset_type}, categories={self.categories}, users={self.users}, course_id={self.course_id}, canvas_assignment_id={self.canvas_assignment_id}, description={self.description}, source={self.source}, title={self.title}, url={self.url}, visible={self.visible}, created_at={self.created_at}, updated_at={self.updated_at}>, deleted_at={self.deleted_at}> """ @classmethod def find_by_id(cls, asset_id): return cls.query.filter_by(id=asset_id, deleted_at=None).first() @classmethod 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 @classmethod def delete(cls, asset_id): asset = cls.find_by_id(asset_id) if asset: asset.deleted_at = utc_now() std_commit() @classmethod 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 @classmethod def get_assets(cls, session, sort, offset, limit, filters): params = { 'course_id': session.course.id, 'user_id': session.user.id, 'asset_types': filters.get('asset_type'), 'category_id': filters.get('category_id'), 'owner_id': filters.get('owner_id'), 'section_id': filters.get('section_id'), 'offset': offset, 'limit': limit, } from_clause = """FROM assets a LEFT JOIN asset_categories ac ON a.id = ac.asset_id LEFT JOIN categories c ON c.id = ac.category_id LEFT JOIN asset_users au ON a.id = au.asset_id LEFT JOIN users u ON au.user_id = u.id LEFT JOIN activities act ON a.id = act.asset_id AND act.course_id = :course_id AND act.user_id = :user_id AND act.object_type = 'asset' AND act.type = 'asset_like'""" where_clause = _build_where_clause(filters, params) order_clause = _build_order_clause(sort) assets_query = text( f"""SELECT DISTINCT ON (a.id, a.likes, a.views, a.comment_count) a.*, act.type AS activity_type {from_clause} {where_clause} {order_clause} LIMIT :limit OFFSET :offset""") assets_result = list(db.session.execute(assets_query, params)) count_query = text( f'SELECT COUNT(DISTINCT(a.id))::int AS count {from_clause} {where_clause}' ) count_result = db.session.execute(count_query, params).fetchone() total = (count_result and count_result[0]) or 0 asset_ids = [r['id'] for r in assets_result] users_query = text("""SELECT au.asset_id, u.id, u.canvas_user_id, u.canvas_course_role, u.canvas_course_sections, u.canvas_enrollment_state, u.canvas_full_name, u.canvas_image FROM users u JOIN asset_users au ON au.user_id = u.id AND au.asset_id = ANY(:asset_ids) ORDER BY au.asset_id""") users_result = db.session.execute(users_query, {'asset_ids': asset_ids}) def _row_to_json_obj(row): d = dict(row) json_obj = dict() for key in d.keys(): if key.endswith('_at'): json_obj[camelize(key)] = isoformat(d[key]) elif isinstance(d[key], dict): json_obj[camelize(key)] = _row_to_json_obj(d[key]) else: json_obj[camelize(key)] = d[key] return json_obj users_by_asset = dict() for asset_id, user_rows in groupby(users_result, lambda r: r['asset_id']): users_by_asset[asset_id] = [_row_to_json_obj(r) for r in user_rows] def _row_to_json_asset(row): json_asset = _row_to_json_obj(row) # Has the current user liked the asset? json_asset['liked'] = (json_asset['activityType'] == 'asset_like') if json_asset['id'] in users_by_asset: json_asset['users'] = users_by_asset[json_asset['id']] json_asset['thumbnailUrl'] = get_s3_signed_url( json_asset['thumbnailUrl']) return json_asset results = { 'offset': offset, 'total': total, 'results': [_row_to_json_asset(r) for r in assets_result], } return results @classmethod def upload_to_s3(cls, filename, byte_stream, course_id): bucket = app.config['S3_BUCKET'] # S3 key begins with course id, reversed for performant key distribution, padded for readability. reverse_course = str(course_id)[::-1].rjust(7, '0') (basename, extension) = os.path.splitext(filename) # Truncate file basename if longer than 170 characters; the complete constructed S3 URI must come in under 255. key = f"{reverse_course}/assets/{datetime.now().strftime('%Y-%m-%d_%H%M%S')}-{basename[0:170]}{extension}" content_type = magic.from_buffer(byte_stream, mime=True) if put_binary_data_to_s3(bucket, key, byte_stream, content_type): return { 'content_type': content_type, 'download_url': f's3://{bucket}/{key}', } else: raise InternalServerError('Could not upload file.') 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 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 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 refresh_comments_count(self): self.comment_count = len(self.comments) db.session.add(self) std_commit() return self.comment_count 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 to_api_json(self, user_id=None): image_url = get_s3_signed_url(self.image_url) pdf_url = get_s3_signed_url(self.pdf_url) liked = False if user_id: like_query = Activity.query.filter_by( object_id=self.id, object_type='asset', activity_type='asset_like', user_id=user_id, ) if like_query.first() is not None: liked = True return { 'id': self.id, 'assetType': self.asset_type, 'body': self.body, 'canvasAssignment_id': self.canvas_assignment_id, 'categories': [c.to_api_json() for c in self.categories], 'commentCount': self.comment_count, 'courseId': self.course_id, 'description': self.description, 'dislikes': self.dislikes, 'downloadUrl': self.download_url, 'imageUrl': image_url, 'liked': liked, 'likes': self.likes, 'mime': self.mime, 'pdfUrl': pdf_url, 'previewMetadata': self.preview_metadata, 'previewStatus': self.preview_status, 'source': self.source, 'title': self.title, 'url': self.url, 'users': [u.to_api_json() for u in self.users], 'views': self.views, 'visible': self.visible, 'createdAt': isoformat(self.created_at), 'deletedAt': isoformat(self.deleted_at), 'updatedAt': isoformat(self.updated_at), }
class Canvas(Base): __tablename__ = 'canvas' canvas_api_domain = db.Column(db.String(255), nullable=False, primary_key=True) api_key = db.Column(db.String(255), nullable=False) logo = db.Column(db.String(255)) lti_key = db.Column(db.String(255), nullable=False) lti_secret = db.Column(db.String(255), nullable=False) name = db.Column(db.String(255), nullable=False) supports_custom_messaging = db.Column(db.Boolean, default=False, nullable=False) use_https = db.Column(db.Boolean, default=True, nullable=False) def __init__( self, canvas_api_domain, api_key, lti_key, lti_secret, name, logo=None, supports_custom_messaging=False, use_https=True, ): self.api_key = api_key self.canvas_api_domain = canvas_api_domain self.lti_key = lti_key self.lti_secret = lti_secret self.name = name self.logo = logo self.supports_custom_messaging = supports_custom_messaging self.use_https = use_https def __repr__(self): return f"""<Course api_key={self.api_key}, canvas_api_domain={self.canvas_api_domain}, lti_key={self.lti_key}, lti_secret={self.lti_secret}, name={self.name}, logo={self.logo}, supports_custom_messaging={self.supports_custom_messaging}, use_https={self.use_https}, created_at={self.created_at}, updated_at={self.updated_at}> """ @classmethod def find_by_domain(cls, canvas_api_domain): return cls.query.filter_by(canvas_api_domain=canvas_api_domain).first() @classmethod def get_all(cls): return cls.query.order_by(cls.name).all() def to_api_json(self): return { 'apiKey': self.api_key, 'canvasApiDomain': self.canvas_api_domain, 'ltiKey': self.lti_key, 'ltiSecret': self.lti_secret, 'logo': self.logo, 'name': self.name, 'supportsCustomMessaging': self.supports_custom_messaging, 'useHttps': self.use_https, 'createdAt': _isoformat(self.created_at), 'updatedAt': _isoformat(self.updated_at), }
class Comment(Base): __tablename__ = 'comments' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 asset_id = db.Column(db.Integer, db.ForeignKey('assets.id'), nullable=False) user_id = db.Column(db.Integer) body = db.Column(db.Text, nullable=False) parent_id = db.Column(db.Integer) asset = db.relationship('Asset', back_populates='comments') def __init__( self, asset_id, user_id, body, parent_id=None, ): self.body = body self.asset_id = asset_id self.user_id = user_id self.parent_id = parent_id def __repr__(self): return f"""<Comment asset_id={self.asset_id}, user_id={self.user_id}, parent_id={self.parent_id}, body={self.body}> """ @classmethod 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() @classmethod def find_by_id(cls, comment_id): return cls.query.filter_by(id=comment_id).first() @classmethod 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 @classmethod def update(cls, body, comment_id): comment = cls.find_by_id(comment_id) comment.body = body db.session.add(comment) std_commit() return comment @classmethod def get_comments(cls, asset_id): orphans = [] parents = [] # Sort reverse chronological order_by = desc(cls.created_at) for row in cls.query.filter_by( asset_id=asset_id).order_by(order_by).all(): comment = row.to_api_json() (orphans if row.parent_id else parents).append(comment) comment['replies'] = [] while len(orphans): orphan = orphans.pop(0) # Find the child's parent parent = next((c for c in (parents + orphans) if orphan['parentId'] == c['id']), None) if parent: parent['replies'].insert(0, orphan) return parents def to_api_json(self): return { 'id': self.id, 'userId': self.user_id, 'parentId': self.parent_id, 'body': self.body, 'createdAt': isoformat(self.created_at), 'updatedAt': isoformat(self.updated_at), }
class Course(Base): __tablename__ = 'courses' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 active = db.Column(db.Boolean, nullable=False) asset_library_url = db.Column(db.String(255)) canvas_api_domain = db.Column(db.String(255), nullable=False) canvas_course_id = db.Column(db.Integer, nullable=False) enable_daily_notifications = db.Column(db.Boolean, default=True, nullable=False) enable_upload = db.Column(db.Boolean, default=True, nullable=False) enable_weekly_notifications = db.Column(db.Boolean, default=True, nullable=False) engagement_index_url = db.Column(db.String(255)) name = db.Column(db.String(255)) last_polled = db.Column(db.DateTime) users = db.relationship('User', back_populates='course') def __init__( self, active, canvas_api_domain, canvas_course_id, asset_library_url=None, enable_daily_notifications=True, enable_upload=True, enable_weekly_notifications=True, engagement_index_url=None, name=None, ): self.active = active self.asset_library_url = asset_library_url self.canvas_api_domain = canvas_api_domain self.canvas_course_id = canvas_course_id self.enable_daily_notifications = enable_daily_notifications self.enable_upload = enable_upload self.enable_weekly_notifications = enable_weekly_notifications self.engagement_index_url = engagement_index_url self.name = name def __repr__(self): return f"""<Course active={self.active}, asset_library_url={self.asset_library_url}, canvas_api_domain={self.canvas_api_domain}, canvas_course_id={self.canvas_course_id}, enable_daily_notifications={self.enable_daily_notifications}, enable_upload={self.enable_upload}, enable_weekly_notifications={self.enable_weekly_notifications}, engagement_index_url={self.engagement_index_url}, id={self.id}, name={self.name}, created_at={self.created_at}, updated_at={self.updated_at}> """ @classmethod def find_by_id(cls, course_id): return cls.query.filter_by(id=course_id).first() @classmethod def find_by_canvas_course_id(cls, canvas_api_domain, canvas_course_id): return cls.query.filter_by(canvas_api_domain=canvas_api_domain, canvas_course_id=canvas_course_id).first() @classmethod 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 @classmethod 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 to_api_json(self): canvas = Canvas.find_by_domain( canvas_api_domain=self.canvas_api_domain) return { 'active': self.active, 'assetLibraryUrl': self.asset_library_url, 'canvas': canvas.to_api_json(), 'canvasApiDomain': self.canvas_api_domain, 'canvasCourseId': self.canvas_course_id, 'enableDailyNotifications': self.enable_daily_notifications, 'enableUpload': self.enable_upload, 'enableWeeklyNotifications': self.enable_weekly_notifications, 'engagementIndexUrl': self.engagement_index_url, 'id': self.id, 'name': self.name, 'lastPolled': _isoformat(self.last_polled), 'createdAt': _isoformat(self.created_at), 'updatedAt': _isoformat(self.updated_at), }
class Activity(Base): __tablename__ = 'activities' id = db.Column(db.Integer, nullable=False, primary_key=True) # noqa: A003 activity_type = db.Column('type', activities_type, nullable=False) object_id = db.Column(db.Integer) object_type = db.Column('object_type', activities_object_type, nullable=False) activity_metadata = db.Column('metadata', JSON) asset_id = db.Column(db.Integer) course_id = db.Column(db.Integer, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) actor_id = db.Column(db.Integer) reciprocal_id = db.Column(db.Integer) user = db.relationship('User') def __init__( self, activity_type, course_id, user_id, object_type, object_id=None, asset_id=None, actor_id=None, reciprocal_id=None, activity_metadata=None, ): self.activity_type = activity_type self.course_id = course_id self.user_id = user_id self.object_type = object_type self.object_id = object_id self.asset_id = asset_id self.actor_id = actor_id self.reciprocal_id = reciprocal_id self.activity_metadata = activity_metadata def __repr__(self): return f"""<Activity type={self.activity_type}, course_id={self.course_id}, user_id={self.user_id}, object_type={self.object_type} object_id={self.object_id} asset_id={self.asset_id} actor_id={self.actor_id} reciprocal_id={self.reciprocal_id} metadata={self.activity_metadata}> """ @classmethod 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 @classmethod def create_unless_exists(cls, **kwargs): if cls.query.filter_by(**kwargs).count() == 0: return cls.create(**kwargs) @classmethod def delete_by_object_id(cls, object_type, object_id): cls.query.filter(and_(cls.object_type == object_type, cls.object_id == object_id)).delete() @classmethod def find_by_object_id(cls, object_type, object_id): return cls.query.filter(and_(cls.object_type == object_type, cls.object_id == object_id)).all() def to_api_json(self): return { 'id': self.id, 'activityType': self.activity_type, 'courseId': self.course_id, 'userId': self.user_id, 'objectType': self.object_type, 'objectId': self.object_id, 'assetId': self.asset_id, 'actorId': self.actor_id, 'reciprocalId': self.reciprocal_id, 'metadata': self.activity_metadata, 'createdAt': isoformat(self.created_at), 'updatedAt': isoformat(self.updated_at), }