Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

Commit

Permalink
create groups with project
Browse files Browse the repository at this point in the history
  • Loading branch information
cstefc committed May 23, 2024
1 parent 3678f04 commit 93eaefd
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 72 deletions.
4 changes: 4 additions & 0 deletions frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
"failed": "Something went wrong while saving your project. Please try again.",
"archived": "Archived:",
"visible": "Visible:",
"amount_of_students": "Amount of students:",
"amount_of_groups": "Amount of groups:",
"individual": "Individual project:",
"name": {
"tag": "Project name:",
"placeholder": "Enter a name"
Expand Down Expand Up @@ -172,6 +175,7 @@
"deadline": "Deadline",
"status": "Status",
"description": "Description",
"amount_of_students": "Amount of students: ",
"submission_files": "Submission must contain",
"groupmembers": {
"tag": "Groupmembers",
Expand Down
3 changes: 3 additions & 0 deletions frontend/public/locales/nl/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
"failed": "Er is iets fout gegaan tijdens het opslaan van uw project. Probeer het opnieuw.",
"archived": "Gearchiveerd:",
"visible": "Zichtbaar:",
"amount_of_students": "Aantal leerlingen:",
"amount_of_groups": "Aantal groepen:",
"individual": "Individueel project:",
"name": {
"tag": "Projectnaam:",
"placeholder": "Voer een naam in"
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/ProjectStudentComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {Submission} from "../utils/ApiInterfaces.ts";
import {DEBUG} from "../pages/root.tsx";
import {make_submission} from "../utils/api/Submission.ts";
import {joinGroup, leaveGroup} from "../utils/api/Groups.ts";
import {getGroupInfo, loadGroupMembers} from "../dataloaders/ProjectsStudentLoader.ts";
import {getGroupInfo, loadGroupMembers} from "../dataloaders/loader_helpers/SharedFunctions.ts";
import SimpleTests from "./SimpleTests/SimpleTests.tsx";
import {TeacherOrStudent} from "./SimpleTests/TeacherOrStudentEnum.tsx";

Expand Down Expand Up @@ -89,7 +89,7 @@ export default function ProjectStudentComponent(props: { project: ProjectStudent
const {t} = useTranslation();

async function onLeaveClick() {
void leaveGroup(groupId)
await leaveGroup(groupId)
const group_info = await getGroupInfo(props.project_id)
setGroupInfo(group_info)
setGroupMembers([])
Expand Down Expand Up @@ -134,7 +134,7 @@ export default function ProjectStudentComponent(props: { project: ProjectStudent
</div>
</div>
<div className={"is-flex is-justify-content-end is-align-items-center"}>
{props.maxGroupMembers > 1 && <div className={"columns"}>
{<div className={"columns"}>

<p className={"mx-3"}>leave group: </p>
<button className={"button"} onClick={() => {
Expand Down Expand Up @@ -270,7 +270,7 @@ export default function ProjectStudentComponent(props: { project: ProjectStudent
{success && <div className="notification is-success is-flex is-justify-content-center mx-5 my-3">
{success}
</div>}
{!hasGroup && props.project.maxGroupMembers > 1 &&
{!hasGroup &&
<div>
<div className={"notification is-danger is-flex is-justify-content-center mx-5 my-3"}>
{t('project.not_in_group')}
Expand Down
110 changes: 93 additions & 17 deletions frontend/src/components/ProjectTeacherComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {ProjectInput} from "../utils/InputInterfaces.ts";
import {course_create_project} from "../utils/api/Course.ts";
import {Project} from "../utils/ApiInterfaces.ts";
import {useNavigate} from "react-router-dom";
import {update_project} from "../utils/api/Project.ts";
import {project_create_group, update_project} from "../utils/api/Project.ts";
import {getScrollbarWidth} from "../utils/ScrollBarWidth.ts";
import Switch from "react-switch";

Expand All @@ -37,16 +37,15 @@ export function ProjectTeacherComponent(props: {
const [deadline, setDeadline] = useState<Value>(props.project.deadline);
const [description, setDescription] = useState(props.project.description);
const [max_students, setMaxStudents] = useState(props.project.maxGroupMembers);
const [groups, setGroups] = useState(props.project.amount_groups);
const [requiredFiles, setRequiredFiles] = useState(props.project.requiredFiles);
const [visible, setVisible] = useState(props.project.visible)
const [archived, setArchived] = useState(props.project.archived)
const [success, isSuccess] = useState<boolean | undefined>(undefined)

// Deze wordt niet gebruikt. Dit zit verwerkt in het json-object als OnlyPresentConstraint.
// const [otherFilesAllow, setOtherFilesAllow] = useState(props.project.otherFilesAllow);
const [groupProject, setGroupProject] = useState(props.project.groupProject);
const [dockerString, setDockerString] = useState(props.project.dockerFile);

// helpers
const [showGroup, setGroup] = useState(props.project.groupProject);
const [dockerFileName, setDockerFileName] = useState("original_docker_file");
Expand All @@ -63,7 +62,8 @@ export function ProjectTeacherComponent(props: {
value9: groupProject,
value10: dockerString,
value11: visible,
value12: archived
value12: archived,
value13: groups
});

function allowSaveButton(): boolean {
Expand All @@ -77,7 +77,8 @@ export function ProjectTeacherComponent(props: {
_.isEqual(groupProject, initialValues.value9) &&
_.isEqual(dockerString, initialValues.value10) &&
_.isEqual(visible, initialValues.value11) &&
_.isEqual(archived, initialValues.value12);
_.isEqual(archived, initialValues.value12) &&
_.isEqual(groups, initialValues.value13);
const second_part_1 = (deadline as Date).toDateString();
const second_part_2 = (initialValues.value5 as Date).toDateString();
const second_part = _.isEqual(second_part_1, second_part_2);
Expand All @@ -88,6 +89,9 @@ export function ProjectTeacherComponent(props: {
const expandGroup = (checked: boolean) => {
setGroup(!showGroup);
setGroupProject(checked);
if (!checked) {
setMaxStudents(1)
}
};

const course_options = props.project.all_courses.map(course => course.course_name);
Expand Down Expand Up @@ -170,8 +174,14 @@ export function ProjectTeacherComponent(props: {

if (props.project.projectId == -1) {
// Create new project
console.log(props.project.projectId)
console.log(groups)
console.log(max_students)
const new_project: Project = await course_create_project(course.course_id, projectInput)
await new Promise(resolve => setTimeout(resolve, 500))
for (let i= 0; i < groups; i += 1){
void project_create_group(new_project.project_id)
}

navigate(`/teacher/project/${new_project.project_id}`)
} else {
// Update project
Expand All @@ -190,7 +200,8 @@ export function ProjectTeacherComponent(props: {
value9: groupProject,
value10: dockerString,
value11: visible,
value12: archived
value12: archived,
value13: groups
});
}
if (props.updateTitle) {
Expand All @@ -205,7 +216,6 @@ export function ProjectTeacherComponent(props: {
useEffect(() => {
const scrollbarWidth = getScrollbarWidth();
setWidth(`calc(100vw - var(--sidebar-width) - ${scrollbarWidth}px)`);
console.log(scrollbarWidth)
}, []);

return (
Expand Down Expand Up @@ -391,7 +401,7 @@ export function ProjectTeacherComponent(props: {
</div>

{/* Archived FIELD*/}
{props.project.projectId != -1 && <div className="field is-horizontal">
{props.project.projectId !== -1 && <div className="field is-horizontal">
<div className="field-label">
<label className="label">{t('create_project.archived')}</label>
</div>
Expand All @@ -405,21 +415,19 @@ export function ProjectTeacherComponent(props: {
</div>
</div>}


{/* TEAMWORK FIELD */}
<div className="field is-horizontal">
{props.project.projectId === -1 && <div className="field is-horizontal">
<div className="field-label">
<label className="label">{t('create_project.teamwork.tag')}</label>
</div>
<div className="field-body is-fullwidth is-align-content-center teamwork">

<div className="field-body is-fullwidth is-align-content-center teamwork">
<Switch
type="checkbox"
onColor="#006edc"
checked={groupProject}
onChange={e => expandGroup(e)}
/>
{showGroup &&
{showGroup && <>
<div className="field is-horizontal">
<label
className="mr-3 is-align-content-center">{t('project.groupmembers.amount_of_members')}</label>
Expand All @@ -428,13 +436,81 @@ export function ProjectTeacherComponent(props: {
className={"input is-rounded"}
type="number"
value={max_students}
onChange={e => setMaxStudents(parseInt(e.target.value))}
onChange={e => {
const newValue = parseInt(e.target.value)
if (newValue > 0) {
setMaxStudents(newValue)
}
}}
/>
</div>

<div className="field is-horizontal">
<label
className="mr-3 is-align-content-center">{t('create_project.amount_of_groups')}
</label>
<input
style={{width: "75px"}}
className={"input is-rounded"}
type="number"
value={groups}
onChange={e => {
const newValue = parseInt(e.target.value)
if (newValue > 0) {
setGroups(newValue)
}
}}
/>
</div>
</>
}

{!showGroup && <>
<div className="field is-horizontal">
<label
className="mr-3 is-align-content-center">{t('create_project.amount_of_students')}</label>
<input
style={{width: "75px"}}
className={"input is-rounded"}
type="number"
value={groups}
onChange={e => {
const newValue = parseInt(e.target.value)
if (newValue > 0) {
setGroups(newValue)
}
}}
/>
</div>
</>
}
</div>
</div>
</div>}

{props.project.projectId !== -1 && <div className="field is-horizontal">
{!showGroup && <div className="field-label">
<label className="label">{t('create_project.amount_of_students')}</label>
</div>}

{showGroup && <div className="field-label">
<label className="label">{t('create_project.amount_of_groups')}</label>
<label className="label">{t('project.groupmembers.amount_of_members')}</label>
</div>}

<div className="field-body is-fullwidth is-align-content-center">
{!showGroup && <div className="field-label">
<label className="label">{groups}</label>
</div>}

{showGroup && <div className="field-label">
<label className="label">{groups}</label>
<label className="label">{max_students}</label>
</div>}
</div>
</div>}

</div>
</div>
);
)
;
}
40 changes: 1 addition & 39 deletions frontend/src/dataloaders/ProjectsStudentLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
teacherStudentRole,
User
} from "../utils/ApiInterfaces.ts";
import {getAllProjectsAndCourses} from "./loader_helpers/SharedFunctions.ts";
import {getAllProjectsAndCourses, getGroupInfo} from "./loader_helpers/SharedFunctions.ts";
import apiFetch from "../utils/ApiFetch.ts";
import {Backend_group, Backend_submission, Backend_user} from "../utils/BackendInterfaces.ts";
import {mapGroup, mapSubmission, mapUser} from "../utils/ApiTypesMapper.ts";
Expand All @@ -30,44 +30,6 @@ export interface GroupInfo {
visible_id: number
}

export async function getGroupInfo(project_id: number): Promise<GroupInfo[] | undefined> {
const groups = await apiFetch<GroupInfo[]>(`/projects/${project_id}/groups`)
if (!groups.ok) {
return undefined
}
return groups.content
}

export async function loadGroupMembers(project_id: number) {
const groupIdData = await apiFetch<{ id: number }>(`/projects/${project_id}/group`)
if (!groupIdData.ok) {
return undefined;
}
const groupId: number = groupIdData.content.id;

const submissionData = await apiFetch<Backend_submission>(`/groups/${groupId}/submission`)
let submission: string = "";
let lastSubmissionId = -1;
if (submissionData.ok) {
submission = submissionData.content.filename.split('/').reverse()[0]
lastSubmissionId = submissionData.content.student_id;
}

const groupMembersData = await apiFetch<[Backend_user]>(`/groups/${groupId}/members`);
if (!groupMembersData.ok) {
return undefined
}
const groupMembersApi = groupMembersData.content
const groupMembers = groupMembersApi.map((user) => {
return {
name: user.name,
email: user.email,
lastSubmission: user.id == lastSubmissionId
}
})
return {members: groupMembers, id: groupId, submission: submission}
}

export default async function projectsStudentLoader(): Promise<projectsStudentLoaderObject> {
const projects: CompleteProjectStudent[] = await LoadProjectsForStudent();
return {projects};
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/dataloaders/ProjectsTeacherLoader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {CompleteProjectTeacher, Group, SUBMISSION_STATE, teacherStudentRole} from "../utils/ApiInterfaces.ts";
import {getAllProjectsAndCourses} from "./loader_helpers/SharedFunctions.ts";
import {getAllProjectsAndCourses, getGroupInfo} from "./loader_helpers/SharedFunctions.ts";
import apiFetch from "../utils/ApiFetch.ts";
import {Backend_group, Backend_submission} from "../utils/BackendInterfaces.ts";
import {mapGroupList, mapSubmission} from "../utils/ApiTypesMapper.ts";
Expand Down Expand Up @@ -70,17 +70,19 @@ export async function LoadProjectsForTeacher(filter_on_current: boolean = false,
}
amount_of_submissions.push(amount);
}
return projects.map((project, index) => {
return await Promise.all(projects.map(async (project, index) => {
const course = courses.find(course => course.course_id === project.course_id);
if (!course) {
throw Error("Course not found for project.");
}
const groups_info = await getGroupInfo(project.project_id);
return {
...project,
...course,
courses: courses,
groups_info: groups_info,
submission_amount: amount_of_submissions[index],
submission_statistics: statistics
}
})
}))
}
Loading

0 comments on commit 93eaefd

Please sign in to comment.