diff --git a/backend/app/models.py b/backend/app/models.py index 5bb9635..8b96c88 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -1,17 +1,18 @@ from app import db +import sqlalchemy as sa from flask_login import UserMixin from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash class User(UserMixin, db.Model): - id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(64), index=True, unique=True) - role = db.Column(db.String(32), index=True) - email = db.Column(db.String(120), index=True, unique=True) - password_hash = db.Column(db.String(128)) - last_seen = db.Column(db.DateTime, default=datetime.utcnow) - token = db.Column(db.String(32), index=True, unique=True) + id = sa.Column(sa.Integer, primary_key=True) + username = sa.Column(sa.String(64), index=True, unique=True) + role = sa.Column(sa.String(32), index=True) + email = sa.Column(sa.String(120), index=True, unique=True) + password_hash = sa.Column(sa.String(128)) + last_seen = sa.Column(sa.DateTime, default=datetime.utcnow) + token = sa.Column(sa.String(32), index=True, unique=True) def __repr__(self): return f"" @@ -35,3 +36,25 @@ class User(UserMixin, db.Model): setattr(self, field, data[field]) if new_user and "password" in data: self.set_password(data["password"]) + + +class Course(db.Model): + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.String(128), index=True) + description = sa.Column(sa.Text, index=True) + instructor = sa.Column(sa.ForeignKey(User.id), index=True) + created_at = sa.Column(sa.DateTime) + + def from_dict(self, data): + for field in ["name", "description", "instructor"]: + if field in data: + setattr(self, field, data[field]) + + if not self.created_at: + self.created_at = datetime.now() + + def to_dict(self): + d = {} + for f in ["id", "name", "description", "instructor", "created_at"]: + d[f] = getattr(self, f) + return d diff --git a/backend/app/routes.py b/backend/app/routes.py index 649411a..5ee41b6 100644 --- a/backend/app/routes.py +++ b/backend/app/routes.py @@ -5,7 +5,7 @@ from app.errors import error_response from flask_login import current_user from app import login, db -from app.models import User +from app.models import Course, User @login.user_loader @@ -13,6 +13,13 @@ def load_user(user_id): return User.query.get(user_id) +def check_data(data, required_fields): + for f in required_fields: + if f not in data: + return f + return None + + @bp.route("/login", methods=["POST"]) def login_route(): data = request.get_json() @@ -46,11 +53,9 @@ def logout_route(): @bp.route("/register", methods=["POST"]) def register(): data = request.get_json() - required_fields = ["role", "username", "email", "password", "password2"] - for f in required_fields: - if f not in data: - return error_response(400, f"Must supply {f}") + if f := check_data(data, required_fields): + return error_response(400, f"Must supply {f}") if User.query.filter_by(username=data["username"]).first(): return error_response(409, "User with that username already exists") @@ -65,3 +70,28 @@ def register(): resp = jsonify(u.to_dict()) return resp + + +@bp.route("/course", methods=["POST"]) +def create_course(): + data = request.get_json() + + required_fields = ["name", "description", "instructor"] + + if f := check_data(data, required_fields): + return error_response(400, f"Must supply {f}") + + u = User.query.get(data["instructor"]) + if not u: + return error_response(400, f"User with id {data['instructor']} does not exist") + + if u.role != "instructor": + return error_response(400, "User is not instructor") + + c = Course() + c.from_dict(data) + db.session.add(c) + db.session.commit() + + return jsonify(c.to_dict()) + diff --git a/backend/main.py b/backend/main.py index be9b9c8..326120a 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,8 +1,9 @@ import dotenv + dotenv.load_dotenv() from app import create_app, db -from app.models import User +from app.models import Course, User from flask_cors import CORS app = create_app() @@ -11,4 +12,4 @@ CORS(app, supports_credentials=True, resources={r"/.*": {"origins": r".*localhos @app.shell_context_processor def make_shell_context(): - return {"db": db, "User": User} + return {"db": db, "User": User, "Course": Course} diff --git a/backend/migrations/versions/471b4225837e_.py b/backend/migrations/versions/471b4225837e_.py new file mode 100644 index 0000000..0fcb7df --- /dev/null +++ b/backend/migrations/versions/471b4225837e_.py @@ -0,0 +1,46 @@ +"""Create course model + +Revision ID: 471b4225837e +Revises: 8e48199f1417 +Create Date: 2023-04-06 14:53:43.061616 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '471b4225837e' +down_revision = '8e48199f1417' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('course', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=128), nullable=True), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('instructor', sa.Integer(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['instructor'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + with op.batch_alter_table('course', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_course_description'), ['description'], unique=False) + batch_op.create_index(batch_op.f('ix_course_instructor'), ['instructor'], unique=False) + batch_op.create_index(batch_op.f('ix_course_name'), ['name'], unique=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('course', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_course_name')) + batch_op.drop_index(batch_op.f('ix_course_instructor')) + batch_op.drop_index(batch_op.f('ix_course_description')) + + op.drop_table('course') + # ### end Alembic commands ### diff --git a/backend/migrations/versions/7736bc740f9b_.py b/backend/migrations/versions/7736bc740f9b_.py index 9a00ee3..b06d60f 100644 --- a/backend/migrations/versions/7736bc740f9b_.py +++ b/backend/migrations/versions/7736bc740f9b_.py @@ -1,4 +1,4 @@ -"""empty message +"""Create user model Revision ID: 7736bc740f9b Revises: diff --git a/backend/migrations/versions/8e48199f1417_.py b/backend/migrations/versions/8e48199f1417_.py index 96f5d0a..b57cb22 100644 --- a/backend/migrations/versions/8e48199f1417_.py +++ b/backend/migrations/versions/8e48199f1417_.py @@ -1,4 +1,4 @@ -"""empty message +"""Add user role, remove about_me Revision ID: 8e48199f1417 Revises: 7736bc740f9b