feat: init assignment subpages and misc python and ts improvements

This commit is contained in:
swve 2024-07-20 21:12:13 +02:00
parent e9caa2ceb4
commit dab80069d4
19 changed files with 109 additions and 49 deletions

View file

@ -1,7 +1,7 @@
import importlib import importlib
from logging.config import fileConfig from logging.config import fileConfig
import os import os
import alembic_postgresql_enum import alembic_postgresql_enum # noqa: F401
from sqlalchemy import engine_from_config from sqlalchemy import engine_from_config
from sqlalchemy import pool from sqlalchemy import pool
from sqlmodel import SQLModel from sqlmodel import SQLModel

View file

@ -8,8 +8,8 @@ Create Date: ${create_date}
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa # noqa: F401
import sqlmodel import sqlmodel # noqa: F401
${imports if imports else ""} ${imports if imports else ""}
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View file

@ -8,8 +8,8 @@ Create Date: 2024-07-11 20:46:26.582170
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa # noqa: F401
import sqlmodel import sqlmodel # noqa: F401
from alembic_postgresql_enum import TableReference # type: ignore from alembic_postgresql_enum import TableReference # type: ignore
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View file

@ -9,7 +9,7 @@ from typing import Sequence, Union
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel # noqa: F401
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View file

@ -8,9 +8,9 @@ Create Date: 2024-07-11 19:33:37.993767
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
from grpc import server from grpc import server # noqa: F401
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel # noqa: F401
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View file

@ -1,5 +1,5 @@
from typing import Optional, Dict from typing import Optional, Dict
from sqlalchemy import JSON, Column, ForeignKey, null from sqlalchemy import JSON, Column, ForeignKey
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
from enum import Enum from enum import Enum

View file

@ -3,7 +3,6 @@ from src.db.courses.assignments import (
AssignmentCreate, AssignmentCreate,
AssignmentRead, AssignmentRead,
AssignmentTaskCreate, AssignmentTaskCreate,
AssignmentTaskSubmissionCreate,
AssignmentTaskSubmissionUpdate, AssignmentTaskSubmissionUpdate,
AssignmentTaskUpdate, AssignmentTaskUpdate,
AssignmentUpdate, AssignmentUpdate,

View file

@ -7,7 +7,6 @@ from typing import Literal
from uuid import uuid4 from uuid import uuid4
from fastapi import HTTPException, Request, UploadFile from fastapi import HTTPException, Request, UploadFile
from sqlmodel import Session, select from sqlmodel import Session, select
from sympy import Sum
from src.db.courses.activities import Activity from src.db.courses.activities import Activity
from src.db.courses.assignments import ( from src.db.courses.assignments import (
@ -1172,7 +1171,7 @@ async def read_user_assignment_submissions(
# Find assignments tasks for an assignment # Find assignments tasks for an assignment
statement = select(AssignmentUserSubmission).where( statement = select(AssignmentUserSubmission).where(
assignment.assignment_uuid == assignment_uuid, AssignmentUserSubmission.assignment_id == assignment.id,
AssignmentUserSubmission.user_id == user_id, AssignmentUserSubmission.user_id == user_id,
) )

View file

@ -1,4 +1,3 @@
from uuid import uuid4
from src.services.utils.upload_content import upload_content from src.services.utils.upload_content import upload_content
@ -12,7 +11,7 @@ async def upload_submission_file(
assignment_task_uuid, assignment_task_uuid,
): ):
contents = file.file.read() contents = file.file.read()
file_format = file.filename.split(".")[-1] file.filename.split(".")[-1]
await upload_content( await upload_content(
f"courses/{course_uuid}/activities/{activity_uuid}/assignments/{assignment_uuid}/tasks/{assignment_task_uuid}/subs", f"courses/{course_uuid}/activities/{activity_uuid}/assignments/{assignment_uuid}/tasks/{assignment_task_uuid}/subs",

View file

@ -1,4 +1,3 @@
from uuid import uuid4
from src.services.utils.upload_content import upload_content from src.services.utils.upload_content import upload_content
@ -12,7 +11,7 @@ async def upload_reference_file(
assignment_task_uuid, assignment_task_uuid,
): ):
contents = file.file.read() contents = file.file.read()
file_format = file.filename.split(".")[-1] file.filename.split(".")[-1]
await upload_content( await upload_content(
f"courses/{course_uuid}/activities/{activity_uuid}/assignments/{assignment_uuid}/tasks/{assignment_task_uuid}", f"courses/{course_uuid}/activities/{activity_uuid}/assignments/{assignment_uuid}/tasks/{assignment_task_uuid}",

View file

@ -17,9 +17,6 @@ function HomeClient() {
const { data: orgs } = useSWR(`${getAPIUrl()}orgs/user/page/1/limit/10`, (url) => swrFetcher(url, access_token)) const { data: orgs } = useSWR(`${getAPIUrl()}orgs/user/page/1/limit/10`, (url) => swrFetcher(url, access_token))
useEffect(() => { useEffect(() => {
console.log(orgs)
}, [session, orgs]) }, [session, orgs])
return ( return (
<div className='flex flex-col'> <div className='flex flex-col'>

View file

@ -8,7 +8,7 @@ import { markActivityAsComplete } from '@services/courses/activity'
import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf' import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf'
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators' import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper' import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
import { useRouter } from 'next/navigation' import { usePathname, useRouter } from 'next/navigation'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement' import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
import { getCourseThumbnailMediaDirectory } from '@services/media/media' import { getCourseThumbnailMediaDirectory } from '@services/media/media'
import { useOrg } from '@components/Contexts/OrgContext' import { useOrg } from '@components/Contexts/OrgContext'
@ -42,6 +42,7 @@ function ActivityClient(props: ActivityClientProps) {
const course = props.course const course = props.course
const org = useOrg() as any const org = useOrg() as any
const session = useLHSession() as any; const session = useLHSession() as any;
const pathname = usePathname()
const access_token = session?.data?.tokens?.access_token; const access_token = session?.data?.tokens?.access_token;
const [bgColor, setBgColor] = React.useState('bg-white') const [bgColor, setBgColor] = React.useState('bg-white')
const [assignment, setAssignment] = React.useState(null) as any; const [assignment, setAssignment] = React.useState(null) as any;
@ -78,7 +79,7 @@ function ActivityClient(props: ActivityClientProps) {
setBgColor('bg-zinc-950'); setBgColor('bg-zinc-950');
} }
} }
, [activity]) , [activity,pathname ])
return ( return (
<> <>
@ -126,6 +127,7 @@ function ActivityClient(props: ActivityClientProps) {
</h1> </h1>
</div> </div>
<div className="flex space-x-1 items-center"> <div className="flex space-x-1 items-center">
{activity && activity.published == true && (
<AuthenticatedClientElement checkMethod="authentication"> <AuthenticatedClientElement checkMethod="authentication">
{activity.activity_type != 'TYPE_ASSIGNMENT' && {activity.activity_type != 'TYPE_ASSIGNMENT' &&
<> <>
@ -141,7 +143,6 @@ function ActivityClient(props: ActivityClientProps) {
} }
{activity.activity_type == 'TYPE_ASSIGNMENT' && {activity.activity_type == 'TYPE_ASSIGNMENT' &&
<> <>
<MoreVertical size={17} className="text-gray-300 " /> <MoreVertical size={17} className="text-gray-300 " />
<AssignmentSubmissionProvider assignment_uuid={assignment?.assignment_uuid}> <AssignmentSubmissionProvider assignment_uuid={assignment?.assignment_uuid}>
<AssignmentTools <AssignmentTools
@ -156,6 +157,7 @@ function ActivityClient(props: ActivityClientProps) {
} }
</AuthenticatedClientElement> </AuthenticatedClientElement>
)}
</div> </div>
</div> </div>
{activity && activity.published == false && ( {activity && activity.published == false && (

View file

@ -8,8 +8,9 @@ import { GalleryVerticalEnd, Info, TentTree, Trash } from 'lucide-react'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { mutate } from 'swr'; import { mutate } from 'swr';
import dynamic from 'next/dynamic';
import { AssignmentTaskGeneralEdit } from './Subs/AssignmentTaskGeneralEdit'; import { AssignmentTaskGeneralEdit } from './Subs/AssignmentTaskGeneralEdit';
import AssignmentTaskContentEdit from './Subs/AssignmentTaskContentEdit'; const AssignmentTaskContentEdit = dynamic(() => import('./Subs/AssignmentTaskContentEdit'))
function AssignmentTaskEditor({ page }: any) { function AssignmentTaskEditor({ page }: any) {
const [selectedSubPage, setSelectedSubPage] = React.useState(page) const [selectedSubPage, setSelectedSubPage] = React.useState(page)

View file

@ -1,12 +1,9 @@
'use client'; 'use client';
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs' import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
import { BookOpen, BookX, EllipsisVertical, Eye, LayoutList } from 'lucide-react' import { BookOpen, BookX, EllipsisVertical, Eye, Layers2, UserRoundPen } from 'lucide-react'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { AssignmentProvider, useAssignments } from '@components/Contexts/Assignments/AssignmentContext'; import { AssignmentProvider, useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
import AssignmentTasks from './_components/Tasks';
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext';
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'; import ToolTip from '@components/StyledElements/Tooltip/Tooltip';
import AssignmentTaskEditor from './_components/TaskEditor/TaskEditor';
import { updateAssignment } from '@services/courses/assignments'; import { updateAssignment } from '@services/courses/assignments';
import { useLHSession } from '@components/Contexts/LHSessionContext'; import { useLHSession } from '@components/Contexts/LHSessionContext';
import { mutate } from 'swr'; import { mutate } from 'swr';
@ -15,40 +12,59 @@ import toast from 'react-hot-toast';
import Link from 'next/link'; import Link from 'next/link';
import { useParams } from 'next/navigation'; import { useParams } from 'next/navigation';
import { updateActivity } from '@services/courses/activities'; import { updateActivity } from '@services/courses/activities';
// Lazy Loading
import dynamic from 'next/dynamic';
import AssignmentEditorSubPage from './subpages/AssignmentEditorSubPage';
const AssignmentSubmissionsSubPage = dynamic(() => import('./subpages/AssignmentSubmissionsSubPage'))
function AssignmentEdit() { function AssignmentEdit() {
const params = useParams<{ assignmentuuid: string; }>() const params = useParams<{ assignmentuuid: string; }>()
const [selectedSubPage, setSelectedSubPage] = React.useState('editor')
return ( return (
<div className='flex w-full flex-col'> <div className='flex w-full flex-col'>
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}> <AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
<div className='pb-5 bg-white z-50 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] nice-shadow'> <div className='flex flex-col bg-white z-50 shadow-[0px_4px_16px_rgba(0,0,0,0.06)] nice-shadow'>
<div className='flex justify-between mr-10 h-full'> <div className='flex justify-between mr-10 h-full'>
<div className="pl-10 mr-10 tracking-tighter"> <div className="pl-10 mr-10 tracking-tighter">
<BrdCmpx /> <BrdCmpx />
<div className="w-100 flex justify-between"> <div className="w-100 flex justify-between">
<div className="flex font-bold text-2xl">Assignment Editor</div> <div className="flex font-bold text-2xl">Assignment Tools </div>
</div> </div>
</div> </div>
<div className='flex flex-col justify-center antialiased'> <div className='flex flex-col justify-center antialiased'>
<PublishingState /> <PublishingState />
</div> </div>
</div> </div>
<div className='flex space-x-2 pt-2 text-sm tracking-tight font-semibold pl-10 mr-10'>
<div
onClick={() => setSelectedSubPage('editor')}
className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'editor'
? 'border-b-4'
: 'opacity-50'
} cursor-pointer`}
>
<div className="flex items-center space-x-2.5 mx-2">
<Layers2 size={16} />
<div>Editor</div>
</div>
</div>
<div
onClick={() => setSelectedSubPage('submissions')}
className={`flex space-x-4 py-2 w-fit text-center border-black transition-all ease-linear ${selectedSubPage === 'submissions'
? 'border-b-4'
: 'opacity-50'
} cursor-pointer`}
>
<div className="flex items-center space-x-2.5 mx-2">
<UserRoundPen size={16} />
<div>Submissions</div>
</div>
</div>
</div>
</div> </div>
<div className="flex h-full w-full"> <div className="flex h-full w-full">
<AssignmentsTaskProvider> {selectedSubPage === 'editor' && <AssignmentEditorSubPage assignmentuuid={params.assignmentuuid} />}
<div className='flex w-[400px] flex-col h-full custom-dots-bg'> {selectedSubPage === 'submissions' && <AssignmentSubmissionsSubPage assignment_uuid={params.assignmentuuid} />}
<div className='flex mx-auto px-3.5 py-1 bg-neutral-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
<LayoutList size={18} />
<p>Tasks</p>
</div>
<AssignmentTasks assignment_uuid={'assignment_' + params.assignmentuuid} />
</div>
<div className='flex flex-grow bg-[#fefcfe] nice-shadow h-full w-full'>
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>
<AssignmentTaskEditor page='general' />
</AssignmentProvider>
</div>
</AssignmentsTaskProvider>
</div> </div>
</AssignmentProvider> </AssignmentProvider>
</div> </div>

View file

@ -0,0 +1,29 @@
'use client';
import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/AssignmentsTaskContext'
import { LayoutList } from 'lucide-react'
import React from 'react'
import AssignmentTasks from '../_components/Tasks'
import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext'
import dynamic from 'next/dynamic';
const AssignmentTaskEditor = dynamic(() => import('../_components/TaskEditor/TaskEditor'))
function AssignmentEditorSubPage({ assignmentuuid }: { assignmentuuid: string }) {
return (
<AssignmentsTaskProvider>
<div className='flex w-[400px] flex-col h-full custom-dots-bg'>
<div className='flex mx-auto px-3.5 py-1 bg-neutral-600/80 space-x-2 my-5 items-center text-sm font-bold text-white rounded-full'>
<LayoutList size={18} />
<p>Tasks</p>
</div>
<AssignmentTasks assignment_uuid={'assignment_' + assignmentuuid} />
</div>
<div className='flex flex-grow bg-[#fefcfe] nice-shadow h-full w-full'>
<AssignmentProvider assignment_uuid={'assignment_' + assignmentuuid}>
<AssignmentTaskEditor page='general' />
</AssignmentProvider>
</div>
</AssignmentsTaskProvider>
)
}
export default AssignmentEditorSubPage

View file

@ -0,0 +1,25 @@
import { useLHSession } from '@components/Contexts/LHSessionContext';
import { getAPIUrl } from '@services/config/config'
import { swrFetcher } from '@services/utils/ts/requests';
import React from 'react'
import useSWR from 'swr';
function AssignmentSubmissionsSubPage({ assignment_uuid }: { assignment_uuid: string }) {
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
const { data: assignmentSubmission, error: assignmentError } = useSWR(
`${getAPIUrl()}assignments/assignment_${assignment_uuid}/submissions`,
(url) => swrFetcher(url, access_token)
)
return (
<div className='pl-10 mr-10 flex'>
{assignmentSubmission && assignmentSubmission.length > 0 && (
<div>s</div>
)}
</div>
)
}
export default AssignmentSubmissionsSubPage

View file

@ -78,7 +78,6 @@ export function useAssignmentsTaskDispatch() {
function assignmentstaskReducer(state: State, action: Action): State { function assignmentstaskReducer(state: State, action: Action): State {
switch (action.type) { switch (action.type) {
case 'setSelectedAssignmentTaskUUID': case 'setSelectedAssignmentTaskUUID':
console.log('st', action.payload)
return { ...state, selectedAssignmentTaskUUID: action.payload }; return { ...state, selectedAssignmentTaskUUID: action.payload };
case 'setAssignmentTask': case 'setAssignmentTask':
return { ...state, assignmentTask: action.payload }; return { ...state, assignmentTask: action.payload };

View file

@ -254,9 +254,6 @@ const ActivityElementOptions = ({ activity }: any) => {
}; };
useEffect(() => { useEffect(() => {
console.log(activity)
fetchAssignmentUUID(); fetchAssignmentUUID();
}, [activity, course]); }, [activity, course]);

View file

@ -55,8 +55,6 @@ function NewAssignment({ submitActivity, chapterId, course, closeModal }: any) {
} }
const activity_res = await createActivity(activity, chapterId, org?.id, session.data?.tokens?.access_token) const activity_res = await createActivity(activity, chapterId, org?.id, session.data?.tokens?.access_token)
console.log(course)
console.log(activity_res)
await createAssignment({ await createAssignment({
title: activityName, title: activityName,
description: activityDescription, description: activityDescription,