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]]
|
[[package]]
|
||||||
name = "boto3"
|
name = "boto3"
|
||||||
version = "1.34.137"
|
version = "1.34.143"
|
||||||
description = "The AWS SDK for Python"
|
description = "The AWS SDK for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "boto3-1.34.137-py3-none-any.whl", hash = "sha256:7cb697d67fd138ceebc6f789919ae370c092a50c6b0ccc4ef483027935502eab"},
|
{file = "boto3-1.34.143-py3-none-any.whl", hash = "sha256:0d16832f23e6bd3ae94e35ea8e625529850bfad9baccd426de96ad8f445d8e03"},
|
||||||
{file = "boto3-1.34.137.tar.gz", hash = "sha256:0b21b84db4619b3711a6f643d465a5a25e81231ee43615c55a20ff6b89c6cc3c"},
|
{file = "boto3-1.34.143.tar.gz", hash = "sha256:b590ce80c65149194def43ebf0ea1cf0533945502507837389a8d22e3ecbcf05"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
botocore = ">=1.34.137,<1.35.0"
|
botocore = ">=1.34.143,<1.35.0"
|
||||||
jmespath = ">=0.7.1,<2.0.0"
|
jmespath = ">=0.7.1,<2.0.0"
|
||||||
s3transfer = ">=0.10.0,<0.11.0"
|
s3transfer = ">=0.10.0,<0.11.0"
|
||||||
|
|
||||||
|
|
@ -234,13 +234,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "botocore"
|
name = "botocore"
|
||||||
version = "1.34.137"
|
version = "1.34.143"
|
||||||
description = "Low-level, data-driven core of boto 3."
|
description = "Low-level, data-driven core of boto 3."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "botocore-1.34.137-py3-none-any.whl", hash = "sha256:a980fa4adec4bfa23fff70a3512622e9412c69c791898a52cafc2458b0be6040"},
|
{file = "botocore-1.34.143-py3-none-any.whl", hash = "sha256:094aea179e8aaa1bc957ad49cc27d93b189dd3a1f3075d8b0ca7c445a2a88430"},
|
||||||
{file = "botocore-1.34.137.tar.gz", hash = "sha256:e29c8e9bfda0b20a1997792968e85868bfce42fefad9730f633a81adcff3f2ef"},
|
{file = "botocore-1.34.143.tar.gz", hash = "sha256:059f032ec05733a836e04e869c5a15534420102f93116f3bc9a5b759b0651caf"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -844,13 +844,13 @@ tqdm = ["tqdm"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "google-auth"
|
name = "google-auth"
|
||||||
version = "2.31.0"
|
version = "2.32.0"
|
||||||
description = "Google Authentication Library"
|
description = "Google Authentication Library"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "google-auth-2.31.0.tar.gz", hash = "sha256:87805c36970047247c8afe614d4e3af8eceafc1ebba0c679fe75ddd1d575e871"},
|
{file = "google_auth-2.32.0-py2.py3-none-any.whl", hash = "sha256:53326ea2ebec768070a94bee4e1b9194c9646ea0c2bd72422785bd0f9abfad7b"},
|
||||||
{file = "google_auth-2.31.0-py2.py3-none-any.whl", hash = "sha256:042c4702efa9f7d3c48d3a69341c209381b125faa6dbf3ebe56bc7e40ae05c23"},
|
{file = "google_auth-2.32.0.tar.gz", hash = "sha256:49315be72c55a6a37d62819e3573f6b416aca00721f7e3e31a008d928bf64022"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -1877,13 +1877,13 @@ sympy = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openai"
|
name = "openai"
|
||||||
version = "1.35.8"
|
version = "1.35.13"
|
||||||
description = "The official Python library for the openai API"
|
description = "The official Python library for the openai API"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7.1"
|
python-versions = ">=3.7.1"
|
||||||
files = [
|
files = [
|
||||||
{file = "openai-1.35.8-py3-none-any.whl", hash = "sha256:69d5b0f47f0c806d5da83fb0f84c147661395226d7f79acc78aa1d9b8c635887"},
|
{file = "openai-1.35.13-py3-none-any.whl", hash = "sha256:36ec3e93e0d1f243f69be85c89b9221a471c3e450dfd9df16c9829e3cdf63e60"},
|
||||||
{file = "openai-1.35.8.tar.gz", hash = "sha256:3f6101888bb516647edade74c503f2b937b8bab73408e799d58f2aba68bbe51c"},
|
{file = "openai-1.35.13.tar.gz", hash = "sha256:c684f3945608baf7d2dcc0ef3ee6f3e27e4c66f21076df0b47be45d57e6ae6e4"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -2732,13 +2732,13 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "resend"
|
name = "resend"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
description = "Resend Python SDK"
|
description = "Resend Python SDK"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "resend-2.1.0-py2.py3-none-any.whl", hash = "sha256:7f2a221983fab74a09f669c0c14a75daf547ffa4b4930141626d9cca55bca767"},
|
{file = "resend-2.2.0-py2.py3-none-any.whl", hash = "sha256:be420762885de25c816497f09207da1cd54d253c44d9f81f441367893a42d099"},
|
||||||
{file = "resend-2.1.0.tar.gz", hash = "sha256:92dc8e035c2ce8cf8210c1c322e86b0a4f509e0c82a80932d3323cd2f3a43d2d"},
|
{file = "resend-2.2.0.tar.gz", hash = "sha256:f44976e4a37bb66445280bd8ef201eaac536b07bbe7c4da8f1717f4fcc63da7e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -2796,13 +2796,13 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-sdk"
|
name = "sentry-sdk"
|
||||||
version = "2.7.1"
|
version = "2.9.0"
|
||||||
description = "Python client for Sentry (https://sentry.io)"
|
description = "Python client for Sentry (https://sentry.io)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
{file = "sentry_sdk-2.7.1-py2.py3-none-any.whl", hash = "sha256:ef1b3d54eb715825657cd4bb3cb42bb4dc85087bac14c56b0fd8c21abd968c9a"},
|
{file = "sentry_sdk-2.9.0-py2.py3-none-any.whl", hash = "sha256:0bea5fa8b564cc0d09f2e6f55893e8f70286048b0ffb3a341d5b695d1af0e6ee"},
|
||||||
{file = "sentry_sdk-2.7.1.tar.gz", hash = "sha256:25006c7e68b75aaa5e6b9c6a420ece22e8d7daec4b7a906ffd3a8607b67c037b"},
|
{file = "sentry_sdk-2.9.0.tar.gz", hash = "sha256:4c85bad74df9767976afb3eeddc33e0e153300e887d637775a753a35ef99bee6"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -2847,13 +2847,13 @@ tornado = ["tornado (>=6)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "setuptools"
|
name = "setuptools"
|
||||||
version = "70.2.0"
|
version = "70.3.0"
|
||||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"},
|
{file = "setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc"},
|
||||||
{file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"},
|
{file = "setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
|
|
@ -3014,27 +3014,30 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sympy"
|
name = "sympy"
|
||||||
version = "1.12.1"
|
version = "1.13.0"
|
||||||
description = "Computer algebra system (CAS) in Python"
|
description = "Computer algebra system (CAS) in Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "sympy-1.12.1-py3-none-any.whl", hash = "sha256:9b2cbc7f1a640289430e13d2a56f02f867a1da0190f2f99d8968c2f74da0e515"},
|
{file = "sympy-1.13.0-py3-none-any.whl", hash = "sha256:6b0b32a4673fb91bd3cac3b55406c8e01d53ae22780be467301cc452f6680c92"},
|
||||||
{file = "sympy-1.12.1.tar.gz", hash = "sha256:2877b03f998cd8c08f07cd0de5b767119cd3ef40d09f41c30d722f6686b0fb88"},
|
{file = "sympy-1.13.0.tar.gz", hash = "sha256:3b6af8f4d008b9a1a6a4268b335b984b23835f26d1d60b0526ebc71d48a25f57"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[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]]
|
[[package]]
|
||||||
name = "tenacity"
|
name = "tenacity"
|
||||||
version = "8.4.2"
|
version = "8.5.0"
|
||||||
description = "Retry code until it succeeds"
|
description = "Retry code until it succeeds"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "tenacity-8.4.2-py3-none-any.whl", hash = "sha256:9e6f7cf7da729125c7437222f8a522279751cdfbe6b67bfe64f75d3a348661b2"},
|
{file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"},
|
||||||
{file = "tenacity-8.4.2.tar.gz", hash = "sha256:cd80a53a79336edba8489e767f729e4f391c896956b57140b5d7511a64bbd3ef"},
|
{file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[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):
|
class BlockRead(BlockBase):
|
||||||
id: int
|
id: int = Field(default=None, primary_key=True)
|
||||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||||
course_id: int = Field(default=None, foreign_key="course.id")
|
course_id: int = Field(default=None, foreign_key="course.id")
|
||||||
chapter_id: int = Field(default=None, foreign_key="chapter.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 pydantic import BaseModel
|
||||||
from sqlalchemy import Column, ForeignKey
|
from sqlalchemy import Column, ForeignKey
|
||||||
from sqlmodel import Field, SQLModel
|
from sqlmodel import Field, SQLModel
|
||||||
from src.db.activities import ActivityRead
|
from src.db.courses.activities import ActivityRead
|
||||||
|
|
||||||
|
|
||||||
class ChapterBase(SQLModel):
|
class ChapterBase(SQLModel):
|
||||||
|
|
@ -33,10 +33,10 @@ class ChapterCreate(ChapterBase):
|
||||||
|
|
||||||
class ChapterUpdate(ChapterBase):
|
class ChapterUpdate(ChapterBase):
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
description: Optional[str]
|
description: Optional[str] = ""
|
||||||
thumbnail_image: Optional[str]
|
thumbnail_image: Optional[str] = ""
|
||||||
course_id: Optional[int]
|
course_id: Optional[int]
|
||||||
org_id: Optional[int]
|
org_id: Optional[int] # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class ChapterRead(ChapterBase):
|
class ChapterRead(ChapterBase):
|
||||||
|
|
@ -3,7 +3,7 @@ from sqlalchemy import Column, ForeignKey, Integer
|
||||||
from sqlmodel import Field, SQLModel
|
from sqlmodel import Field, SQLModel
|
||||||
from src.db.users import UserRead
|
from src.db.users import UserRead
|
||||||
from src.db.trails import TrailRead
|
from src.db.trails import TrailRead
|
||||||
from src.db.chapters import ChapterRead
|
from src.db.courses.chapters import ChapterRead
|
||||||
|
|
||||||
|
|
||||||
class CourseBase(SQLModel):
|
class CourseBase(SQLModel):
|
||||||
|
|
@ -6,8 +6,12 @@ from src.db.trail_runs import TrailRunRead
|
||||||
|
|
||||||
|
|
||||||
class TrailBase(SQLModel):
|
class TrailBase(SQLModel):
|
||||||
org_id: int = Field(default=None, foreign_key="organization.id")
|
org_id: int = Field(
|
||||||
user_id: int = Field(default=None, foreign_key="user.id")
|
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):
|
class Trail(TrailBase, table=True):
|
||||||
|
|
@ -20,6 +24,7 @@ class Trail(TrailBase, table=True):
|
||||||
class TrailCreate(TrailBase):
|
class TrailCreate(TrailBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# TODO: This is a hacky way to get around the list[TrailRun] issue, find a better way to do this
|
# TODO: This is a hacky way to get around the list[TrailRun] issue, find a better way to do this
|
||||||
class TrailRead(BaseModel):
|
class TrailRead(BaseModel):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from src.routers import usergroups
|
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.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.routers.install import install
|
||||||
from src.services.dev.dev import isDevModeEnabledOrRaise
|
from src.services.dev.dev import isDevModeEnabledOrRaise
|
||||||
from src.services.install.install import isInstallModeEnabled
|
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(roles.router, prefix="/roles", tags=["roles"])
|
||||||
v1_router.include_router(blocks.router, prefix="/blocks", tags=["blocks"])
|
v1_router.include_router(blocks.router, prefix="/blocks", tags=["blocks"])
|
||||||
v1_router.include_router(courses.router, prefix="/courses", tags=["courses"])
|
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(chapters.router, prefix="/chapters", tags=["chapters"])
|
||||||
v1_router.include_router(activities.router, prefix="/activities", tags=["activities"])
|
v1_router.include_router(activities.router, prefix="/activities", tags=["activities"])
|
||||||
v1_router.include_router(collections.router, prefix="/collections", tags=["collections"])
|
v1_router.include_router(collections.router, prefix="/collections", tags=["collections"])
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
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.db.users import PublicUser
|
||||||
from src.core.events.database import get_db_session
|
from src.core.events.database import get_db_session
|
||||||
from src.services.courses.activities.activities import (
|
from src.services.courses.activities.activities import (
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
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.core.events.database import get_db_session
|
||||||
from src.security.auth import get_current_user
|
from src.security.auth import get_current_user
|
||||||
from src.services.blocks.block_types.imageBlock.imageBlock import (
|
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 typing import List
|
||||||
from fastapi import APIRouter, Depends, Request
|
from fastapi import APIRouter, Depends, Request
|
||||||
from src.core.events.database import get_db_session
|
from src.core.events.database import get_db_session
|
||||||
from src.db.chapters import (
|
from src.db.courses.chapters import (
|
||||||
ChapterCreate,
|
ChapterCreate,
|
||||||
ChapterRead,
|
ChapterRead,
|
||||||
ChapterUpdate,
|
ChapterUpdate,
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ from typing import List
|
||||||
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
from src.core.events.database import get_db_session
|
from src.core.events.database import get_db_session
|
||||||
from src.db.course_updates import (
|
from src.db.courses.course_updates import (
|
||||||
CourseUpdateCreate,
|
CourseUpdateCreate,
|
||||||
CourseUpdateRead,
|
CourseUpdateRead,
|
||||||
CourseUpdateUpdate,
|
CourseUpdateUpdate,
|
||||||
)
|
)
|
||||||
from src.db.users import PublicUser
|
from src.db.users import PublicUser
|
||||||
from src.db.courses import (
|
from src.db.courses.courses import (
|
||||||
CourseCreate,
|
CourseCreate,
|
||||||
CourseRead,
|
CourseRead,
|
||||||
CourseUpdate,
|
CourseUpdate,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from fastapi import HTTPException, status, Request
|
||||||
from sqlalchemy import null
|
from sqlalchemy import null
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.db.collections import Collection
|
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.resource_authors import ResourceAuthor, ResourceAuthorshipEnum
|
||||||
from src.db.roles import Role
|
from src.db.roles import Role
|
||||||
from src.db.user_organizations import UserOrganization
|
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.organization_config import OrganizationConfig
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
from src.services.ai.utils import check_limits_and_config, count_ai_ask
|
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.core.events.database import get_db_session
|
||||||
from src.db.users import PublicUser
|
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.security.auth import get_current_user
|
||||||
from src.services.ai.base import ask_ai, get_chat_session_history
|
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 src.db.organizations import Organization
|
||||||
from fastapi import HTTPException, status, UploadFile, Request
|
from fastapi import HTTPException, status, UploadFile, Request
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.db.activities import Activity
|
from src.db.courses.activities import Activity
|
||||||
from src.db.blocks import Block, BlockRead, BlockTypeEnum
|
from src.db.courses.blocks import Block, BlockRead, BlockTypeEnum
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
||||||
from src.services.users.users import PublicUser
|
from src.services.users.users import PublicUser
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ from uuid import uuid4
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
from fastapi import HTTPException, status, UploadFile, Request
|
from fastapi import HTTPException, status, UploadFile, Request
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.db.activities import Activity
|
from src.db.courses.activities import Activity
|
||||||
from src.db.blocks import Block, BlockRead, BlockTypeEnum
|
from src.db.courses.blocks import Block, BlockRead, BlockTypeEnum
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
||||||
|
|
||||||
from src.services.users.users import PublicUser
|
from src.services.users.users import PublicUser
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ from uuid import uuid4
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
from fastapi import HTTPException, status, UploadFile, Request
|
from fastapi import HTTPException, status, UploadFile, Request
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.db.activities import Activity
|
from src.db.courses.activities import Activity
|
||||||
from src.db.blocks import Block, BlockRead, BlockTypeEnum
|
from src.db.courses.blocks import Block, BlockRead, BlockTypeEnum
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
from src.services.blocks.utils.upload_files import upload_file_and_return_file_object
|
||||||
|
|
||||||
from src.services.users.users import PublicUser
|
from src.services.users.users import PublicUser
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.db.chapters import Chapter
|
from src.db.courses.chapters import Chapter
|
||||||
from src.security.rbac.rbac import (
|
from src.security.rbac.rbac import (
|
||||||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||||
authorization_verify_if_element_is_public,
|
authorization_verify_if_element_is_public,
|
||||||
authorization_verify_if_user_is_anon,
|
authorization_verify_if_user_is_anon,
|
||||||
)
|
)
|
||||||
from src.db.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
|
from src.db.courses.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
|
||||||
from src.db.chapter_activities import ChapterActivity
|
from src.db.courses.chapter_activities import ChapterActivity
|
||||||
from src.db.users import AnonymousUser, PublicUser
|
from src.db.users import AnonymousUser, PublicUser
|
||||||
from fastapi import HTTPException, Request
|
from fastapi import HTTPException, Request
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
@ -58,7 +58,7 @@ async def create_activity(
|
||||||
statement = (
|
statement = (
|
||||||
select(ChapterActivity)
|
select(ChapterActivity)
|
||||||
.where(ChapterActivity.chapter_id == activity_object.chapter_id)
|
.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()
|
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 typing import Literal
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.security.rbac.rbac import (
|
from src.security.rbac.rbac import (
|
||||||
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
authorization_verify_based_on_roles_and_authorship_and_usergroups,
|
||||||
authorization_verify_if_user_is_anon,
|
authorization_verify_if_user_is_anon,
|
||||||
)
|
)
|
||||||
from src.db.chapters import Chapter
|
from src.db.courses.chapters import Chapter
|
||||||
from src.db.activities import (
|
from src.db.courses.activities import (
|
||||||
Activity,
|
Activity,
|
||||||
ActivityRead,
|
ActivityRead,
|
||||||
ActivitySubTypeEnum,
|
ActivitySubTypeEnum,
|
||||||
ActivityTypeEnum,
|
ActivityTypeEnum,
|
||||||
)
|
)
|
||||||
from src.db.chapter_activities import ChapterActivity
|
from src.db.courses.chapter_activities import ChapterActivity
|
||||||
from src.db.course_chapters import CourseChapter
|
from src.db.courses.course_chapters import CourseChapter
|
||||||
from src.db.users import AnonymousUser, PublicUser
|
from src.db.users import AnonymousUser, PublicUser
|
||||||
from src.services.courses.activities.uploads.pdfs import upload_pdf
|
from src.services.courses.activities.uploads.pdfs import upload_pdf
|
||||||
from fastapi import HTTPException, status, UploadFile, Request
|
from fastapi import HTTPException, status, UploadFile, Request
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from src.db.activities import ActivityRead
|
from src.db.courses.activities import ActivityRead
|
||||||
from src.db.courses import CourseRead
|
from src.db.courses.courses import CourseRead
|
||||||
|
|
||||||
|
|
||||||
def structure_activity_content_by_type(activity):
|
def structure_activity_content_by_type(activity):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
|
|
||||||
from pydantic import BaseModel
|
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_based_on_roles_and_authorship_and_usergroups,
|
||||||
authorization_verify_if_user_is_anon,
|
authorization_verify_if_user_is_anon,
|
||||||
)
|
)
|
||||||
from src.db.chapters import Chapter
|
from src.db.courses.chapters import Chapter
|
||||||
from src.db.activities import (
|
from src.db.courses.activities import (
|
||||||
Activity,
|
Activity,
|
||||||
ActivityRead,
|
ActivityRead,
|
||||||
ActivitySubTypeEnum,
|
ActivitySubTypeEnum,
|
||||||
ActivityTypeEnum,
|
ActivityTypeEnum,
|
||||||
)
|
)
|
||||||
from src.db.chapter_activities import ChapterActivity
|
from src.db.courses.chapter_activities import ChapterActivity
|
||||||
from src.db.course_chapters import CourseChapter
|
from src.db.courses.course_chapters import CourseChapter
|
||||||
from src.db.users import AnonymousUser, PublicUser
|
from src.db.users import AnonymousUser, PublicUser
|
||||||
from src.services.courses.activities.uploads.videos import upload_video
|
from src.services.courses.activities.uploads.videos import upload_video
|
||||||
from fastapi import HTTPException, status, UploadFile, Request
|
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_element_is_public,
|
||||||
authorization_verify_if_user_is_anon,
|
authorization_verify_if_user_is_anon,
|
||||||
)
|
)
|
||||||
from src.db.course_chapters import CourseChapter
|
from src.db.courses.course_chapters import CourseChapter
|
||||||
from src.db.activities import Activity, ActivityRead
|
from src.db.courses.activities import Activity, ActivityRead
|
||||||
from src.db.chapter_activities import ChapterActivity
|
from src.db.courses.chapter_activities import ChapterActivity
|
||||||
from src.db.chapters import (
|
from src.db.courses.chapters import (
|
||||||
Chapter,
|
Chapter,
|
||||||
ChapterCreate,
|
ChapterCreate,
|
||||||
ChapterRead,
|
ChapterRead,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from src.db.collections import (
|
||||||
CollectionUpdate,
|
CollectionUpdate,
|
||||||
)
|
)
|
||||||
from src.db.collections_courses import CollectionCourse
|
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 src.services.users.users import PublicUser
|
||||||
from fastapi import HTTPException, status, Request
|
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.services.trail.trail import get_user_trail_with_orgid
|
||||||
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum
|
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum
|
||||||
from src.db.users import PublicUser, AnonymousUser, User, UserRead
|
from src.db.users import PublicUser, AnonymousUser, User, UserRead
|
||||||
from src.db.courses import (
|
from src.db.courses.courses import (
|
||||||
Course,
|
Course,
|
||||||
CourseCreate,
|
CourseCreate,
|
||||||
CourseRead,
|
CourseRead,
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ from typing import List
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from fastapi import HTTPException, Request, status
|
from fastapi import HTTPException, Request, status
|
||||||
from sqlmodel import Session, col, select
|
from sqlmodel import Session, col, select
|
||||||
from src.db.course_updates import (
|
from src.db.courses.course_updates import (
|
||||||
CourseUpdate,
|
CourseUpdate,
|
||||||
CourseUpdateCreate,
|
CourseUpdateCreate,
|
||||||
CourseUpdateRead,
|
CourseUpdateRead,
|
||||||
CourseUpdateUpdate,
|
CourseUpdateUpdate,
|
||||||
)
|
)
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
from src.db.users import AnonymousUser, PublicUser
|
from src.db.users import AnonymousUser, PublicUser
|
||||||
from src.services.courses.courses import rbac_check
|
from src.services.courses.courses import rbac_check
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from uuid import uuid4
|
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 fastapi import HTTPException, Request, status
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.db.activities import Activity
|
from src.db.courses.activities import Activity
|
||||||
from src.db.courses import Course
|
from src.db.courses.courses import Course
|
||||||
from src.db.trail_runs import TrailRun, TrailRunRead
|
from src.db.trail_runs import TrailRun, TrailRunRead
|
||||||
from src.db.trail_steps import TrailStep
|
from src.db.trail_steps import TrailStep
|
||||||
from src.db.trails import Trail, TrailCreate, TrailRead
|
from src.db.trails import Trail, TrailCreate, TrailRead
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue