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
61
apps/api/poetry.lock
generated
61
apps/api/poetry.lock
generated
|
|
@ -215,17 +215,17 @@ typecheck = ["mypy"]
|
|||
|
||||
[[package]]
|
||||
name = "boto3"
|
||||
version = "1.34.137"
|
||||
version = "1.34.143"
|
||||
description = "The AWS SDK for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "boto3-1.34.137-py3-none-any.whl", hash = "sha256:7cb697d67fd138ceebc6f789919ae370c092a50c6b0ccc4ef483027935502eab"},
|
||||
{file = "boto3-1.34.137.tar.gz", hash = "sha256:0b21b84db4619b3711a6f643d465a5a25e81231ee43615c55a20ff6b89c6cc3c"},
|
||||
{file = "boto3-1.34.143-py3-none-any.whl", hash = "sha256:0d16832f23e6bd3ae94e35ea8e625529850bfad9baccd426de96ad8f445d8e03"},
|
||||
{file = "boto3-1.34.143.tar.gz", hash = "sha256:b590ce80c65149194def43ebf0ea1cf0533945502507837389a8d22e3ecbcf05"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
botocore = ">=1.34.137,<1.35.0"
|
||||
botocore = ">=1.34.143,<1.35.0"
|
||||
jmespath = ">=0.7.1,<2.0.0"
|
||||
s3transfer = ">=0.10.0,<0.11.0"
|
||||
|
||||
|
|
@ -234,13 +234,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
|||
|
||||
[[package]]
|
||||
name = "botocore"
|
||||
version = "1.34.137"
|
||||
version = "1.34.143"
|
||||
description = "Low-level, data-driven core of boto 3."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "botocore-1.34.137-py3-none-any.whl", hash = "sha256:a980fa4adec4bfa23fff70a3512622e9412c69c791898a52cafc2458b0be6040"},
|
||||
{file = "botocore-1.34.137.tar.gz", hash = "sha256:e29c8e9bfda0b20a1997792968e85868bfce42fefad9730f633a81adcff3f2ef"},
|
||||
{file = "botocore-1.34.143-py3-none-any.whl", hash = "sha256:094aea179e8aaa1bc957ad49cc27d93b189dd3a1f3075d8b0ca7c445a2a88430"},
|
||||
{file = "botocore-1.34.143.tar.gz", hash = "sha256:059f032ec05733a836e04e869c5a15534420102f93116f3bc9a5b759b0651caf"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -844,13 +844,13 @@ tqdm = ["tqdm"]
|
|||
|
||||
[[package]]
|
||||
name = "google-auth"
|
||||
version = "2.31.0"
|
||||
version = "2.32.0"
|
||||
description = "Google Authentication Library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "google-auth-2.31.0.tar.gz", hash = "sha256:87805c36970047247c8afe614d4e3af8eceafc1ebba0c679fe75ddd1d575e871"},
|
||||
{file = "google_auth-2.31.0-py2.py3-none-any.whl", hash = "sha256:042c4702efa9f7d3c48d3a69341c209381b125faa6dbf3ebe56bc7e40ae05c23"},
|
||||
{file = "google_auth-2.32.0-py2.py3-none-any.whl", hash = "sha256:53326ea2ebec768070a94bee4e1b9194c9646ea0c2bd72422785bd0f9abfad7b"},
|
||||
{file = "google_auth-2.32.0.tar.gz", hash = "sha256:49315be72c55a6a37d62819e3573f6b416aca00721f7e3e31a008d928bf64022"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1877,13 +1877,13 @@ sympy = "*"
|
|||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.35.8"
|
||||
version = "1.35.13"
|
||||
description = "The official Python library for the openai API"
|
||||
optional = false
|
||||
python-versions = ">=3.7.1"
|
||||
files = [
|
||||
{file = "openai-1.35.8-py3-none-any.whl", hash = "sha256:69d5b0f47f0c806d5da83fb0f84c147661395226d7f79acc78aa1d9b8c635887"},
|
||||
{file = "openai-1.35.8.tar.gz", hash = "sha256:3f6101888bb516647edade74c503f2b937b8bab73408e799d58f2aba68bbe51c"},
|
||||
{file = "openai-1.35.13-py3-none-any.whl", hash = "sha256:36ec3e93e0d1f243f69be85c89b9221a471c3e450dfd9df16c9829e3cdf63e60"},
|
||||
{file = "openai-1.35.13.tar.gz", hash = "sha256:c684f3945608baf7d2dcc0ef3ee6f3e27e4c66f21076df0b47be45d57e6ae6e4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2732,13 +2732,13 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "resend"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
description = "Resend Python SDK"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "resend-2.1.0-py2.py3-none-any.whl", hash = "sha256:7f2a221983fab74a09f669c0c14a75daf547ffa4b4930141626d9cca55bca767"},
|
||||
{file = "resend-2.1.0.tar.gz", hash = "sha256:92dc8e035c2ce8cf8210c1c322e86b0a4f509e0c82a80932d3323cd2f3a43d2d"},
|
||||
{file = "resend-2.2.0-py2.py3-none-any.whl", hash = "sha256:be420762885de25c816497f09207da1cd54d253c44d9f81f441367893a42d099"},
|
||||
{file = "resend-2.2.0.tar.gz", hash = "sha256:f44976e4a37bb66445280bd8ef201eaac536b07bbe7c4da8f1717f4fcc63da7e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2796,13 +2796,13 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.7.1"
|
||||
version = "2.9.0"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "sentry_sdk-2.7.1-py2.py3-none-any.whl", hash = "sha256:ef1b3d54eb715825657cd4bb3cb42bb4dc85087bac14c56b0fd8c21abd968c9a"},
|
||||
{file = "sentry_sdk-2.7.1.tar.gz", hash = "sha256:25006c7e68b75aaa5e6b9c6a420ece22e8d7daec4b7a906ffd3a8607b67c037b"},
|
||||
{file = "sentry_sdk-2.9.0-py2.py3-none-any.whl", hash = "sha256:0bea5fa8b564cc0d09f2e6f55893e8f70286048b0ffb3a341d5b695d1af0e6ee"},
|
||||
{file = "sentry_sdk-2.9.0.tar.gz", hash = "sha256:4c85bad74df9767976afb3eeddc33e0e153300e887d637775a753a35ef99bee6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2847,13 +2847,13 @@ tornado = ["tornado (>=6)"]
|
|||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "70.2.0"
|
||||
version = "70.3.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"},
|
||||
{file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"},
|
||||
{file = "setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc"},
|
||||
{file = "setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
|
@ -3014,27 +3014,30 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7
|
|||
|
||||
[[package]]
|
||||
name = "sympy"
|
||||
version = "1.12.1"
|
||||
version = "1.13.0"
|
||||
description = "Computer algebra system (CAS) in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "sympy-1.12.1-py3-none-any.whl", hash = "sha256:9b2cbc7f1a640289430e13d2a56f02f867a1da0190f2f99d8968c2f74da0e515"},
|
||||
{file = "sympy-1.12.1.tar.gz", hash = "sha256:2877b03f998cd8c08f07cd0de5b767119cd3ef40d09f41c30d722f6686b0fb88"},
|
||||
{file = "sympy-1.13.0-py3-none-any.whl", hash = "sha256:6b0b32a4673fb91bd3cac3b55406c8e01d53ae22780be467301cc452f6680c92"},
|
||||
{file = "sympy-1.13.0.tar.gz", hash = "sha256:3b6af8f4d008b9a1a6a4268b335b984b23835f26d1d60b0526ebc71d48a25f57"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mpmath = ">=1.1.0,<1.4.0"
|
||||
mpmath = ">=1.1.0,<1.4"
|
||||
|
||||
[package.extras]
|
||||
dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "tenacity"
|
||||
version = "8.4.2"
|
||||
version = "8.5.0"
|
||||
description = "Retry code until it succeeds"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tenacity-8.4.2-py3-none-any.whl", hash = "sha256:9e6f7cf7da729125c7437222f8a522279751cdfbe6b67bfe64f75d3a348661b2"},
|
||||
{file = "tenacity-8.4.2.tar.gz", hash = "sha256:cd80a53a79336edba8489e767f729e4f391c896956b57140b5d7511a64bbd3ef"},
|
||||
{file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"},
|
||||
{file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from fastapi import APIRouter, Depends
|
||||
from src.routers import usergroups
|
||||
from src.routers import blocks, dev, trail, users, auth, orgs, roles
|
||||
from src.routers import dev, trail, users, auth, orgs, roles
|
||||
from src.routers.ai import ai
|
||||
from src.routers.courses import chapters, collections, courses, activities
|
||||
from src.routers.courses import chapters, collections, courses, assignments
|
||||
from src.routers.courses.activities import activities, blocks
|
||||
from src.routers.install import install
|
||||
from src.services.dev.dev import isDevModeEnabledOrRaise
|
||||
from src.services.install.install import isInstallModeEnabled
|
||||
|
|
@ -19,6 +20,7 @@ v1_router.include_router(orgs.router, prefix="/orgs", tags=["orgs"])
|
|||
v1_router.include_router(roles.router, prefix="/roles", tags=["roles"])
|
||||
v1_router.include_router(blocks.router, prefix="/blocks", tags=["blocks"])
|
||||
v1_router.include_router(courses.router, prefix="/courses", tags=["courses"])
|
||||
v1_router.include_router(assignments.router, prefix="/assignments", tags=["assignments"])
|
||||
v1_router.include_router(chapters.router, prefix="/chapters", tags=["chapters"])
|
||||
v1_router.include_router(activities.router, prefix="/activities", tags=["activities"])
|
||||
v1_router.include_router(collections.router, prefix="/collections", tags=["collections"])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import List
|
||||
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
||||
from src.db.activities import ActivityCreate, ActivityRead, ActivityUpdate
|
||||
from src.db.courses.activities import ActivityCreate, ActivityRead, ActivityUpdate
|
||||
from src.db.users import PublicUser
|
||||
from src.core.events.database import get_db_session
|
||||
from src.services.courses.activities.activities import (
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
||||
from src.db.blocks import BlockRead
|
||||
from src.db.courses.blocks import BlockRead
|
||||
from src.core.events.database import get_db_session
|
||||
from src.security.auth import get_current_user
|
||||
from src.services.blocks.block_types.imageBlock.imageBlock import (
|
||||
310
apps/api/src/routers/courses/assignments.py
Normal file
310
apps/api/src/routers/courses/assignments.py
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
from fastapi import APIRouter, Depends, Request
|
||||
from src.db.courses.assignments import (
|
||||
AssignmentCreate,
|
||||
AssignmentRead,
|
||||
AssignmentTaskCreate,
|
||||
AssignmentTaskSubmissionCreate,
|
||||
AssignmentTaskUpdate,
|
||||
AssignmentUpdate,
|
||||
AssignmentUserSubmissionCreate,
|
||||
)
|
||||
from src.db.users import PublicUser
|
||||
from src.core.events.database import get_db_session
|
||||
from src.security.auth import get_current_user
|
||||
from src.services.courses.activities.assignments import (
|
||||
create_assignment,
|
||||
create_assignment_submission,
|
||||
create_assignment_task,
|
||||
create_assignment_task_submission,
|
||||
delete_assignment,
|
||||
delete_assignment_submission,
|
||||
delete_assignment_task,
|
||||
delete_assignment_task_submission,
|
||||
read_assignment,
|
||||
read_assignment_submissions,
|
||||
read_assignment_task_submissions,
|
||||
read_assignment_tasks,
|
||||
read_user_assignment_submissions,
|
||||
read_user_assignment_task_submissions,
|
||||
update_assignment,
|
||||
update_assignment_submission,
|
||||
update_assignment_task,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
## ASSIGNMENTS ##
|
||||
|
||||
|
||||
@router.post("/")
|
||||
async def api_create_assignments(
|
||||
request: Request,
|
||||
assignment_object: AssignmentCreate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> AssignmentRead:
|
||||
"""
|
||||
Create new activity
|
||||
"""
|
||||
return await create_assignment(request, assignment_object, current_user, db_session)
|
||||
|
||||
|
||||
@router.get("/{assignment_uuid}")
|
||||
async def api_read_assignment(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> AssignmentRead:
|
||||
"""
|
||||
Read an assignment
|
||||
"""
|
||||
return await read_assignment(request, assignment_uuid, current_user, db_session)
|
||||
|
||||
|
||||
@router.put("/{assignment_uuid}")
|
||||
async def api_update_assignment(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
assignment_object: AssignmentUpdate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
) -> AssignmentRead:
|
||||
"""
|
||||
Update an assignment
|
||||
"""
|
||||
return await update_assignment(
|
||||
request, assignment_uuid, assignment_object, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{assignment_uuid}")
|
||||
async def api_delete_assignment(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Delete an assignment
|
||||
"""
|
||||
return await delete_assignment(request, assignment_uuid, current_user, db_session)
|
||||
|
||||
|
||||
## ASSIGNMENTS Tasks ##
|
||||
|
||||
|
||||
@router.post("/{assignment_uuid}/tasks")
|
||||
async def api_create_assignment_tasks(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
assignment_task_object: AssignmentTaskCreate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Create new tasks for an assignment
|
||||
"""
|
||||
return await create_assignment_task(
|
||||
request, assignment_uuid, assignment_task_object, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{assignment_uuid}/tasks")
|
||||
async def api_read_assignment_tasks(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Read tasks for an assignment
|
||||
"""
|
||||
return await read_assignment_tasks(
|
||||
request, assignment_uuid, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.put("/{assignment_uuid}/tasks/{task_uuid}")
|
||||
async def api_update_assignment_tasks(
|
||||
request: Request,
|
||||
assignment_task_uuid: str,
|
||||
assignment_task_object: AssignmentTaskUpdate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Update tasks for an assignment
|
||||
"""
|
||||
return await update_assignment_task(
|
||||
request, assignment_task_uuid, assignment_task_object, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{assignment_uuid}/tasks/{task_uuid}")
|
||||
async def api_delete_assignment_tasks(
|
||||
request: Request,
|
||||
assignment_task_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Delete tasks for an assignment
|
||||
"""
|
||||
return await delete_assignment_task(
|
||||
request, assignment_task_uuid, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
## ASSIGNMENTS Tasks Submissions ##
|
||||
|
||||
|
||||
@router.post("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions")
|
||||
async def api_create_assignment_task_submissions(
|
||||
request: Request,
|
||||
assignment_task_submission_object: AssignmentTaskSubmissionCreate,
|
||||
assignment_task_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Create new task submissions for an assignment
|
||||
"""
|
||||
return await create_assignment_task_submission(
|
||||
request,
|
||||
assignment_task_uuid,
|
||||
assignment_task_submission_object,
|
||||
current_user,
|
||||
db_session,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions/{user_id}")
|
||||
async def api_read_user_assignment_task_submissions(
|
||||
request: Request,
|
||||
assignment_task_uuid: str,
|
||||
user_id: int,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Read task submissions for an assignment from a user
|
||||
"""
|
||||
return await read_user_assignment_task_submissions(
|
||||
request, assignment_task_uuid, user_id, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions")
|
||||
async def api_read_assignment_task_submissions(
|
||||
request: Request,
|
||||
assignment_task_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Read task submissions for an assignment from a user
|
||||
"""
|
||||
return await read_assignment_task_submissions(
|
||||
request, assignment_task_uuid, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{assignment_uuid}/tasks/{assignment_task_uuid}/submissions/{assignment_task_submission_uuid}"
|
||||
)
|
||||
async def api_delete_assignment_task_submissions(
|
||||
request: Request,
|
||||
assignment_task_submission_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Delete task submissions for an assignment from a user
|
||||
"""
|
||||
return await delete_assignment_task_submission(
|
||||
request, assignment_task_submission_uuid, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
## ASSIGNMENTS Submissions ##
|
||||
|
||||
|
||||
@router.post("/{assignment_uuid}/submissions")
|
||||
async def api_create_assignment_submissions(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
assignment_submission: AssignmentUserSubmissionCreate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Create new submissions for an assignment
|
||||
"""
|
||||
return await create_assignment_submission(
|
||||
request, assignment_uuid, assignment_submission, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{assignment_uuid}/submissions")
|
||||
async def api_read_assignment_submissions(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Read submissions for an assignment
|
||||
"""
|
||||
return await read_assignment_submissions(
|
||||
request, assignment_uuid, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{assignment_uuid}/submissions/{user_id}")
|
||||
async def api_read_user_assignment_submissions(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
user_id: int,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Read submissions for an assignment from a user
|
||||
"""
|
||||
return await read_user_assignment_submissions(
|
||||
request, assignment_uuid, user_id, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.put("/{assignment_uuid}/submissions/{user_id}")
|
||||
async def api_update_user_assignment_submissions(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
user_id: str,
|
||||
assignment_submission: AssignmentUserSubmissionCreate,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Update submissions for an assignment from a user
|
||||
"""
|
||||
return await update_assignment_submission(
|
||||
request, user_id, assignment_submission, current_user, db_session
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{assignment_uuid}/submissions/{user_id}")
|
||||
async def api_delete_user_assignment_submissions(
|
||||
request: Request,
|
||||
assignment_id: str,
|
||||
user_id: str,
|
||||
current_user: PublicUser = Depends(get_current_user),
|
||||
db_session=Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
Delete submissions for an assignment from a user
|
||||
"""
|
||||
return await delete_assignment_submission(
|
||||
request, assignment_id, user_id, current_user, db_session
|
||||
)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import List
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from src.core.events.database import get_db_session
|
||||
from src.db.chapters import (
|
||||
from src.db.courses.chapters import (
|
||||
ChapterCreate,
|
||||
ChapterRead,
|
||||
ChapterUpdate,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ from typing import List
|
|||
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
||||
from sqlmodel import Session
|
||||
from src.core.events.database import get_db_session
|
||||
from src.db.course_updates import (
|
||||
from src.db.courses.course_updates import (
|
||||
CourseUpdateCreate,
|
||||
CourseUpdateRead,
|
||||
CourseUpdateUpdate,
|
||||
)
|
||||
from src.db.users import PublicUser
|
||||
from src.db.courses import (
|
||||
from src.db.courses.courses import (
|
||||
CourseCreate,
|
||||
CourseRead,
|
||||
CourseUpdate,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from fastapi import HTTPException, status, Request
|
|||
from sqlalchemy import null
|
||||
from sqlmodel import Session, select
|
||||
from src.db.collections import Collection
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.courses import Course
|
||||
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum
|
||||
from src.db.roles import Role
|
||||
from src.db.user_organizations import UserOrganization
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ from sqlmodel import Session, select
|
|||
from src.db.organization_config import OrganizationConfig
|
||||
from src.db.organizations import Organization
|
||||
from src.services.ai.utils import check_limits_and_config, count_ai_ask
|
||||
from src.db.courses import Course, CourseRead
|
||||
from src.db.courses.courses import Course, CourseRead
|
||||
from src.core.events.database import get_db_session
|
||||
from src.db.users import PublicUser
|
||||
from src.db.activities import Activity, ActivityRead
|
||||
from src.db.courses.activities import Activity, ActivityRead
|
||||
from src.security.auth import get_current_user
|
||||
from src.services.ai.base import ask_ai, get_chat_session_history
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ from uuid import uuid4
|
|||
from src.db.organizations import Organization
|
||||
from fastapi import HTTPException, status, UploadFile, Request
|
||||
from sqlmodel import Session, select
|
||||
from src.db.activities import Activity
|
||||
from src.db.blocks import Block, BlockRead, BlockTypeEnum
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.activities import Activity
|
||||
from src.db.courses.blocks import Block, BlockRead, BlockTypeEnum
|
||||
from src.db.courses.courses import Course
|
||||
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ from uuid import uuid4
|
|||
from src.db.organizations import Organization
|
||||
from fastapi import HTTPException, status, UploadFile, Request
|
||||
from sqlmodel import Session, select
|
||||
from src.db.activities import Activity
|
||||
from src.db.blocks import Block, BlockRead, BlockTypeEnum
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.activities import Activity
|
||||
from src.db.courses.blocks import Block, BlockRead, BlockTypeEnum
|
||||
from src.db.courses.courses import Course
|
||||
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
||||
|
||||
from src.services.users.users import PublicUser
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ from uuid import uuid4
|
|||
from src.db.organizations import Organization
|
||||
from fastapi import HTTPException, status, UploadFile, Request
|
||||
from sqlmodel import Session, select
|
||||
from src.db.activities import Activity
|
||||
from src.db.blocks import Block, BlockRead, BlockTypeEnum
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.activities import Activity
|
||||
from src.db.courses.blocks import Block, BlockRead, BlockTypeEnum
|
||||
from src.db.courses.courses import Course
|
||||
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
||||
|
||||
from src.services.users.users import PublicUser
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
from typing import Literal
|
||||
from sqlmodel import Session, select
|
||||
from src.db.courses import Course
|
||||
from src.db.chapters import Chapter
|
||||
from src.db.courses.courses import Course
|
||||
from src.db.courses.chapters import Chapter
|
||||
from src.security.rbac.rbac import (
|
||||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||
authorization_verify_if_element_is_public,
|
||||
authorization_verify_if_user_is_anon,
|
||||
)
|
||||
from src.db.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
|
||||
from src.db.chapter_activities import ChapterActivity
|
||||
from src.db.courses.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
|
||||
from src.db.courses.chapter_activities import ChapterActivity
|
||||
from src.db.users import AnonymousUser, PublicUser
|
||||
from fastapi import HTTPException, Request
|
||||
from uuid import uuid4
|
||||
|
|
@ -58,7 +58,7 @@ async def create_activity(
|
|||
statement = (
|
||||
select(ChapterActivity)
|
||||
.where(ChapterActivity.chapter_id == activity_object.chapter_id)
|
||||
.order_by(ChapterActivity.order)
|
||||
.order_by(ChapterActivity.order) # type: ignore
|
||||
)
|
||||
chapter_activities = db_session.exec(statement).all()
|
||||
|
||||
|
|
|
|||
997
apps/api/src/services/courses/activities/assignments.py
Normal file
997
apps/api/src/services/courses/activities/assignments.py
Normal file
|
|
@ -0,0 +1,997 @@
|
|||
####################################################
|
||||
# CRUD
|
||||
####################################################
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
from uuid import uuid4
|
||||
from fastapi import HTTPException, Request
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from src.db.courses.assignments import (
|
||||
Assignment,
|
||||
AssignmentCreate,
|
||||
AssignmentRead,
|
||||
AssignmentTask,
|
||||
AssignmentTaskCreate,
|
||||
AssignmentTaskRead,
|
||||
AssignmentTaskSubmission,
|
||||
AssignmentTaskSubmissionCreate,
|
||||
AssignmentTaskSubmissionRead,
|
||||
AssignmentTaskUpdate,
|
||||
AssignmentUpdate,
|
||||
AssignmentUserSubmission,
|
||||
AssignmentUserSubmissionCreate,
|
||||
AssignmentUserSubmissionRead,
|
||||
)
|
||||
from src.db.courses.courses import Course
|
||||
from src.db.users import AnonymousUser, PublicUser
|
||||
from src.security.rbac.rbac import (
|
||||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||
authorization_verify_if_element_is_public,
|
||||
authorization_verify_if_user_is_anon,
|
||||
)
|
||||
|
||||
## > Assignments CRUD
|
||||
|
||||
|
||||
async def create_assignment(
|
||||
request: Request,
|
||||
assignment_object: AssignmentCreate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if org exists
|
||||
statement = select(Course).where(Course.id == assignment_object.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "create", db_session)
|
||||
|
||||
# Create Assignment
|
||||
assignment = Assignment(**assignment_object.model_dump())
|
||||
|
||||
assignment.assignment_uuid = str(f"assignment_{uuid4()}")
|
||||
assignment.creation_date = str(datetime.now())
|
||||
assignment.update_date = str(datetime.now())
|
||||
assignment.org_id = course.org_id
|
||||
|
||||
# Insert Assignment in DB
|
||||
db_session.add(assignment)
|
||||
db_session.commit()
|
||||
db_session.refresh(assignment)
|
||||
|
||||
# return assignment read
|
||||
return AssignmentRead.model_validate(assignment)
|
||||
|
||||
|
||||
async def read_assignment(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||
|
||||
# return assignment read
|
||||
return AssignmentRead.model_validate(assignment)
|
||||
|
||||
|
||||
async def update_assignment(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
assignment_object: AssignmentUpdate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
||||
|
||||
# Update only the fields that were passed in
|
||||
for var, value in vars(assignment_object).items():
|
||||
if value is not None:
|
||||
setattr(assignment, var, value)
|
||||
assignment.update_date = str(datetime.now())
|
||||
|
||||
# Insert Assignment in DB
|
||||
db_session.add(assignment)
|
||||
db_session.commit()
|
||||
db_session.refresh(assignment)
|
||||
|
||||
# return assignment read
|
||||
return AssignmentRead.model_validate(assignment)
|
||||
|
||||
|
||||
async def delete_assignment(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
|
||||
|
||||
# Delete Assignment
|
||||
db_session.delete(assignment)
|
||||
db_session.commit()
|
||||
|
||||
return {"message": "Assignment deleted"}
|
||||
|
||||
|
||||
## > Assignments Tasks CRUD
|
||||
|
||||
|
||||
async def create_assignment_task(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
assignment_task_object: AssignmentTaskCreate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "create", db_session)
|
||||
|
||||
# Create Assignment Task
|
||||
assignment_task = AssignmentTask(**assignment_task_object.model_dump())
|
||||
|
||||
assignment_task.assignment_task_uuid = str(f"assignmenttask_{uuid4()}")
|
||||
assignment_task.creation_date = str(datetime.now())
|
||||
assignment_task.update_date = str(datetime.now())
|
||||
assignment_task.org_id = course.org_id
|
||||
|
||||
# Insert Assignment Task in DB
|
||||
db_session.add(assignment_task)
|
||||
db_session.commit()
|
||||
db_session.refresh(assignment_task)
|
||||
|
||||
# return assignment task read
|
||||
return AssignmentTaskRead.model_validate(assignment_task)
|
||||
|
||||
|
||||
async def read_assignment_tasks(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Find assignment
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# Find assignments tasks for an assignment
|
||||
statement = select(AssignmentTask).where(
|
||||
assignment.assignment_uuid == assignment_uuid
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||
|
||||
# return assignment tasks read
|
||||
return [
|
||||
AssignmentTaskRead.model_validate(assignment_task)
|
||||
for assignment_task in db_session.exec(statement).all()
|
||||
]
|
||||
|
||||
|
||||
async def update_assignment_task(
|
||||
request: Request,
|
||||
assignment_task_uuid: str,
|
||||
assignment_task_object: AssignmentTaskUpdate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment task exists
|
||||
statement = select(AssignmentTask).where(
|
||||
AssignmentTask.assignment_task_uuid == assignment_task_uuid
|
||||
)
|
||||
assignment_task = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
||||
|
||||
# Update only the fields that were passed in
|
||||
for var, value in vars(assignment_task_object).items():
|
||||
if value is not None:
|
||||
setattr(assignment_task, var, value)
|
||||
assignment_task.update_date = str(datetime.now())
|
||||
|
||||
# Insert Assignment Task in DB
|
||||
db_session.add(assignment_task)
|
||||
db_session.commit()
|
||||
db_session.refresh(assignment_task)
|
||||
|
||||
# return assignment task read
|
||||
return AssignmentTaskRead.model_validate(assignment_task)
|
||||
|
||||
|
||||
async def delete_assignment_task(
|
||||
request: Request,
|
||||
assignment_task_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment task exists
|
||||
statement = select(AssignmentTask).where(
|
||||
AssignmentTask.assignment_task_uuid == assignment_task_uuid
|
||||
)
|
||||
assignment_task = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
|
||||
|
||||
# Delete Assignment Task
|
||||
db_session.delete(assignment_task)
|
||||
db_session.commit()
|
||||
|
||||
return {"message": "Assignment Task deleted"}
|
||||
|
||||
|
||||
## > Assignments Tasks Submissions CRUD
|
||||
|
||||
|
||||
async def create_assignment_task_submission(
|
||||
request: Request,
|
||||
assignment_task_uuid: str,
|
||||
assignment_task_submission_object: AssignmentTaskSubmissionCreate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment task exists
|
||||
statement = select(AssignmentTask).where(
|
||||
AssignmentTask.assignment_task_uuid == assignment_task_uuid
|
||||
)
|
||||
assignment_task = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "create", db_session)
|
||||
|
||||
# Create Assignment Task Submission
|
||||
assignment_task_submission = AssignmentTaskSubmission(
|
||||
**assignment_task_submission_object.model_dump()
|
||||
)
|
||||
|
||||
assignment_task_submission.assignment_task_submission_uuid = str(
|
||||
f"assignmenttasksubmission_{uuid4()}"
|
||||
)
|
||||
assignment_task_submission.creation_date = str(datetime.now())
|
||||
assignment_task_submission.update_date = str(datetime.now())
|
||||
assignment_task_submission.org_id = course.org_id
|
||||
|
||||
# Insert Assignment Task Submission in DB
|
||||
db_session.add(assignment_task_submission)
|
||||
db_session.commit()
|
||||
db_session.refresh(assignment_task_submission)
|
||||
|
||||
# return assignment task submission read
|
||||
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
|
||||
|
||||
|
||||
async def read_user_assignment_task_submissions(
|
||||
request: Request,
|
||||
assignment_task_submission_uuid: str,
|
||||
user_id: int,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment task submission exists
|
||||
statement = select(AssignmentTaskSubmission).where(
|
||||
AssignmentTaskSubmission.assignment_task_submission_uuid
|
||||
== assignment_task_submission_uuid,
|
||||
AssignmentTaskSubmission.user_id == user_id,
|
||||
)
|
||||
assignment_task_submission = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task_submission:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task Submission not found",
|
||||
)
|
||||
|
||||
# Check if assignment task exists
|
||||
statement = select(AssignmentTask).where(
|
||||
AssignmentTask.id == assignment_task_submission.assignment_task_id
|
||||
)
|
||||
assignment_task = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||
|
||||
# return assignment task submission read
|
||||
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
|
||||
|
||||
|
||||
async def read_assignment_task_submissions(
|
||||
request: Request,
|
||||
assignment_task_submission_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment task submission exists
|
||||
statement = select(AssignmentTaskSubmission).where(
|
||||
AssignmentTaskSubmission.assignment_task_submission_uuid
|
||||
== assignment_task_submission_uuid,
|
||||
)
|
||||
assignment_task_submission = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task_submission:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task Submission not found",
|
||||
)
|
||||
|
||||
# Check if assignment task exists
|
||||
statement = select(AssignmentTask).where(
|
||||
AssignmentTask.id == assignment_task_submission.assignment_task_id
|
||||
)
|
||||
assignment_task = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||
|
||||
# return assignment task submission read
|
||||
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
|
||||
|
||||
|
||||
async def update_assignment_task_submission(
|
||||
request: Request,
|
||||
assignment_task_submission_uuid: str,
|
||||
assignment_task_submission_object: AssignmentTaskSubmissionCreate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment task submission exists
|
||||
statement = select(AssignmentTaskSubmission).where(
|
||||
AssignmentTaskSubmission.assignment_task_submission_uuid
|
||||
== assignment_task_submission_uuid
|
||||
)
|
||||
assignment_task_submission = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task_submission:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task Submission not found",
|
||||
)
|
||||
|
||||
# Check if assignment task exists
|
||||
statement = select(AssignmentTask).where(
|
||||
AssignmentTask.id == assignment_task_submission.assignment_task_id
|
||||
)
|
||||
assignment_task = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
||||
|
||||
# Update only the fields that were passed in
|
||||
for var, value in vars(assignment_task_submission_object).items():
|
||||
if value is not None:
|
||||
setattr(assignment_task_submission, var, value)
|
||||
assignment_task_submission.update_date = str(datetime.now())
|
||||
|
||||
# Insert Assignment Task Submission in DB
|
||||
db_session.add(assignment_task_submission)
|
||||
db_session.commit()
|
||||
db_session.refresh(assignment_task_submission)
|
||||
|
||||
# return assignment task submission read
|
||||
return AssignmentTaskSubmissionRead.model_validate(assignment_task_submission)
|
||||
|
||||
|
||||
async def delete_assignment_task_submission(
|
||||
request: Request,
|
||||
assignment_task_submission_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment task submission exists
|
||||
statement = select(AssignmentTaskSubmission).where(
|
||||
AssignmentTaskSubmission.assignment_task_submission_uuid
|
||||
== assignment_task_submission_uuid
|
||||
)
|
||||
assignment_task_submission = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task_submission:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task Submission not found",
|
||||
)
|
||||
|
||||
# Check if assignment task exists
|
||||
statement = select(AssignmentTask).where(
|
||||
AssignmentTask.id == assignment_task_submission.assignment_task_id
|
||||
)
|
||||
assignment_task = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_task:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment Task not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.id == assignment_task.assignment_id)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
|
||||
|
||||
# Delete Assignment Task Submission
|
||||
db_session.delete(assignment_task_submission)
|
||||
db_session.commit()
|
||||
|
||||
return {"message": "Assignment Task Submission deleted"}
|
||||
|
||||
|
||||
## > Assignments Submissions CRUD
|
||||
|
||||
|
||||
async def create_assignment_submission(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
assignment_user_submission_object: AssignmentUserSubmissionCreate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if the submission has already been made
|
||||
statement = select(AssignmentUserSubmission).where(
|
||||
AssignmentUserSubmission.assignment_id == assignment.id,
|
||||
AssignmentUserSubmission.user_id == assignment_user_submission_object.user_id,
|
||||
)
|
||||
|
||||
assignment_user_submission = db_session.exec(statement).first()
|
||||
|
||||
if assignment_user_submission:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Assignment User Submission already exists",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "create", db_session)
|
||||
|
||||
# Create Assignment User Submission
|
||||
assignment_user_submission = AssignmentUserSubmission(
|
||||
**assignment_user_submission_object.model_dump()
|
||||
)
|
||||
|
||||
assignment_user_submission.assignment_user_submission_uuid = str(
|
||||
f"assignmentusersubmission_{uuid4()}"
|
||||
)
|
||||
assignment_user_submission.creation_date = str(datetime.now())
|
||||
assignment_user_submission.update_date = str(datetime.now())
|
||||
assignment_user_submission.org_id = course.org_id
|
||||
|
||||
# Insert Assignment User Submission in DB
|
||||
db_session.add(assignment_user_submission)
|
||||
db_session.commit()
|
||||
|
||||
# return assignment user submission read
|
||||
return AssignmentUserSubmissionRead.model_validate(assignment_user_submission)
|
||||
|
||||
|
||||
async def read_assignment_submissions(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Find assignment
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# Find assignments tasks for an assignment
|
||||
statement = select(AssignmentUserSubmission).where(
|
||||
assignment.assignment_uuid == assignment_uuid
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||
|
||||
# return assignment tasks read
|
||||
return [
|
||||
AssignmentUserSubmissionRead.model_validate(assignment_user_submission)
|
||||
for assignment_user_submission in db_session.exec(statement).all()
|
||||
]
|
||||
|
||||
|
||||
async def read_user_assignment_submissions(
|
||||
request: Request,
|
||||
assignment_uuid: str,
|
||||
user_id: int,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Find assignment
|
||||
statement = select(Assignment).where(Assignment.assignment_uuid == assignment_uuid)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# Find assignments tasks for an assignment
|
||||
statement = select(AssignmentUserSubmission).where(
|
||||
assignment.assignment_uuid == assignment_uuid,
|
||||
AssignmentUserSubmission.user_id == user_id,
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
|
||||
|
||||
# return assignment tasks read
|
||||
return [
|
||||
AssignmentUserSubmissionRead.model_validate(assignment_user_submission)
|
||||
for assignment_user_submission in db_session.exec(statement).all()
|
||||
]
|
||||
|
||||
|
||||
async def update_assignment_submission(
|
||||
request: Request,
|
||||
user_id: str,
|
||||
assignment_user_submission_object: AssignmentUserSubmissionCreate,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment user submission exists
|
||||
statement = select(AssignmentUserSubmission).where(
|
||||
AssignmentUserSubmission.user_id == user_id
|
||||
)
|
||||
assignment_user_submission = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_user_submission:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment User Submission not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(
|
||||
Assignment.id == assignment_user_submission.assignment_id
|
||||
)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "update", db_session)
|
||||
|
||||
# Update only the fields that were passed in
|
||||
for var, value in vars(assignment_user_submission_object).items():
|
||||
if value is not None:
|
||||
setattr(assignment_user_submission, var, value)
|
||||
assignment_user_submission.update_date = str(datetime.now())
|
||||
|
||||
# Insert Assignment User Submission in DB
|
||||
db_session.add(assignment_user_submission)
|
||||
db_session.commit()
|
||||
db_session.refresh(assignment_user_submission)
|
||||
|
||||
# return assignment user submission read
|
||||
return AssignmentUserSubmissionRead.model_validate(assignment_user_submission)
|
||||
|
||||
|
||||
async def delete_assignment_submission(
|
||||
request: Request,
|
||||
user_id: str,
|
||||
assignment_id: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
db_session: Session,
|
||||
):
|
||||
# Check if assignment user submission exists
|
||||
statement = select(AssignmentUserSubmission).where(
|
||||
AssignmentUserSubmission.user_id == user_id,
|
||||
AssignmentUserSubmission.assignment_id == assignment_id,
|
||||
)
|
||||
assignment_user_submission = db_session.exec(statement).first()
|
||||
|
||||
if not assignment_user_submission:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment User Submission not found",
|
||||
)
|
||||
|
||||
# Check if assignment exists
|
||||
statement = select(Assignment).where(
|
||||
Assignment.id == assignment_user_submission.assignment_id
|
||||
)
|
||||
assignment = db_session.exec(statement).first()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Assignment not found",
|
||||
)
|
||||
|
||||
# Check if course exists
|
||||
statement = select(Course).where(Course.id == assignment.course_id)
|
||||
course = db_session.exec(statement).first()
|
||||
|
||||
if not course:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Course not found",
|
||||
)
|
||||
|
||||
# RBAC check
|
||||
await rbac_check(request, course.course_uuid, current_user, "delete", db_session)
|
||||
|
||||
# Delete Assignment User Submission
|
||||
db_session.delete(assignment_user_submission)
|
||||
db_session.commit()
|
||||
|
||||
return {"message": "Assignment User Submission deleted"}
|
||||
|
||||
|
||||
## 🔒 RBAC Utils ##
|
||||
|
||||
|
||||
async def rbac_check(
|
||||
request: Request,
|
||||
course_uuid: str,
|
||||
current_user: PublicUser | AnonymousUser,
|
||||
action: Literal["create", "read", "update", "delete"],
|
||||
db_session: Session,
|
||||
):
|
||||
|
||||
if action == "read":
|
||||
if current_user.id == 0: # Anonymous user
|
||||
res = await authorization_verify_if_element_is_public(
|
||||
request, course_uuid, action, db_session
|
||||
)
|
||||
return res
|
||||
else:
|
||||
res = (
|
||||
await authorization_verify_based_on_roles_and_authorship_and_usergroups(
|
||||
request, current_user.id, action, course_uuid, db_session
|
||||
)
|
||||
)
|
||||
return res
|
||||
else:
|
||||
await authorization_verify_if_user_is_anon(current_user.id)
|
||||
|
||||
await authorization_verify_based_on_roles_and_authorship_and_usergroups(
|
||||
request,
|
||||
current_user.id,
|
||||
action,
|
||||
course_uuid,
|
||||
db_session,
|
||||
)
|
||||
|
||||
|
||||
## 🔒 RBAC Utils ##
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
from typing import Literal
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.courses import Course
|
||||
from src.db.organizations import Organization
|
||||
from sqlmodel import Session, select
|
||||
from src.security.rbac.rbac import (
|
||||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||
authorization_verify_if_user_is_anon,
|
||||
)
|
||||
from src.db.chapters import Chapter
|
||||
from src.db.activities import (
|
||||
from src.db.courses.chapters import Chapter
|
||||
from src.db.courses.activities import (
|
||||
Activity,
|
||||
ActivityRead,
|
||||
ActivitySubTypeEnum,
|
||||
ActivityTypeEnum,
|
||||
)
|
||||
from src.db.chapter_activities import ChapterActivity
|
||||
from src.db.course_chapters import CourseChapter
|
||||
from src.db.courses.chapter_activities import ChapterActivity
|
||||
from src.db.courses.course_chapters import CourseChapter
|
||||
from src.db.users import AnonymousUser, PublicUser
|
||||
from src.services.courses.activities.uploads.pdfs import upload_pdf
|
||||
from fastapi import HTTPException, status, UploadFile, Request
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from src.db.activities import ActivityRead
|
||||
from src.db.courses import CourseRead
|
||||
from src.db.courses.activities import ActivityRead
|
||||
from src.db.courses.courses import CourseRead
|
||||
|
||||
|
||||
def structure_activity_content_by_type(activity):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from typing import Literal
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.courses import Course
|
||||
from src.db.organizations import Organization
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
|
@ -8,15 +8,15 @@ from src.security.rbac.rbac import (
|
|||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||
authorization_verify_if_user_is_anon,
|
||||
)
|
||||
from src.db.chapters import Chapter
|
||||
from src.db.activities import (
|
||||
from src.db.courses.chapters import Chapter
|
||||
from src.db.courses.activities import (
|
||||
Activity,
|
||||
ActivityRead,
|
||||
ActivitySubTypeEnum,
|
||||
ActivityTypeEnum,
|
||||
)
|
||||
from src.db.chapter_activities import ChapterActivity
|
||||
from src.db.course_chapters import CourseChapter
|
||||
from src.db.courses.chapter_activities import ChapterActivity
|
||||
from src.db.courses.course_chapters import CourseChapter
|
||||
from src.db.users import AnonymousUser, PublicUser
|
||||
from src.services.courses.activities.uploads.videos import upload_video
|
||||
from fastapi import HTTPException, status, UploadFile, Request
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ from src.security.rbac.rbac import (
|
|||
authorization_verify_if_element_is_public,
|
||||
authorization_verify_if_user_is_anon,
|
||||
)
|
||||
from src.db.course_chapters import CourseChapter
|
||||
from src.db.activities import Activity, ActivityRead
|
||||
from src.db.chapter_activities import ChapterActivity
|
||||
from src.db.chapters import (
|
||||
from src.db.courses.course_chapters import CourseChapter
|
||||
from src.db.courses.activities import Activity, ActivityRead
|
||||
from src.db.courses.chapter_activities import ChapterActivity
|
||||
from src.db.courses.chapters import (
|
||||
Chapter,
|
||||
ChapterCreate,
|
||||
ChapterRead,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from src.db.collections import (
|
|||
CollectionUpdate,
|
||||
)
|
||||
from src.db.collections_courses import CollectionCourse
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.courses import Course
|
||||
from src.services.users.users import PublicUser
|
||||
from fastapi import HTTPException, status, Request
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from src.db.organizations import Organization
|
|||
from src.services.trail.trail import get_user_trail_with_orgid
|
||||
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum
|
||||
from src.db.users import PublicUser, AnonymousUser, User, UserRead
|
||||
from src.db.courses import (
|
||||
from src.db.courses.courses import (
|
||||
Course,
|
||||
CourseCreate,
|
||||
CourseRead,
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ from typing import List
|
|||
from uuid import uuid4
|
||||
from fastapi import HTTPException, Request, status
|
||||
from sqlmodel import Session, col, select
|
||||
from src.db.course_updates import (
|
||||
from src.db.courses.course_updates import (
|
||||
CourseUpdate,
|
||||
CourseUpdateCreate,
|
||||
CourseUpdateRead,
|
||||
CourseUpdateUpdate,
|
||||
)
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.courses import Course
|
||||
from src.db.organizations import Organization
|
||||
from src.db.users import AnonymousUser, PublicUser
|
||||
from src.services.courses.courses import rbac_check
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
from src.db.chapter_activities import ChapterActivity
|
||||
from src.db.courses.chapter_activities import ChapterActivity
|
||||
from fastapi import HTTPException, Request, status
|
||||
from sqlmodel import Session, select
|
||||
from src.db.activities import Activity
|
||||
from src.db.courses import Course
|
||||
from src.db.courses.activities import Activity
|
||||
from src.db.courses.courses import Course
|
||||
from src.db.trail_runs import TrailRun, TrailRunRead
|
||||
from src.db.trail_steps import TrailStep
|
||||
from src.db.trails import Trail, TrailCreate, TrailRead
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue