From 039e341f33eae276dfd5a93b58104df567545742 Mon Sep 17 00:00:00 2001 From: Casu Al Snek Date: Sat, 11 Jan 2025 13:13:01 +0545 Subject: [PATCH] Add backend blueprints layout --- backend/__init__.py | 0 backend/app.py | 15 +++ backend/blueprints/badge/.dirhold | 0 backend/blueprints/chat/.dirhold | 0 backend/blueprints/course/.dirhold | 0 backend/blueprints/notification/.dirhold | 0 backend/blueprints/profile/.dirhold | 0 backend/blueprints/quiz/.dirhold | 0 backend/blueprints/session/.dirhold | 0 backend/config.py | 19 +++ backend/constants/__init__.py | 20 +++ backend/db/__init__.py | 0 backend/db/model.py | 148 +++++++++++++++++++++++ 13 files changed, 202 insertions(+) create mode 100644 backend/__init__.py create mode 100644 backend/app.py create mode 100644 backend/blueprints/badge/.dirhold create mode 100644 backend/blueprints/chat/.dirhold create mode 100644 backend/blueprints/course/.dirhold create mode 100644 backend/blueprints/notification/.dirhold create mode 100644 backend/blueprints/profile/.dirhold create mode 100644 backend/blueprints/quiz/.dirhold create mode 100644 backend/blueprints/session/.dirhold create mode 100644 backend/config.py create mode 100644 backend/constants/__init__.py create mode 100644 backend/db/__init__.py create mode 100644 backend/db/model.py diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app.py b/backend/app.py new file mode 100644 index 0000000..6f8e7cc --- /dev/null +++ b/backend/app.py @@ -0,0 +1,15 @@ +from flask import Flask +from db.model import db +from config import * +app = Flask(__name__) +app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI +db.init_app(app) + +@app.route('/', methods=['GET', 'POST']) +def homepage(): + return {'message': 'Cocks were sucked !'}, 200 + +if __name__ == '__main__': + with app.app_context(): + db.create_all() + app.run(host='0.0.0.0', port=9999, debug=True) \ No newline at end of file diff --git a/backend/blueprints/badge/.dirhold b/backend/blueprints/badge/.dirhold new file mode 100644 index 0000000..e69de29 diff --git a/backend/blueprints/chat/.dirhold b/backend/blueprints/chat/.dirhold new file mode 100644 index 0000000..e69de29 diff --git a/backend/blueprints/course/.dirhold b/backend/blueprints/course/.dirhold new file mode 100644 index 0000000..e69de29 diff --git a/backend/blueprints/notification/.dirhold b/backend/blueprints/notification/.dirhold new file mode 100644 index 0000000..e69de29 diff --git a/backend/blueprints/profile/.dirhold b/backend/blueprints/profile/.dirhold new file mode 100644 index 0000000..e69de29 diff --git a/backend/blueprints/quiz/.dirhold b/backend/blueprints/quiz/.dirhold new file mode 100644 index 0000000..e69de29 diff --git a/backend/blueprints/session/.dirhold b/backend/blueprints/session/.dirhold new file mode 100644 index 0000000..e69de29 diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 0000000..8aba1ae --- /dev/null +++ b/backend/config.py @@ -0,0 +1,19 @@ +import os + +DB_ENGINE: str = "postgresql" +DB_USER: str = "postgres" +DB_PASSWORD: str = "1234" +DB_HOST: str = "localhost" +DB_PORT: int = 5432 +DB_NAME: str = "educonnect" + + +DEFAULT_PROFILE_FILE: str = "defaultUserBanner.png" +DEFAULT_COURSE_COVER: str = "defaultCourseCover.png" +DEFAULT_BADGE_ICON: str = "defaultBadgeIcon.png" + +PROJECT_ROOT: os.path = os.path.dirname(os.path.abspath(__file__)) +USER_UPLOADS_DIR: str = os.path.join(PROJECT_ROOT, "uploads") + +DB_URI: str = f"{DB_ENGINE}://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}" +ACTIVATE_ACCOUNTS_ON_SIGNUP: bool = True \ No newline at end of file diff --git a/backend/constants/__init__.py b/backend/constants/__init__.py new file mode 100644 index 0000000..8157ce2 --- /dev/null +++ b/backend/constants/__init__.py @@ -0,0 +1,20 @@ +from enum import Enum + +class UserRole(Enum): + ADMIN = 0 + USER = 1 + +class PublishedStatus(Enum): + APPROVED = 0 + PENDING = 1 + DECLINED = 2 + REVOKED = 3 + BANNED = 4 + DRAFT = 5 + +class NotificationTypes(Enum): + MENTION = 0 + COURSE_PUBLISH_STATUS_UPDATE = 1 + NEW_BADGE = 2 + TEXT_WITH_URL = 3 + PLAINTEXT_NOTICE = 4 \ No newline at end of file diff --git a/backend/db/__init__.py b/backend/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/db/model.py b/backend/db/model.py new file mode 100644 index 0000000..7a24772 --- /dev/null +++ b/backend/db/model.py @@ -0,0 +1,148 @@ +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, MappedAsDataclass +from sqlalchemy import types, text, String, DateTime, func, Boolean, ForeignKey, SmallInteger +from datetime import datetime +import uuid +from typing import List +from config import * +from constants import UserRole, PublishedStatus + +class Base(MappedAsDataclass, DeclarativeBase): + pass + +db = SQLAlchemy(model_class=Base) + +class User(db.Model): + __tablename__ = 'user' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + email: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) + firstName: Mapped[str] = mapped_column(String(32), nullable=False) + lastName: Mapped[str] = mapped_column(String(32), nullable=False) + username: Mapped[str] = mapped_column(String(32), nullable=False) + sessions: Mapped[List["Session"]] = relationship(back_populates="user", cascade="all, delete-orphan") + enrollments: Mapped[List["Enrollment"]] = relationship(back_populates="user", cascade="all, delete-orphan") + quizzes: Mapped[List["Quiz"]] = relationship(back_populates="creatorUser", cascade="all, delete-orphan") + pfpFilename: Mapped[str] = mapped_column(String(256), nullable=False, default=DEFAULT_PROFILE_FILE) + joinedDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + lastOnline: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now(), onupdate=func.now()) + bio: Mapped[str] = mapped_column(String(256), nullable=False, default='') + role: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=UserRole.USER) + isActivated: Mapped[bool] = mapped_column(Boolean, nullable=False, default=ACTIVATE_ACCOUNTS_ON_SIGNUP) + +class Session(db.Model): + __tablename__ = 'session' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) + user: Mapped["User"] = relationship(back_populates="sessions") + key: Mapped[str] = mapped_column(String(256), nullable=False, unique=True) + ua: Mapped[str] = mapped_column(String(1024), nullable=False) + creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + lastUsed: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now(), onupdate=func.now()) + isValid: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) + +class Category(db.Model): + __tablename__ = 'category' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + name: Mapped[str] = mapped_column(String(32), nullable=False, unique=True) + courses: Mapped[List["Course"]] = relationship(back_populates="category", cascade="all, delete-orphan") + description: Mapped[str] = mapped_column(String(128), nullable=False, default='') + isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) + creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + +class Course(db.Model): + __tablename__ = 'course' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + name: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) + categoryID: Mapped[uuid.UUID] = mapped_column(ForeignKey("category.id")) + category: Mapped["Category"] = relationship(back_populates="courses") + enrollments: Mapped[List["Enrollment"]] = relationship(back_populates="course", cascade="all, delete-orphan") + quizzes: Mapped[List['Quiz']] = relationship(back_populates="course", cascade="all, delete-orphan") + description: Mapped[str] = mapped_column(String(1024), nullable=False, default='') + isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) + publishedStatus: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=PublishedStatus.DRAFT) + creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + coverImage: Mapped[str] = mapped_column(String(256), nullable=False, default=DEFAULT_COURSE_COVER) + serverFilename: Mapped[str] = mapped_column(String(256), nullable=False, default='') + +class Enrollment(db.Model): + __tablename__ = 'enrollment' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) + user: Mapped["User"] = relationship(back_populates="enrollments") + courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id")) + course: Mapped["Course"] = relationship(back_populates="enrollments") + joinedDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + lastActivity: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now(), onupdate=func.now()) + currentPage: Mapped[int] = mapped_column(nullable=False, default=1) + maxPage: Mapped[int] = mapped_column(nullable=False, default=1) + +class Quiz(db.Model): + __tablename__ = 'quiz' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + creatorUserID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) + creatorUser: Mapped["User"] = relationship(back_populates="quizzes") + courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id")) + course: Mapped["Course"] = relationship(back_populates="quizzes") + quizJson: Mapped[str] = mapped_column(String, nullable=False) + creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) + +class QuizAttempt(db.Model): + __tablename__ = 'quiz_attempts' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) + user: Mapped["User"] = relationship(back_populates="quiz_attempts") + quizID: Mapped[uuid.UUID] = mapped_column(ForeignKey("quiz.id")) + quiz: Mapped["Quiz"] = relationship(back_populates="quiz_attempts") + answerKey: Mapped[str] = mapped_column(String, nullable=False) + score: Mapped[int] = mapped_column(default=0, nullable=False) + attemptDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + +class Chat(db.Model): + __tablename__ = 'chat' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + textContent: Mapped[str] = mapped_column(nullable=False) + userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) + courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id")) + course: Mapped["Course"] = relationship(back_populates="chats") + user: Mapped["User"] = relationship(back_populates="chats") + chatDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + +class Notification(db.Model): + __tablename__ = 'notification' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) + user: Mapped["User"] = relationship(back_populates="notifications") + notificationType: Mapped[int] = mapped_column(SmallInteger, nullable=False) + notificationData: Mapped[str] = mapped_column(String, nullable=False) + isSeen: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) + notifDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + +class Badge(db.Model): + __tablename__ = 'badge' + + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + name: Mapped[str] = mapped_column(String(16), nullable=False) + description: Mapped[str] = mapped_column(String(256), nullable=False, default='') + createDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + icon: Mapped[str] = mapped_column(String(256), nullable=False, default='') + canClaim: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) + +class UserBadge(db.Model): + __tablename__ = 'userbadge' + id: Mapped[uuid.UUID] = mapped_column(types.Uuid, primary_key=True, init=False, server_default=text("gen_random_uuid()")) + userID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) + badgeID: Mapped[uuid.UUID] = mapped_column(ForeignKey("badge.id")) + user: Mapped["User"] = relationship(back_populates="user_badges") + badge: Mapped["Badge"] = relationship(back_populates="user_badges") + claimedDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) + \ No newline at end of file