Skip to content

Commit

Permalink
Merge branch 'dev' into submit-file-ext
Browse files Browse the repository at this point in the history
  • Loading branch information
pieterjanin authored May 18, 2024
2 parents 83e4046 + d301892 commit b0e54a0
Show file tree
Hide file tree
Showing 54 changed files with 1,215 additions and 528 deletions.
5 changes: 3 additions & 2 deletions backend/src/project/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ async def list_groups(groups: GroupList = Depends(retrieve_groups_by_project)):


@router.get("/{project_id}/submissions", dependencies=[Depends(patch_permission_validation)])
async def list_submissions(group_id: int,
async def list_submissions(project_id: int,
db: AsyncSession = Depends(get_async_db)
) -> Sequence[Submission]:
return await get_submissions_by_project(db, group_id)
"""Return a list of the latest submission of each group of this project"""
return await get_submissions_by_project(db, project_id)


@router.get("/{project_id}/test_files")
Expand Down
10 changes: 8 additions & 2 deletions backend/src/submission/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Sequence

from fastapi import APIRouter, Depends, BackgroundTasks
from fastapi.responses import FileResponse
from fastapi.responses import FileResponse, StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession

from src.dependencies import get_async_db
Expand All @@ -15,7 +15,7 @@
)
from src.submission.exceptions import FileNotFound
from src.submission.exceptions import FilesNotFound
from src.submission.utils import upload_files, remove_files
from src.submission.utils import upload_files, remove_files, zip_stream
from src.user.dependencies import admin_user_validation, get_authenticated_user
from src.user.schemas import User
from . import service
Expand Down Expand Up @@ -94,6 +94,12 @@ async def get_file(path: str, submission: Submission = Depends(retrieve_submissi
return FileResponse(path=path)


@router.get("/{submission_id}/zip", response_class=StreamingResponse)
async def get_all_files(submission: Submission = Depends(retrieve_submission)):
path = submission_path(submission.files_uuid, "")
return StreamingResponse(zip_stream(path, submission.group_id), media_type="application/zip")


@router.get("/{submission_id}/artifacts", response_model=list[File])
async def get_artifacts(submission: Submission = Depends(retrieve_submission)):
if submission.status == Status.InProgress:
Expand Down
9 changes: 7 additions & 2 deletions backend/src/submission/service.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Sequence, List
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from sqlalchemy import func

from . import models, schemas
from .models import Status
Expand All @@ -12,9 +13,13 @@ async def get_submissions(db: AsyncSession) -> Sequence[models.Submission]:

async def get_submissions_by_project(db: AsyncSession,
project_id: int) -> Sequence[models.Submission]:
# SQL for the win
subquery = select(models.Submission.group_id, func.max(models.Submission.date).label(
'max_date')).group_by(models.Submission.group_id).subquery()
query = select(models.Submission).join(subquery, (models.Submission.group_id == subquery.c.group_id) & (
models.Submission.date == subquery.c.max_date) & (models.Submission.project_id == project_id))

return (await db.execute(select(models.Submission).
filter_by(project_id=project_id))).unique().scalars().all()
return (await db.execute(query)).unique().scalars().all()


async def get_submissions_by_group(db: AsyncSession,
Expand Down
13 changes: 13 additions & 0 deletions backend/src/submission/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
import shutil
import zipfile
import pathlib
import io
import fnmatch
from uuid import uuid4

Expand Down Expand Up @@ -45,5 +47,16 @@ def upload_files(files: list[UploadFile], project: Project) -> str:
return uuid


def zip_stream(path, group_id: int):
base_path = pathlib.Path(path)
data = io.BytesIO()
with zipfile.ZipFile(data, mode='w') as z:
for f_name in base_path.iterdir():
name = f"group_{group_id}/{str(f_name).replace(path, "")}"
z.write(f_name, arcname=name)
data.seek(0)
yield from data


def remove_files(uuid: str):
shutil.rmtree(submissions_path(uuid))
1 change: 0 additions & 1 deletion frontend/src/components/BackgroundContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
padding: 20px;
height: 100vh;
border: 25px solid white;
border-radius: 50px;
background-color: var(--color-secondary);
}
</style>
9 changes: 9 additions & 0 deletions frontend/src/components/buttons/HeaderSubtitleButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,19 @@ defineProps<{
</script>

<style scoped>
.not-clickable {
cursor: default;
pointer-events: none;
}
.subtitle-button {
margin-right: 10px;
}
.subtitle-button-active {
background-color: #9fb6ff;
}
.button-text {
color: #003eff;
font-weight: 1000;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/home/listcontent/DeadlineItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ import type Project from "@/models/Project";
import type Submission from "@/models/Submission";
import { Status } from "@/models/Submission";
import { useSubjectQuery } from "@/queries/Subject";
import { useProjectSubmissionsQuery } from "@/queries/Submission";
import { useUserProjectSubmissionsQuery } from "@/queries/Submission";
const props = defineProps<{
project: Project;
}>();
const { project } = toRefs(props);
const { data: submissions } = useProjectSubmissionsQuery(project.value.id);
const { data: submissions } = useUserProjectSubmissionsQuery(project.value.id);
const latestSubmissionStatus = computed(() => {
if (!submissions.value || submissions.value.length === 0) return null;
Expand Down
24 changes: 21 additions & 3 deletions frontend/src/components/project/ProjectInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,26 @@
</v-card-title>
<div v-html="renderQuillContent(project.description)"></div>
</v-card-item>
<v-card-actions>
<v-btn color="blue" v-if="isTeacher" :to="`/project/${project.id}/submissions`">
{{ $t("project.submissions_list_teacher") }}
</v-btn>
</v-card-actions>
</v-card>
<SubmitInfo class="submitInfo" v-if="group" :project="project" :group="group" />
<SubmitInfo
class="submitInfo"
v-if="group && !isTeacher"
:project="project"
:group="group"
/>
</v-container>
</template>

<script setup lang="ts">
import type Project from "@/models/Project";
import type Group from "@/models/Group";
import SubmitInfo from "@/components/submission/SubmitInfo.vue";
import { toRefs } from "vue";
import { toRefs, computed } from "vue";
import { Quill } from "@vueup/vue-quill";
import type User from "@/models/User";
import type Subject from "@/models/Subject";
Expand All @@ -46,10 +56,18 @@ const props = defineProps<{
project: Project;
group: Group | null;
instructors: User[];
user: User;
subject: Subject;
}>();
const { project, group, instructors, subject } = toRefs(props);
const { project, group, instructors, subject, user } = toRefs(props);
const isTeacher = computed(
() =>
user.value.is_teacher ||
user.value.is_admin ||
instructors.value?.some((element) => element.uid == user.value.uid)
);
const renderQuillContent = (content: string) => {
const quill = new Quill(document.createElement("div"));
Expand Down

This file was deleted.

129 changes: 0 additions & 129 deletions frontend/src/components/subject/body/projects/SubjectProjectPage.vue

This file was deleted.

26 changes: 26 additions & 0 deletions frontend/src/components/subject/extra/SubjectIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<v-icon color="white" :size="size">
{{ `mdi-${userRole}` }}
</v-icon>
</template>

<script setup lang="ts">
import { SubjectRole } from "@/models/Subject";
import { computed, toRefs } from "vue";
const props = defineProps<{
role: SubjectRole;
size: string;
}>();
const { role } = toRefs(props);
const userRole = computed(() => {
if (role.value === SubjectRole.Instructor) return "account-tie";
if (role.value === SubjectRole.Student) return "school";
if (role.value === SubjectRole.Admin) return "security";
return "cancel";
});
</script>

<style scoped></style>
Loading

0 comments on commit b0e54a0

Please sign in to comment.