Skip to content

Commit

Permalink
fix: small submission fixes (#465)
Browse files Browse the repository at this point in the history
* fix translations, fix links, fix errors

* fix: delete garbage

* fix: small changes

* chore: fixing submission

* fix: submission permissions

* chore: linting

* feat: notifications frontend (#460)

* chore: notifications (wip)

* chore: notifications (wip)

* feat: notifications in frontend

* chore: added files

* chore: notification creation

* fix: fixed some weird shizzles

* chore: notifications

* chore: disabled load more button when no notifications

* chore: linting

* chore: removed vitepress cache

* fix: score creation

* chore: linting

---------

Co-authored-by: Topvennie <vincent@vallaeys.com>

* fix: translations

---------

Co-authored-by: lander <landermaes@outlook.com>
Co-authored-by: Topvennie <vincent@vallaeys.com>
Co-authored-by: Bram Meir <bram.meir@ugent.be>
  • Loading branch information
4 people authored May 23, 2024
1 parent be49ee1 commit 6f47fe0
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 109 deletions.
23 changes: 22 additions & 1 deletion backend/api/permissions/group_permissions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import cast

from api.models.assistant import Assistant
from api.models.group import Group
from api.models.project import Project
Expand Down Expand Up @@ -65,7 +67,26 @@ def has_object_permission(self, request: Request, view: ViewSet, group) -> bool:
class GroupSubmissionPermission(BasePermission):
"""Permission class for submission related group endpoints"""

def had_object_permission(self, request: Request, _: ViewSet, group: Group) -> bool:
def has_permission(self, request: Request, view: APIView) -> bool:
user = cast(User, request.user)
group_id = view.kwargs.get('pk')

if group_id is None:
return False

group: Group | None = Group.objects.get(id=group_id)

if group is None:
return True

# Get the individual permissions.
teacher_permission = group.project.course.teachers.filter(id=user.id).exists()
assistant_permission = group.project.course.assistants.filter(id=user.id).exists()
student_permission = group.students.filter(id=user.id).exists()

return teacher_permission or assistant_permission or student_permission

def had_object_permission(self, request: Request, view: ViewSet, group: Group) -> bool:
"""Check if user has permission to view a detailed group submission endpoint"""
user = request.user
course = group.project.course
Expand Down
5 changes: 1 addition & 4 deletions backend/api/views/group_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ def _add_submission(self, request: Request, **_):
if serializer.is_valid(raise_exception=True):
serializer.save(group=group)

return Response({
"message": gettext("group.success.submissions.add"),
"submission": serializer.data
})
return Response(serializer.data)

@students.mapping.post
@students.mapping.put
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/assets/lang/app/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,17 @@
"title": "Submissions",
"submit": "Submit",
"course": "Course",
"allSubmissions": "Alle indieningen",
"file": "file",
"files": "files",
"chooseFile": "Choose a file",
"noSubmissions": "No submissions available",
"passed": "Passed",
"noFiles": "No files selected.",
"passed": "All tests passed",
"failed": "The submission is not passed.",
"downloadZip": "Download all files of this submission as a zip.",
"downloadLog": "Log of check {0}",
"backToSubmissions": "Back to submissions",
"hoverText": {
"allChecksFailed": "Structure and extra checks failed",
"allChecksPassed": "All checks passed",
Expand All @@ -152,7 +155,7 @@
"dockerImageError": "Error in Docker image",
"timeLimit": "Time limit exceeded",
"memoryLimit": "Memory limit exceeded",
"checkError": "Check error",
"checkError": "Extra tests on your submission did not pass.",
"runtimeError": "Runtime error",
"unknown": "Unknown error",
"failedStructureCheck": "Structure check failed"
Expand Down Expand Up @@ -233,7 +236,7 @@
"card": {
"open": "Details",
"newProject": "New project",
"noSubmissions": "This project does not have any submissions \uD83D\uDE2D",
"noSubmissions": "This project does not have any submissions",
"submissions": "Group has made a submission | Groups have made a submission",
"groups": "Participating group | Participating groups",
"structureTestsFail": "Failed structure tests",
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/assets/lang/app/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,19 @@
},
"submissions": {
"title": "Indieningen",
"allSubmissions": "Alle indieningen",
"submit": "Indienen",
"course": "Vak",
"file": "Bestand",
"files": "Bestanden",
"chooseFile": "Kies bestand(en)",
"noSubmissions": "Geen indieningen beschikbaar",
"passed": "Geslaagd",
"failed": "De indiening is mislukt",
"noSubmissions": "Geen indieningen beschikbaar.",
"noFiles": "Geen bestanden geselecteerd.",
"passed": "Alle testen zijn geslaagd.",
"failed": "De indiening is mislukt.",
"downloadZip": "Download alle bestanden als zip.",
"downloadLog": "Log bestand van check {0}",
"backToSubmissions": "Terug naar indieningen",
"hoverText": {
"allChecksFailed": "Structuur en extra checks gefaald",
"allChecksPassed": "Alle checks geslaagd",
Expand All @@ -152,7 +155,7 @@
"DockerImageError": "Fout in Docker-afbeelding",
"timeLimit": "Tijdslimiet overschreden",
"memoryLimit": "Geheugenlimiet overschreden",
"checkError": "Controlefout",
"checkError": "Extra checks in je indiening zijn niet geslaagd.",
"runtimeError": "Runtime fout",
"unknown": "Onbekende fout",
"failedStructureCheck": "Structuurcontrole mislukt"
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/submissions/AllSubmission.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { type ExtraCheckResult } from '@/types/submission/ExtraCheckResult.ts';
import { type StructureCheckResult } from '@/types/submission/StructureCheckResult.ts';
import { useI18n } from 'vue-i18n';
import router from '@/router/router.ts';
import { useRoute } from 'vue-router';
const { params } = useRoute();
const { t } = useI18n();
const props = defineProps<{
Expand Down Expand Up @@ -83,7 +85,10 @@ const timeSince = (submissionDate: Date): string => {
* @param submissionId
*/
const navigateToSubmission = (submissionId: string): void => {
router.push({ name: 'submission', params: { submissionId, groupId: '0', projectId: '0', courseId: '0' } });
router.push({
name: 'submission',
params: { submissionId, groupId: params.groupId, projectId: params.projectId, courseId: params.courseId },
});
};
</script>

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/composables/services/submission.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ export function useSubmission(): SubmissionState {
selfProcessError: boolean = true,
): Promise<void> {
const endpoint = endpoints.submissions.byGroup.replace('{groupId}', groupId);

// formData is necessary with multiform data (otherwise files value is changed to files[] by axios)
const formData = new FormData();
uploadedFiles.forEach((file: File) => {
formData.append('files', file); // Gebruik 'files' in plaats van 'files[]'
formData.append('files', file);
});

await create(endpoint, formData, submission, Submission.fromJSON, 'multipart/form-data', selfProcessError);
}

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/types/submission/ExtraCheckResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface ExtraCheckResultJSON {
error_message: string | null;
submission: number;
structure_check: number;
extra_check: number;
extra_check: string;
resourcetype: string;
log_file: HyperlinkedRelation;
artifact: HyperlinkedRelation;
Expand All @@ -20,7 +20,7 @@ export class ExtraCheckResult {
public log_file: string = '',
public artifact: string = '',
public submission: number = 0,
public extra_check: number = 0,
public extra_check: string = '',
public resourcetype: string = '',
) {}

Expand Down
12 changes: 10 additions & 2 deletions frontend/src/types/submission/Submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class Submission {
public is_valid: boolean = false,
public extraCheckResults: ExtraCheckResult[] = [],
public structureCheckResults: StructureCheckResult[] = [],
public zip: File | null = null,
public zip: HyperlinkedRelation = '',
) {}

/**
Expand Down Expand Up @@ -67,14 +67,21 @@ export class Submission {
}

/**
* Geeft de juiste vertaling voor alle extra check fouten
* Get the error messages for the submission.
*
* @returns string[]
*/
public extraCheckFaults(): string[] {
return this.extraCheckResults.map((check: ExtraCheckResult) =>
this.getErrorMessageEnumKey(check.error_message as string),
);
}

/**
* Get the error message from the error message enum.
*
* @param key
*/
private getErrorMessageEnumKey(key: string): string {
if (key != null) {
return ErrorMessageEnum[key as keyof typeof ErrorMessageEnum];
Expand Down Expand Up @@ -108,6 +115,7 @@ export class Submission {
submission.is_valid,
extraCheckResults,
structureCheckResults,
submission.zip,
);
}
}
29 changes: 20 additions & 9 deletions frontend/src/views/submissions/SubmissionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import moment from 'moment/moment';
import { PrimeIcons } from 'primevue/api';
import { useSubmission } from '@/composables/services/submission.service.ts';
import { useProject } from '@/composables/services/project.service.ts';
import { useExtraCheck } from '@/composables/services/extra_checks.service.ts';
/* Composable injections */
const route = useRoute();
Expand All @@ -23,6 +24,7 @@ const { user } = storeToRefs(useAuthStore());
const { submission, getSubmissionByID } = useSubmission();
const { feedbacks, getFeedbackBySubmission, createFeedback, updateFeedback } = useFeedback();
const { project, getProjectByID } = useProject();
const { extraChecks, getExtraChecksByProject } = useExtraCheck();
/* Feedback content */
const feedbackTextValue = ref<string>('');
Expand Down Expand Up @@ -55,23 +57,23 @@ const structureAndExtraFaults = computed(() => {
const structureFaults = submission.value.structureCheckFaults();
const extraFaults = submission.value.isStructureCheckPassed() ? submission.value.extraCheckFaults() : [];
return [...structureFaults, ...extraFaults];
// Filter out empty faults
return [...structureFaults, ...extraFaults].filter((fault) => fault);
});
const showLogsAndArtifacts = computed(() => {
const extraChecks = submission.value?.extraCheckResults ?? [];
const extraChecksResults = submission.value?.extraCheckResults ?? [];
let results: string[] = [];
if (project.value != null) {
const visibleLogs = extraChecks
if (project.value != null && extraChecks.value != null) {
const visibleLogs = extraChecksResults
.filter((check) => {
return project.value?.extra_checks?.find((extraCheck) => extraCheck.id === check.id)?.show_log;
return extraChecks.value?.find((extraCheck) => extraCheck.id === check.extra_check)?.show_log;
})
.map((check) => check.log_file);
const visibleArtifacts = extraChecks
const visibleArtifacts = extraChecksResults
.filter((check) => {
return project.value?.extra_checks?.find((extraCheck) => extraCheck.id === check.id)?.show_log;
return extraChecks.value?.find((extraCheck) => extraCheck.id === check.extra_check)?.show_artifact;
})
.map((check) => check.artifact);
Expand All @@ -98,20 +100,29 @@ watch(
getSubmissionByID(submissionId as string);
getFeedbackBySubmission(submissionId as string);
getProjectByID(route.params.projectId as string);
getExtraChecksByProject(route.params.projectId as string);
},
{ immediate: true },
);
</script>

<template>
<BaseLayout>
<RouterLink :to="{ name: 'submissions' }">
<Button
class="mb-4 p-0 text-sm text-black-alpha-70"
:icon="PrimeIcons.ARROW_LEFT"
:label="t('views.submissions.backToSubmissions')"
link
/>
</RouterLink>
<div class="grid">
<!-- Submission properties -->
<div v-if="submission" class="col-6 md:col-4">
<!-- Submission status -->
<div>
<Title class="flex">Status</Title>
<div class="p-2">
<div class="pl-2 mt-4">
<p v-if="submission.isPassed()">{{ t('views.submissions.passed') }}</p>
<div v-else>
<span>{{ t('views.submissions.failed') }}</span>
Expand Down
Loading

0 comments on commit 6f47fe0

Please sign in to comment.