diff --git a/backend/app/models.py b/backend/app/models.py index 3df09c5..de4d95b 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -58,7 +58,7 @@ class User(UserMixin, db.Model): "id": self.id, "username": self.username, "email": self.email, - "role": self.role + "role": self.role, } def from_dict(self, data, new_user=False) -> None: @@ -76,6 +76,7 @@ class Course(db.Model): description = sa.Column(sa.Text, index=True) instructor = sa.Column(sa.ForeignKey(User.id), index=True) created_at = sa.Column(sa.DateTime) + assignments = db.relationship("Assignment", backref="course", lazy="dynamic") def __repr__(self) -> str: return f"" @@ -95,3 +96,35 @@ class Course(db.Model): d["instructor"] = User.query.get(self.instructor).username return d + + +class Assignment(db.Model): + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.String(128), index=True) + course_id = sa.Column(sa.ForeignKey(Course.id), index=True) + description = sa.Column(sa.Text, index=True) + due_date = sa.Column(sa.DateTime) + created_at = sa.Column(sa.DateTime) + + def from_dict(self, data) -> None: + for field in ["name", "course_id", "description", "due_date"]: + if field in data: + setattr(self, field, data[field]) + + if not self.created_at: + self.created_at = datetime.now() + + def from_dict(self, data) -> None: + for field in ["name", "course_id", "description", "due_date"]: + if field in data: + setattr(self, field, data[field]) + + if not self.created_at: + self.created_at = datetime.now() + + def to_dict(self) -> dict: + d = {} + for f in ["id", "name", "course_id", "description", "due_date", "created_at"]: + d[f] = getattr(self, f) + + return d diff --git a/backend/app/routes.py b/backend/app/routes.py index 5c57a2e..bf63657 100644 --- a/backend/app/routes.py +++ b/backend/app/routes.py @@ -6,7 +6,7 @@ from app.errors import error_response from flask_login import current_user from app import login, db -from app.models import Course, User +from app.models import Course, User, Assignment @login.user_loader @@ -125,6 +125,7 @@ def get_courses(id): resp = jsonify(d) return resp + @bp.route("/course//students", methods=["GET"]) @login_required def get_students_in_course(id): @@ -138,6 +139,7 @@ def get_students_in_course(id): resp["students"].append(s.to_dict()) return jsonify(resp) + @bp.route("/course/", methods=["GET"]) @login_required def get_course(id): @@ -147,6 +149,7 @@ def get_course(id): resp = jsonify(c.to_dict()) return resp + @bp.route("/user//enroll/", methods=["POST", "DELETE"]) @login_required @instructor_required @@ -172,6 +175,7 @@ def enroll_student(uid, cid): resp = {"user": u.to_dict(), "course": c.to_dict()} return jsonify(resp) + @bp.route("/user//enroll/", methods=["POST"]) @login_required @instructor_required @@ -197,3 +201,22 @@ def enroll_student_by_username(username, cid): resp = {"user": u.to_dict(), "course": c.to_dict()} return jsonify(resp) + +@bp.route("/assignment", methods=["POST"]) +@login_required +@instructor_required +def create_assignment(): + data = request.get_json() + required_fields = ["name", "description", "course_id", "due_date"] + if f := check_data(data, required_fields): + return error_response(400, f"Must supply {f}") + + c = Course.query.get(data["course_id"]) + if not c: + return error_response(400, f"Course with id {data['course_id']} does not exist") + + a = Assignment() + a.from_dict(data) + db.session.add(a) + db.session.commit() + return jsonify(a.to_dict()) diff --git a/backend/main.py b/backend/main.py index 326120a..8a8832d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -3,7 +3,7 @@ import dotenv dotenv.load_dotenv() from app import create_app, db -from app.models import Course, User +from app.models import Assignment, Course, User from flask_cors import CORS app = create_app() @@ -12,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, "Course": Course} + return {"db": db, "User": User, "Course": Course, "Assignment": Assignment} diff --git a/backend/migrations/versions/cab0d39ef662_.py b/backend/migrations/versions/cab0d39ef662_.py new file mode 100644 index 0000000..00121d0 --- /dev/null +++ b/backend/migrations/versions/cab0d39ef662_.py @@ -0,0 +1,46 @@ +"""Create assignment model + +Revision ID: cab0d39ef662 +Revises: 862905f5e34a +Create Date: 2023-04-13 18:27:15.748107 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'cab0d39ef662' +down_revision = '862905f5e34a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('assignment', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=128), nullable=True), + sa.Column('course_id', sa.Integer(), nullable=True), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['course_id'], ['course.id'], ), + sa.PrimaryKeyConstraint('id') + ) + with op.batch_alter_table('assignment', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_assignment_course_id'), ['course_id'], unique=False) + batch_op.create_index(batch_op.f('ix_assignment_description'), ['description'], unique=False) + batch_op.create_index(batch_op.f('ix_assignment_name'), ['name'], unique=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('assignment', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_assignment_name')) + batch_op.drop_index(batch_op.f('ix_assignment_description')) + batch_op.drop_index(batch_op.f('ix_assignment_course_id')) + + op.drop_table('assignment') + # ### end Alembic commands ###