diff --git a/compose.yaml b/compose.yaml index a6f946f..42bcd33 100644 --- a/compose.yaml +++ b/compose.yaml @@ -22,6 +22,10 @@ services: restart: false condition: service_healthy required: true + checker: + restart: false + condition: service_healthy + required: true env_file: - path: ./infrastructure/backend/.env.template required: true @@ -384,6 +388,10 @@ services: restart: false condition: service_completed_successfully required: true + minio: + restart: false + condition: service_healthy + required: true env_file: - path: ./infrastructure/checker/.env.template required: true diff --git a/services/backend/api/v1/task/views.py b/services/backend/api/v1/task/views.py index 137e73e..e0f83cc 100644 --- a/services/backend/api/v1/task/views.py +++ b/services/backend/api/v1/task/views.py @@ -117,7 +117,10 @@ def submit_task( return status.FORBIDDEN, ForbiddenError() if task.type == CompetitionTask.CompetitionTaskType.INPUT: - verdict = content.read() == task.correct_answer_file.read() + user_input = content.read() + correct_answer = task.correct_answer_file.read() + verdict = user_input == correct_answer + print(user_input, correct_answer) submission = CompetitionTaskSubmission.objects.create( user=user, task=task, @@ -126,7 +129,7 @@ def submit_task( result={ "correct": verdict }, - earned_points=task.points + earned_points=task.points if verdict else 0 ) if task.type == CompetitionTask.CompetitionTaskType.REVIEW: submission = CompetitionTaskSubmission.objects.create( diff --git a/services/backend/apps/task/migrations/0003_alter_competitiontaskattachment_task.py b/services/backend/apps/task/migrations/0003_alter_competitiontaskattachment_task.py new file mode 100644 index 0000000..a6208ba --- /dev/null +++ b/services/backend/apps/task/migrations/0003_alter_competitiontaskattachment_task.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.6 on 2025-03-03 15:11 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('task', '0002_remove_competitiontasksubmission_plagiarism_checked_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='competitiontaskattachment', + name='task', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='task.competitiontask', verbose_name='задание'), + ), + ] diff --git a/services/backend/apps/task/models.py b/services/backend/apps/task/models.py index 77123b2..a23b315 100644 --- a/services/backend/apps/task/models.py +++ b/services/backend/apps/task/models.py @@ -111,7 +111,10 @@ class CompetitionTaskAttachment(BaseModel): return f"attachments/{instance.id}/file/{filename}" task = models.ForeignKey( - CompetitionTask, on_delete=models.CASCADE, verbose_name="задание" + CompetitionTask, + on_delete=models.CASCADE, + verbose_name="задание", + related_name="attachments", ) file = models.FileField(upload_to=file_upload_at, verbose_name="файл") bind_at = models.CharField(verbose_name="путь сохранения", max_length=255) diff --git a/services/backend/apps/task/tasks.py b/services/backend/apps/task/tasks.py index b68ecf2..0b866f9 100644 --- a/services/backend/apps/task/tasks.py +++ b/services/backend/apps/task/tasks.py @@ -21,12 +21,12 @@ def analyze_data_task(self, submission_id): { "url": ( f"{settings.MINIO_DEFAULT_CUSTOM_ENDPOINT_URL}/" - f"{urlparse(submission.content.url).path}" + f"{urlparse(attachment.file.url).path}" ), "bind_path": attachment.bind_at, } for attachment in submission.task.attachments.filter( - bind_path__isnull=False + bind_at__isnull=False ) ] @@ -37,7 +37,7 @@ def analyze_data_task(self, submission_id): "code_url": code_url, "answer_file_path": submission.task.answer_file_path, "expected_hash": hashlib.sha256( - submission.task.correct_answer_file.read().encode() + submission.task.correct_answer_file.read() ).hexdigest(), }, timeout=30, diff --git a/services/frontend/src/components/layout/header.tsx b/services/frontend/src/components/layout/header.tsx index 27b0421..fc74d70 100644 --- a/services/frontend/src/components/layout/header.tsx +++ b/services/frontend/src/components/layout/header.tsx @@ -1,5 +1,5 @@ import { DataRush } from "@/components/ui/icons/datarush"; -import { ChevronDown } from "lucide-react"; +import { ChevronDown, FileText } from "lucide-react"; import { Link, useNavigate } from "react-router"; import { useUserStore } from "@/shared/stores/user"; import { @@ -14,6 +14,13 @@ import { removeToken } from "@/shared/token"; export const Header = () => { const navigate = useNavigate(); const user = useUserStore((state) => state.user); + const clearUser = useUserStore((state) => state.clearUser); + + const handleLogout = () => { + removeToken(); + clearUser(); + navigate("/login"); + }; return (
@@ -21,33 +28,39 @@ export const Header = () => { - - - - - - - Аккаунт - - - - { - removeToken(); - navigate("/login"); - }} - > - Выйти - - - + +
+ + + + + + + + + + + Аккаунт + + + + Выйти + + + +
); -}; +}; \ No newline at end of file diff --git a/services/frontend/src/pages/CompetitionSession/index.tsx b/services/frontend/src/pages/CompetitionSession/index.tsx index fd8ec50..42ea9c4 100644 --- a/services/frontend/src/pages/CompetitionSession/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/index.tsx @@ -1,5 +1,5 @@ -import { useState } from "react"; -import { useParams, Navigate } from "react-router-dom"; +import { useState, useEffect } from "react"; +import { useParams, Navigate, useNavigate } from "react-router-dom"; import CompetitionHeader from "./components/CompetitionHeader"; import TaskContent from "./components/TaskContent"; import TaskSolution from "./modules/TaskSolution"; @@ -13,8 +13,10 @@ const CompetitionSession = () => { const { id, taskId } = useParams<{ id: string; taskId?: string }>(); const [answer, setAnswer] = useState(""); const [selectedFile, setSelectedFile] = useState(null); + const [isReloading, setIsReloading] = useState(false); const competitionId = id || ""; const queryClient = useQueryClient(); + const navigate = useNavigate(); const competitionQuery = useQuery({ queryKey: ["competition", competitionId], @@ -45,8 +47,12 @@ const CompetitionSession = () => { queryKey: ['solutionHistory', competitionId, taskId] }); - setAnswer(""); - setSelectedFile(null); + setIsReloading(true); + + setTimeout(() => { + window.location.reload() + setIsReloading(false); + }, 2500); }, onError: (error) => { console.error("Error submitting solution:", error); @@ -89,6 +95,13 @@ const CompetitionSession = () => { const competitionTitle = competition?.title || "Загрузка соревнования..."; + useEffect(() => { + setAnswer(""); + setSelectedFile(null); + }, [taskId]); + + const isSubmitting = submitMutation.isPending || isReloading; + return (
{ selectedFile={selectedFile} setSelectedFile={setSelectedFile} onSubmit={handleSubmit} - isSubmitting={submitMutation.isPending} + isSubmitting={isSubmitting} />
) : ( diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx index cd7906b..049345b 100644 --- a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx @@ -27,7 +27,7 @@ const TaskSolution: React.FC = ({ selectedFile, setSelectedFile, onSubmit, - isSubmitting = false + isSubmitting = false, }) => { const fileInputRef = useRef(null); const [isHistoryOpen, setIsHistoryOpen] = useState(false); @@ -70,14 +70,6 @@ const TaskSolution: React.FC = ({ } }, [task.id, solutionHistory]); - // useEffect(() => { - // if (solutionHistory.length > 0 && - // (!displayedSolution || - // (solutionHistory[solutionHistory.length - 1].id !== displayedSolution.id))) { - // setDisplayedSolution(solutionHistory[solutionHistory.length - 1]); - // } - // }, [solutionHistory, displayedSolution]); - useEffect(() => { const loadSolutionContent = async () => { if (!displayedSolution || !displayedSolution.content) return; @@ -122,9 +114,6 @@ const TaskSolution: React.FC = ({
{displayedSolution ? ( <> -
- Результат последней посылки: -
) : ( diff --git a/services/frontend/src/pages/Review/modules/review-header.tsx b/services/frontend/src/pages/Review/modules/review-header.tsx index d27e9e8..a7e14c5 100644 --- a/services/frontend/src/pages/Review/modules/review-header.tsx +++ b/services/frontend/src/pages/Review/modules/review-header.tsx @@ -1,13 +1,22 @@ import { buttonVariants } from "@/components/ui/button"; import { DataRushReview } from "@/components/ui/icons/datarush-review"; import { Reviewer } from "@/shared/types/review"; -import { Link } from "react-router"; +import { useUserStore } from "@/shared/stores/user"; +import { useNavigate } from "react-router-dom"; interface ReviewHeaderProps { reviewer: Reviewer; } export const ReviewHeader = ({ reviewer }: ReviewHeaderProps) => { + const clearUser = useUserStore((state) => state.clearUser); + const navigate = useNavigate(); + + const handleLogout = () => { + clearUser(); + navigate("/"); + }; + return (
@@ -15,13 +24,13 @@ export const ReviewHeader = ({ reviewer }: ReviewHeaderProps) => {

{reviewer.name} {reviewer.surname}

- Выйти - +
); -}; +}; \ No newline at end of file