mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add Assignments, Tasks, Submissions CRUD
This commit is contained in:
parent
cd2397f4f7
commit
47782b57bc
32 changed files with 1719 additions and 218 deletions
|
|
@ -1,133 +0,0 @@
|
|||
from typing import Optional
|
||||
from openai import BaseModel
|
||||
from sqlalchemy import JSON, Column, ForeignKey
|
||||
from sqlmodel import Field, SQLModel
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class AssignmentTypeEnum(str, Enum):
|
||||
FILE_SUBMISSION = "FILE_SUBMISSION"
|
||||
QUIZ = "QUIZ"
|
||||
OTHER = "OTHER"
|
||||
|
||||
|
||||
class Assignment(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
assignment_uuid: str
|
||||
title: str
|
||||
description: str
|
||||
due_date: str
|
||||
|
||||
org_id: int = Field(
|
||||
sa_column=Column("org_id", ForeignKey("organization.id", ondelete="CASCADE"))
|
||||
)
|
||||
course_id: int = Field(
|
||||
sa_column=Column("course_id", ForeignKey("course.id", ondelete="CASCADE"))
|
||||
)
|
||||
chapter_id: int = Field(
|
||||
sa_column=Column("chapter_id", ForeignKey("chapter.id", ondelete="CASCADE"))
|
||||
)
|
||||
activity_id: int = Field(
|
||||
sa_column=Column("activity_id", ForeignKey("activity.id", ondelete="CASCADE"))
|
||||
)
|
||||
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
|
||||
class AssignmentTask(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
assignment_task_uuid: str = ""
|
||||
title: str = ""
|
||||
description: str = ""
|
||||
hint: str = ""
|
||||
assignment_type: AssignmentTypeEnum
|
||||
contents: dict = Field(default={}, sa_column=Column(JSON))
|
||||
max_grade_value: int = (
|
||||
0 # Value is always between 0-100 and is used as the maximum grade for the task
|
||||
)
|
||||
|
||||
# Foreign keys
|
||||
assignment_id: int = Field(
|
||||
sa_column=Column(
|
||||
"assignment_id", ForeignKey("assignment.id", ondelete="CASCADE")
|
||||
)
|
||||
)
|
||||
org_id: int = Field(
|
||||
sa_column=Column("org_id", ForeignKey("organization.id", ondelete="CASCADE"))
|
||||
)
|
||||
course_id: int = Field(
|
||||
sa_column=Column("course_id", ForeignKey("course.id", ondelete="CASCADE"))
|
||||
)
|
||||
chapter_id: int = Field(
|
||||
sa_column=Column("chapter_id", ForeignKey("chapter.id", ondelete="CASCADE"))
|
||||
)
|
||||
activity_id: int = Field(
|
||||
sa_column=Column("activity_id", ForeignKey("activity.id", ondelete="CASCADE"))
|
||||
)
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
|
||||
class AssignmentTaskSubmission(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
assignment_task_submission_uuid: str = ""
|
||||
task_submission: dict = Field(default={}, sa_column=Column(JSON))
|
||||
grade: int = (
|
||||
0 # Value is always between 0-100 depending on the questions, this is used to calculate the final grade on the AssignmentUser model
|
||||
)
|
||||
task_submission_grade_feedback: str = "" # Feedback given by the teacher
|
||||
|
||||
# Foreign keys
|
||||
user_id: int = Field(
|
||||
sa_column=Column("user_id", ForeignKey("user.id", ondelete="CASCADE"))
|
||||
)
|
||||
activity_id: int = Field(
|
||||
sa_column=Column("activity_id", ForeignKey("activity.id", ondelete="CASCADE"))
|
||||
)
|
||||
course_id: int = Field(
|
||||
sa_column=Column("course_id", ForeignKey("course.id", ondelete="CASCADE"))
|
||||
)
|
||||
chapter_id: int = Field(
|
||||
sa_column=Column("chapter_id", ForeignKey("chapter.id", ondelete="CASCADE"))
|
||||
)
|
||||
assignment_task_id: int = Field(
|
||||
sa_column=Column(
|
||||
"assignment_task_id", ForeignKey("assignment_task.id", ondelete="CASCADE")
|
||||
)
|
||||
)
|
||||
creation_date: str = ""
|
||||
update_date: str = ""
|
||||
|
||||
|
||||
# Note on grading :
|
||||
# To calculate the final grade :
|
||||
|
||||
|
||||
|
||||
|
||||
class AssignmentUserSubmissionStatus(str, Enum):
|
||||
PENDING = "PENDING"
|
||||
SUBMITTED = "SUBMITTED"
|
||||
GRADED = "GRADED"
|
||||
LATE = "LATE"
|
||||
NOT_SUBMITTED = "NOT_SUBMITTED"
|
||||
|
||||
|
||||
class AssignmentUserSubmission(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
assignment_user_uuid: str = ""
|
||||
submission_status: AssignmentUserSubmissionStatus = (
|
||||
AssignmentUserSubmissionStatus.PENDING
|
||||
)
|
||||
grading: str = ""
|
||||
user_id: int = Field(
|
||||
sa_column=Column("user_id", ForeignKey("user.id", ondelete="CASCADE"))
|
||||
)
|
||||
assignment_id: int = Field(
|
||||
sa_column=Column(
|
||||
"assignment_id", ForeignKey("assignment.id", ondelete="CASCADE")
|
||||
)
|
||||
)
|
||||
creation_date: str = ""
|
||||
update_date: str = ""
|
||||
317
apps/api/src/db/courses/assignments.py
Normal file
317
apps/api/src/db/courses/assignments.py
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
from typing import Optional, Dict
|
||||
from sqlalchemy import JSON, Column, ForeignKey
|
||||
from sqlmodel import Field, SQLModel
|
||||
from enum import Enum
|
||||
|
||||
|
||||
|
||||
## Assignment ##
|
||||
class GradingTypeEnum(str, Enum):
|
||||
ALPHABET = "ALPHABET"
|
||||
NUMERIC = "NUMERIC"
|
||||
PERCENTAGE = "PERCENTAGE"
|
||||
|
||||
|
||||
class AssignmentBase(SQLModel):
|
||||
"""Represents the common fields for an assignment."""
|
||||
|
||||
title: str
|
||||
description: str
|
||||
due_date: str
|
||||
published: Optional[bool] = False
|
||||
grading_type: GradingTypeEnum
|
||||
|
||||
org_id: int
|
||||
course_id: int
|
||||
chapter_id: int
|
||||
activity_id: int
|
||||
|
||||
|
||||
class AssignmentCreate(AssignmentBase):
|
||||
"""Model for creating a new assignment."""
|
||||
|
||||
pass # Inherits all fields from AssignmentBase
|
||||
|
||||
|
||||
class AssignmentRead(AssignmentBase):
|
||||
"""Model for reading an assignment."""
|
||||
|
||||
id: int
|
||||
assignment_uuid: str
|
||||
creation_date: Optional[str]
|
||||
update_date: Optional[str]
|
||||
|
||||
|
||||
class AssignmentUpdate(SQLModel):
|
||||
"""Model for updating an assignment."""
|
||||
|
||||
title: Optional[str]
|
||||
description: Optional[str]
|
||||
due_date: Optional[str]
|
||||
published: Optional[bool]
|
||||
grading_type: Optional[GradingTypeEnum]
|
||||
org_id: Optional[int]
|
||||
course_id: Optional[int]
|
||||
chapter_id: Optional[int]
|
||||
activity_id: Optional[int]
|
||||
update_date: Optional[str]
|
||||
|
||||
|
||||
class Assignment(AssignmentBase, table=True):
|
||||
"""Represents an assignment with relevant details and foreign keys."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
creation_date: Optional[str]
|
||||
update_date: Optional[str]
|
||||
assignment_uuid: str
|
||||
|
||||
org_id: int = Field(
|
||||
sa_column=Column("org_id", ForeignKey("organization.id", ondelete="CASCADE"))
|
||||
)
|
||||
course_id: int = Field(
|
||||
sa_column=Column("course_id", ForeignKey("course.id", ondelete="CASCADE"))
|
||||
)
|
||||
chapter_id: int = Field(
|
||||
sa_column=Column("chapter_id", ForeignKey("chapter.id", ondelete="CASCADE"))
|
||||
)
|
||||
activity_id: int = Field(
|
||||
sa_column=Column("activity_id", ForeignKey("activity.id", ondelete="CASCADE"))
|
||||
)
|
||||
|
||||
|
||||
## Assignment ##
|
||||
|
||||
## AssignmentTask ##
|
||||
|
||||
|
||||
class AssignmentTaskTypeEnum(str, Enum):
|
||||
FILE_SUBMISSION = "FILE_SUBMISSION"
|
||||
QUIZ = "QUIZ"
|
||||
FORM = "FORM" # soon to be implemented
|
||||
OTHER = "OTHER"
|
||||
|
||||
|
||||
class AssignmentTaskBase(SQLModel):
|
||||
"""Represents the common fields for an assignment task."""
|
||||
|
||||
title: str
|
||||
description: str
|
||||
hint: str
|
||||
assignment_type: AssignmentTaskTypeEnum
|
||||
contents: Dict = Field(default={}, sa_column=Column(JSON))
|
||||
max_grade_value: int = 0 # Value is always between 0-100
|
||||
|
||||
assignment_id: int
|
||||
org_id: int
|
||||
course_id: int
|
||||
chapter_id: int
|
||||
activity_id: int
|
||||
|
||||
|
||||
class AssignmentTaskCreate(AssignmentTaskBase ):
|
||||
"""Model for creating a new assignment task."""
|
||||
|
||||
pass # Inherits all fields from AssignmentTaskBase
|
||||
|
||||
|
||||
class AssignmentTaskRead(AssignmentTaskBase):
|
||||
"""Model for reading an assignment task."""
|
||||
|
||||
id: int
|
||||
assignment_task_uuid: str
|
||||
|
||||
|
||||
class AssignmentTaskUpdate(SQLModel):
|
||||
"""Model for updating an assignment task."""
|
||||
|
||||
title: Optional[str]
|
||||
description: Optional[str]
|
||||
hint: Optional[str]
|
||||
assignment_type: Optional[AssignmentTaskTypeEnum]
|
||||
contents: Optional[Dict] = Field(default={}, sa_column=Column(JSON))
|
||||
max_grade_value: Optional[int]
|
||||
assignment_id: Optional[int]
|
||||
org_id: Optional[int]
|
||||
course_id: Optional[int]
|
||||
chapter_id: Optional[int]
|
||||
activity_id: Optional[int]
|
||||
|
||||
|
||||
class AssignmentTask(AssignmentTaskBase, table=True):
|
||||
"""Represents a task within an assignment with various attributes and foreign keys."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
|
||||
assignment_task_uuid: str
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
assignment_id: int = Field(
|
||||
sa_column=Column(
|
||||
"assignment_id", ForeignKey("assignment.id", ondelete="CASCADE")
|
||||
)
|
||||
)
|
||||
org_id: int = Field(
|
||||
sa_column=Column("org_id", ForeignKey("organization.id", ondelete="CASCADE"))
|
||||
)
|
||||
course_id: int = Field(
|
||||
sa_column=Column("course_id", ForeignKey("course.id", ondelete="CASCADE"))
|
||||
)
|
||||
chapter_id: int = Field(
|
||||
sa_column=Column("chapter_id", ForeignKey("chapter.id", ondelete="CASCADE"))
|
||||
)
|
||||
activity_id: int = Field(
|
||||
sa_column=Column("activity_id", ForeignKey("activity.id", ondelete="CASCADE"))
|
||||
)
|
||||
|
||||
|
||||
## AssignmentTask ##
|
||||
|
||||
|
||||
## AssignmentTaskSubmission ##
|
||||
|
||||
|
||||
class AssignmentTaskSubmissionBase(SQLModel):
|
||||
"""Represents the common fields for an assignment task submission."""
|
||||
|
||||
task_submission: Dict = Field(default={}, sa_column=Column(JSON))
|
||||
grade: int = 0 # Value is always between 0-100
|
||||
task_submission_grade_feedback: str
|
||||
assignment_type: AssignmentTaskTypeEnum
|
||||
|
||||
user_id: int
|
||||
activity_id: int
|
||||
course_id: int
|
||||
chapter_id: int
|
||||
assignment_task_id: int
|
||||
|
||||
|
||||
class AssignmentTaskSubmissionCreate(AssignmentTaskSubmissionBase):
|
||||
"""Model for creating a new assignment task submission."""
|
||||
|
||||
pass # Inherits all fields from AssignmentTaskSubmissionBase
|
||||
|
||||
|
||||
class AssignmentTaskSubmissionRead(AssignmentTaskSubmissionBase):
|
||||
"""Model for reading an assignment task submission."""
|
||||
|
||||
id: int
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
|
||||
class AssignmentTaskSubmissionUpdate(SQLModel):
|
||||
"""Model for updating an assignment task submission."""
|
||||
|
||||
assignment_task_submission_uuid: Optional[str]
|
||||
task_submission: Optional[Dict] = Field(default={}, sa_column=Column(JSON))
|
||||
grade: Optional[int]
|
||||
task_submission_grade_feedback: Optional[str]
|
||||
assignment_type: Optional[AssignmentTaskTypeEnum]
|
||||
user_id: Optional[int]
|
||||
activity_id: Optional[int]
|
||||
course_id: Optional[int]
|
||||
chapter_id: Optional[int]
|
||||
assignment_task_id: Optional[int]
|
||||
|
||||
|
||||
class AssignmentTaskSubmission(AssignmentTaskSubmissionBase, table=True):
|
||||
"""Represents a submission for a specific assignment task with grade and feedback."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
assignment_task_submission_uuid: str
|
||||
task_submission: Dict = Field(default={}, sa_column=Column(JSON))
|
||||
grade: int = 0 # Value is always between 0-100
|
||||
task_submission_grade_feedback: str
|
||||
assignment_type: AssignmentTaskTypeEnum
|
||||
|
||||
user_id: int = Field(
|
||||
sa_column=Column("user_id", ForeignKey("user.id", ondelete="CASCADE"))
|
||||
)
|
||||
activity_id: int = Field(
|
||||
sa_column=Column("activity_id", ForeignKey("activity.id", ondelete="CASCADE"))
|
||||
)
|
||||
course_id: int = Field(
|
||||
sa_column=Column("course_id", ForeignKey("course.id", ondelete="CASCADE"))
|
||||
)
|
||||
chapter_id: int = Field(
|
||||
sa_column=Column("chapter_id", ForeignKey("chapter.id", ondelete="CASCADE"))
|
||||
)
|
||||
assignment_task_id: int = Field(
|
||||
sa_column=Column(
|
||||
"assignment_task_id", ForeignKey("assignmenttask.id", ondelete="CASCADE")
|
||||
)
|
||||
)
|
||||
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
## AssignmentTaskSubmission ##
|
||||
|
||||
## AssignmentUserSubmission ##
|
||||
|
||||
class AssignmentUserSubmissionStatus(str, Enum):
|
||||
PENDING = "PENDING"
|
||||
SUBMITTED = "SUBMITTED"
|
||||
GRADED = "GRADED"
|
||||
LATE = "LATE"
|
||||
NOT_SUBMITTED = "NOT_SUBMITTED"
|
||||
|
||||
|
||||
class AssignmentUserSubmissionBase(SQLModel):
|
||||
"""Represents the submission status of an assignment for a user."""
|
||||
|
||||
|
||||
submission_status: AssignmentUserSubmissionStatus = (
|
||||
AssignmentUserSubmissionStatus.PENDING
|
||||
)
|
||||
grade: str
|
||||
user_id: int = Field(
|
||||
sa_column=Column("user_id", ForeignKey("user.id", ondelete="CASCADE"))
|
||||
)
|
||||
assignment_id: int = Field(
|
||||
sa_column=Column(
|
||||
"assignment_id", ForeignKey("assignment.id", ondelete="CASCADE")
|
||||
)
|
||||
)
|
||||
|
||||
class AssignmentUserSubmissionCreate(AssignmentUserSubmissionBase):
|
||||
"""Model for creating a new assignment user submission."""
|
||||
|
||||
pass # Inherits all fields from AssignmentUserSubmissionBase
|
||||
|
||||
class AssignmentUserSubmissionRead(AssignmentUserSubmissionBase):
|
||||
"""Model for reading an assignment user submission."""
|
||||
|
||||
id: int
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
class AssignmentUserSubmissionUpdate(SQLModel):
|
||||
"""Model for updating an assignment user submission."""
|
||||
|
||||
submission_status: Optional[AssignmentUserSubmissionStatus]
|
||||
grade: Optional[str]
|
||||
user_id: Optional[int]
|
||||
assignment_id: Optional[int]
|
||||
|
||||
class AssignmentUserSubmission(AssignmentUserSubmissionBase, table=True):
|
||||
"""Represents the submission status of an assignment for a user."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
creation_date: str
|
||||
update_date: str
|
||||
|
||||
submission_status: AssignmentUserSubmissionStatus = (
|
||||
AssignmentUserSubmissionStatus.PENDING
|
||||
)
|
||||
grade: str
|
||||
user_id: int = Field(
|
||||
sa_column=Column("user_id", ForeignKey("user.id", ondelete="CASCADE"))
|
||||
)
|
||||
assignment_id: int = Field(
|
||||
sa_column=Column(
|
||||
"assignment_id", ForeignKey("assignment.id", ondelete="CASCADE")
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ class BlockCreate(BlockBase):
|
|||
|
||||
|
||||
class BlockRead(BlockBase):
|
||||
id: int
|
||||
id: int = Field(default=None, primary_key=True)
|
||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||
course_id: int = Field(default=None, foreign_key="course.id")
|
||||
chapter_id: int = Field(default=None, foreign_key="chapter.id")
|
||||
|
|
@ -2,7 +2,7 @@ from typing import Any, List, Optional
|
|||
from pydantic import BaseModel
|
||||
from sqlalchemy import Column, ForeignKey
|
||||
from sqlmodel import Field, SQLModel
|
||||
from src.db.activities import ActivityRead
|
||||
from src.db.courses.activities import ActivityRead
|
||||
|
||||
|
||||
class ChapterBase(SQLModel):
|
||||
|
|
@ -33,10 +33,10 @@ class ChapterCreate(ChapterBase):
|
|||
|
||||
class ChapterUpdate(ChapterBase):
|
||||
name: Optional[str]
|
||||
description: Optional[str]
|
||||
thumbnail_image: Optional[str]
|
||||
description: Optional[str] = ""
|
||||
thumbnail_image: Optional[str] = ""
|
||||
course_id: Optional[int]
|
||||
org_id: Optional[int]
|
||||
org_id: Optional[int] # type: ignore
|
||||
|
||||
|
||||
class ChapterRead(ChapterBase):
|
||||
|
|
@ -3,7 +3,7 @@ from sqlalchemy import Column, ForeignKey, Integer
|
|||
from sqlmodel import Field, SQLModel
|
||||
from src.db.users import UserRead
|
||||
from src.db.trails import TrailRead
|
||||
from src.db.chapters import ChapterRead
|
||||
from src.db.courses.chapters import ChapterRead
|
||||
|
||||
|
||||
class CourseBase(SQLModel):
|
||||
|
|
@ -21,7 +21,7 @@ class Course(CourseBase, table=True):
|
|||
org_id: int = Field(
|
||||
sa_column=Column(Integer, ForeignKey("organization.id", ondelete="CASCADE"))
|
||||
)
|
||||
course_uuid: str = ""
|
||||
course_uuid: str = ""
|
||||
creation_date: str = ""
|
||||
update_date: str = ""
|
||||
|
||||
|
|
@ -6,8 +6,12 @@ from src.db.trail_runs import TrailRunRead
|
|||
|
||||
|
||||
class TrailBase(SQLModel):
|
||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||
user_id: int = Field(default=None, foreign_key="user.id")
|
||||
org_id: int = Field(
|
||||
sa_column=Column(Integer, ForeignKey("organization.id", ondelete="CASCADE"))
|
||||
)
|
||||
user_id: int = Field(
|
||||
sa_column=Column(Integer, ForeignKey("user.id", ondelete="CASCADE"))
|
||||
)
|
||||
|
||||
|
||||
class Trail(TrailBase, table=True):
|
||||
|
|
@ -20,6 +24,7 @@ class Trail(TrailBase, table=True):
|
|||
class TrailCreate(TrailBase):
|
||||
pass
|
||||
|
||||
|
||||
# TODO: This is a hacky way to get around the list[TrailRun] issue, find a better way to do this
|
||||
class TrailRead(BaseModel):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue