Source code for app.models

from app import db, app
from flask_login import UserMixin
from cryptography.fernet import Fernet
import base64
import hashlib

[docs] def get_cipher_suite(): """ Generates a Fernet cipher suite based on the application's SECRET_KEY. Returns: cryptography.fernet.Fernet: A Fernet instance for encryption/decryption. None: If an error occurs during key generation. """ try: key = app.config['SECRET_KEY'].encode() digest = hashlib.sha256(key).digest() fernet_key = base64.urlsafe_b64encode(digest) return Fernet(fernet_key) except Exception: return None
[docs] def encrypt_value(value): """ Encrypts a string value using the application's cipher suite. Args: value (str): The string to encrypt. Returns: str: The encrypted string, or the original value if encryption fails or value is None. """ if not value: return None cipher = get_cipher_suite() if not cipher: return value try: return cipher.encrypt(value.encode()).decode() except Exception as e: print(f"Encryption error: {e}") return value
[docs] def decrypt_value(value): """ Decrypts an encrypted string value using the application's cipher suite. Args: value (str): The encrypted string to decrypt. Returns: str: The decrypted string, or the original value if decryption fails or value is None. """ if not value: return None cipher = get_cipher_suite() if not cipher: return value try: return cipher.decrypt(value.encode()).decode() except Exception: return value
[docs] class User(UserMixin, db.Model): """ Represents a user of the application. Attributes: id (int): Primary key. google_id (str): Unique Google ID of the user. email (str): User's email address. name (str): User's full name. picture (str): URL to the user's profile picture. _access_token (str): Encrypted OAuth access token. _refresh_token (str): Encrypted OAuth refresh token. token_expiry (datetime): Expiration time of the access token. token_uri (str): URI for token refreshing. _client_id (str): Encrypted OAuth client ID. _client_secret (str): Encrypted OAuth client secret. scopes (str): Comma-separated list of granted OAuth scopes. courses (dynamic): Relationship to the user's courses. """ id = db.Column(db.Integer, primary_key=True) google_id = db.Column(db.String(100), unique=True, nullable=False) email = db.Column(db.String(120), index=True, unique=True, nullable=False) name = db.Column(db.String(100)) picture = db.Column(db.String(200)) # OAuth tokens (Stored encrypted) _access_token = db.Column('access_token', db.Text) _refresh_token = db.Column('refresh_token', db.Text) token_expiry = db.Column(db.DateTime) token_uri = db.Column(db.String(200)) _client_id = db.Column('client_id', db.Text) _client_secret = db.Column('client_secret', db.Text) scopes = db.Column(db.String(500)) courses = db.relationship('Course', backref='user', lazy='dynamic') @property def access_token(self): """Decrypted OAuth access token.""" return decrypt_value(self._access_token) @access_token.setter def access_token(self, value): self._access_token = encrypt_value(value) @property def refresh_token(self): """Decrypted OAuth refresh token.""" return decrypt_value(self._refresh_token) @refresh_token.setter def refresh_token(self, value): self._refresh_token = encrypt_value(value) @property def client_id(self): """Decrypted OAuth client ID.""" return decrypt_value(self._client_id) @client_id.setter def client_id(self, value): self._client_id = encrypt_value(value) @property def client_secret(self): """Decrypted OAuth client secret.""" return decrypt_value(self._client_secret) @client_secret.setter def client_secret(self, value): self._client_secret = encrypt_value(value) def __repr__(self): return '<User {}>'.format(self.email)
[docs] class Course(db.Model): """ Represents a course (class) in the application. Stores local overrides and settings for a Google Classroom course. Attributes: id (int): Primary key. user_id (int): Foreign key to the User. google_course_id (str): ID of the course in Google Classroom. custom_name (str): User-defined name for the course. custom_section (str): User-defined section for the course. custom_code (str): User-defined class code (enrollment code). custom_banner (str): URL to a custom banner image. custom_icon (str): URL to a custom icon. cached_teacher_name (str): Cached name of the teacher from Google. custom_teacher_name (str): User-defined teacher name. is_archived (bool): Whether the course is archived locally. is_pinned (bool): Whether the course is pinned (not currently used). display_order (int): Order index for display sorting. user_tags (list): List of UserTags associated with this course. """ id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) google_course_id = db.Column(db.String(100), nullable=False) # Local overrides custom_name = db.Column(db.String(200)) custom_section = db.Column(db.String(200)) custom_code = db.Column(db.String(100)) # For "Class Code" rename custom_banner = db.Column(db.String(500)) custom_icon = db.Column(db.String(500)) # Teacher info cached_teacher_name = db.Column(db.String(100)) custom_teacher_name = db.Column(db.String(100)) is_archived = db.Column(db.Boolean, default=False) # User archived is_pinned = db.Column(db.Boolean, default=False) display_order = db.Column(db.Integer, default=0) user_tags = db.relationship('UserTag', secondary='course_tags_map', lazy='subquery', backref=db.backref('courses', lazy=True)) __table_args__ = (db.UniqueConstraint('user_id', 'google_course_id', name='_user_course_uc'),) def __repr__(self): return '<Course {}>'.format(self.google_course_id)
[docs] class CourseTag(db.Model): """ Represents a tag specific to a course, used for tagging items within that course. (Note: This seems to be a legacy or specific-use tag, distinct from UserTag). Attributes: id (int): Primary key. course_id (int): Foreign key to the Course. name (str): Name of the tag. color (str): Color code for the tag. """ id = db.Column(db.Integer, primary_key=True) course_id = db.Column(db.Integer, db.ForeignKey('course.id'), nullable=False) name = db.Column(db.String(50), nullable=False) color = db.Column(db.String(20), default='blue') course = db.relationship('Course', backref=db.backref('tags', lazy=True, cascade='all, delete-orphan')) __table_args__ = (db.UniqueConstraint('course_id', 'name', name='_course_tag_uc'),)
[docs] class ItemTag(db.Model): """ Associates a CourseTag with a specific item (assignment/material) in Google Classroom. Attributes: id (int): Primary key. tag_id (int): Foreign key to the CourseTag. google_item_id (str): ID of the item in Google Classroom. """ id = db.Column(db.Integer, primary_key=True) tag_id = db.Column(db.Integer, db.ForeignKey('course_tag.id'), nullable=False) google_item_id = db.Column(db.String(100), nullable=False) tag = db.relationship('CourseTag', backref=db.backref('item_assignments', lazy=True, cascade='all, delete-orphan')) __table_args__ = (db.UniqueConstraint('tag_id', 'google_item_id', name='_tag_item_uc'),)
[docs] class MutedItem(db.Model): """ Represents an item (assignment) that a user has muted/hidden from the 'Missing Assignments' list. Attributes: id (int): Primary key. user_id (int): Foreign key to the User. google_item_id (str): ID of the item in Google Classroom. """ id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) google_item_id = db.Column(db.String(100), nullable=False) __table_args__ = (db.UniqueConstraint('user_id', 'google_item_id', name='_user_item_mute_uc'),)
# Association table for Course <-> UserTag course_tags_map = db.Table('course_tags_map', db.Column('course_id', db.Integer, db.ForeignKey('course.id'), primary_key=True), db.Column('user_tag_id', db.Integer, db.ForeignKey('user_tag.id'), primary_key=True) )
[docs] class UserTag(db.Model): """ Represents a global tag created by a user to organize their courses. Attributes: id (int): Primary key. user_id (int): Foreign key to the User. name (str): Name of the tag. """ id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) name = db.Column(db.String(50), nullable=False) __table_args__ = (db.UniqueConstraint('user_id', 'name', name='_user_tag_name_uc'),)