From a153940c4a0743f098de39541b8cc39071cabf8c Mon Sep 17 00:00:00 2001 From: rngsurrounded Date: Mon, 3 Mar 2025 06:26:49 +0900 Subject: [PATCH 1/3] fix: submission update logic --- .../components/SolutionHistorySheet/index.tsx | 24 +++--- .../modules/TaskSolution/index.tsx | 81 +++++++++++-------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx index 870d511..3f181bd 100644 --- a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Sheet, SheetClose, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; -import { X } from "lucide-react"; +import { X, Check } from "lucide-react"; import SolutionStatus from '../SolutionStatus'; -import { Solution, TaskType } from '@/shared/types/task'; +import { Solution } from '@/shared/types/task'; interface SolutionHistorySheetProps { isOpen: boolean; @@ -11,6 +11,7 @@ interface SolutionHistorySheetProps { solutions: Solution[]; maxPoints: number; onSolutionSelect: (solution: Solution) => void; + currentSolutionId?: string | null; } const SolutionHistorySheet: React.FC = ({ @@ -18,7 +19,8 @@ const SolutionHistorySheet: React.FC = ({ onOpenChange, solutions, maxPoints, - onSolutionSelect + onSolutionSelect, + currentSolutionId }) => { return ( @@ -36,15 +38,17 @@ const SolutionHistorySheet: React.FC = ({
{solutions.length > 0 ? ( - solutions.map((solution, index) => ( + solutions.map((solution) => (
{ - onSolutionSelect(solution); - onOpenChange(false); - }} + key={solution.id} + className={`w-full cursor-pointer relative ${solution.id === currentSolutionId ? 'ring-2 ring-blue-500 rounded-lg' : ''}`} + onClick={() => onSolutionSelect(solution)} > + {solution.id === currentSolutionId && ( +
+ +
+ )}
)) diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx index e2262f9..1005d60 100644 --- a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx @@ -30,6 +30,8 @@ const TaskSolution: React.FC = ({ const fileInputRef = useRef(null); const [isHistoryOpen, setIsHistoryOpen] = useState(false); const [selectedSolutionUrl, setSelectedSolutionUrl] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [currentSolution, setCurrentSolution] = useState(null); const { id: competitionId } = useParams<{ id: string }>(); const solutionsQuery = useQuery({ @@ -39,18 +41,33 @@ const TaskSolution: React.FC = ({ }); const solutionHistory = solutionsQuery.data || []; - const latestSolution = solutionHistory && solutionHistory.length > 0 ? solutionHistory[0] : null; + // Set the current solution to the latest one when the task changes or new solutions arrive useEffect(() => { - const loadLatestSolution = async () => { - if (!latestSolution || !latestSolution.content) return; + if (solutionHistory.length > 0 && !currentSolution) { + setCurrentSolution(solutionHistory[0]); + } + }, [solutionHistory, currentSolution, task.id]); + + // Reset current solution when task changes + useEffect(() => { + setCurrentSolution(null); + setSelectedSolutionUrl(null); + setAnswer(""); // Reset answer when task changes + }, [task.id, setAnswer]); + + // Load the current solution content when it changes + useEffect(() => { + const loadSolutionContent = async () => { + if (!currentSolution || !currentSolution.content) return; + setIsLoading(true); try { if (task.type === TaskType.FILE) { setSelectedFile(null); - setSelectedSolutionUrl(latestSolution.content); + setSelectedSolutionUrl(currentSolution.content); } else { - const response = await fetch(latestSolution.content); + const response = await fetch(currentSolution.content); if (!response.ok) { throw new Error(`Failed to fetch solution content: ${response.status}`); } @@ -58,48 +75,47 @@ const TaskSolution: React.FC = ({ setAnswer(text); } } catch (error) { - console.error('Error loading latest solution content:', error); + console.error('Error loading solution content:', error); } finally { + setIsLoading(false); } }; - if (latestSolution && !solutionsQuery.isLoading && !solutionsQuery.isError) { - loadLatestSolution(); - } - }, [latestSolution, task.id, task.type, setAnswer, setSelectedFile]); + loadSolutionContent(); + }, [currentSolution, task.type, setAnswer, setSelectedFile]); const handleOpenHistory = () => { setIsHistoryOpen(true); }; - const handleSolutionSelect = async (solution: Solution) => { - if (!solution.content) return; - - try { - if (task.type === TaskType.FILE) { - setSelectedFile(null); - setSelectedSolutionUrl(solution.content); - } else { - const response = await fetch(solution.content); - if (!response.ok) { - throw new Error(`Failed to fetch solution content: ${response.status}`); - } - const text = await response.text(); - setAnswer(text); - } - } catch (error) { - console.error('Error loading solution content:', error); - } + const handleSolutionSelect = (solution: Solution) => { + setCurrentSolution(solution); + setIsHistoryOpen(false); }; const handleClearExistingFile = () => { setSelectedSolutionUrl(null); }; + // Handle successful submission + const handleSubmitSuccess = (newSolution: Solution) => { + // Update the current solution to the newly submitted one + setCurrentSolution(newSolution); + // Reset form + setAnswer(""); + setSelectedFile(null); + setSelectedSolutionUrl(null); + }; + + const handleSubmit = () => { + onSubmit(); + + }; + return (
- {latestSolution ? ( - + {currentSolution ? ( + ) : (
Решение еще не отправлено @@ -120,7 +136,7 @@ const TaskSolution: React.FC = ({ fileInputRef={fileInputRef} existingFileUrl={selectedSolutionUrl} onClearExistingFile={handleClearExistingFile} - isLoading={isInitialLoading} + isLoading={isLoading} /> )} @@ -132,7 +148,7 @@ const TaskSolution: React.FC = ({ )} @@ -142,6 +158,7 @@ const TaskSolution: React.FC = ({ solutions={solutionHistory} maxPoints={task.points} onSolutionSelect={handleSolutionSelect} + currentSolutionId={currentSolution?.id} />
); From 082d24acefeb8d5b94537a87770c604d3a0bf717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A1=D1=83=D0=BC?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Mon, 3 Mar 2025 00:30:17 +0300 Subject: [PATCH 2/3] fix: fixed data generation (generatuion sumbissions for all types of task) --- .../core/management/commands/generate_data.py | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/services/backend/apps/core/management/commands/generate_data.py b/services/backend/apps/core/management/commands/generate_data.py index 4d2a916..d6e6157 100644 --- a/services/backend/apps/core/management/commands/generate_data.py +++ b/services/backend/apps/core/management/commands/generate_data.py @@ -132,28 +132,29 @@ class Command(BaseCommand): def create_submissions(self, tasks, users): for task in tasks: - # Each task will get between 1 and 3 submissions - num_submissions = random.randint(1, 3) - for _ in range(num_submissions): - user = random.choice(users) - # Create a dummy content file - dummy_content = ContentFile( - b"Submission content", - name=f"submission_{uuid.uuid4().hex}.txt", - ) - submission = CompetitionTaskSubmission.objects.create( - user=user, - task=task, - earned_points=random.randint( - 0, task.points if task.points else 10 - ), - content=dummy_content, - ) - submission.save() - submission.send_on_review() - self.stdout.write( - f"Created submission for task '{task.title}' by user '{user.username}'" - ) + if task.type == CompetitionTask.CompetitionTaskType.REVIEW.value: + # Each task will get between 1 and 3 submissions + num_submissions = random.randint(1, 3) + for _ in range(num_submissions): + user = random.choice(users) + # Create a dummy content file + dummy_content = ContentFile( + b"Submission content", + name=f"submission_{uuid.uuid4().hex}.txt", + ) + submission = CompetitionTaskSubmission.objects.create( + user=user, + task=task, + earned_points=random.randint( + 0, task.points if task.points else 10 + ), + content=dummy_content, + ) + submission.save() + submission.send_on_review() + self.stdout.write( + f"Created submission for task '{task.title}' by user '{user.username}'" + ) def create_states(self, competitions, users): # For each competition, create a State for some of its participants From 67a3cfe5212aab78cc3389926d4b2aae7a005020 Mon Sep 17 00:00:00 2001 From: rngsurrounded Date: Mon, 3 Mar 2025 06:35:22 +0900 Subject: [PATCH 3/3] minor fixes --- .../modules/TaskSolution/index.tsx | 80 +++++++------------ 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx index 1005d60..fb8324b 100644 --- a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx @@ -30,8 +30,6 @@ const TaskSolution: React.FC = ({ const fileInputRef = useRef(null); const [isHistoryOpen, setIsHistoryOpen] = useState(false); const [selectedSolutionUrl, setSelectedSolutionUrl] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const [currentSolution, setCurrentSolution] = useState(null); const { id: competitionId } = useParams<{ id: string }>(); const solutionsQuery = useQuery({ @@ -41,33 +39,18 @@ const TaskSolution: React.FC = ({ }); const solutionHistory = solutionsQuery.data || []; + const latestSolution = solutionHistory && solutionHistory.length > 0 ? solutionHistory[0] : null; - // Set the current solution to the latest one when the task changes or new solutions arrive useEffect(() => { - if (solutionHistory.length > 0 && !currentSolution) { - setCurrentSolution(solutionHistory[0]); - } - }, [solutionHistory, currentSolution, task.id]); - - // Reset current solution when task changes - useEffect(() => { - setCurrentSolution(null); - setSelectedSolutionUrl(null); - setAnswer(""); // Reset answer when task changes - }, [task.id, setAnswer]); - - // Load the current solution content when it changes - useEffect(() => { - const loadSolutionContent = async () => { - if (!currentSolution || !currentSolution.content) return; + const loadLatestSolution = async () => { + if (!latestSolution || !latestSolution.content) return; - setIsLoading(true); try { if (task.type === TaskType.FILE) { setSelectedFile(null); - setSelectedSolutionUrl(currentSolution.content); + setSelectedSolutionUrl(latestSolution.content); } else { - const response = await fetch(currentSolution.content); + const response = await fetch(latestSolution.content); if (!response.ok) { throw new Error(`Failed to fetch solution content: ${response.status}`); } @@ -75,47 +58,48 @@ const TaskSolution: React.FC = ({ setAnswer(text); } } catch (error) { - console.error('Error loading solution content:', error); + console.error('Error loading latest solution content:', error); } finally { - setIsLoading(false); } }; - loadSolutionContent(); - }, [currentSolution, task.type, setAnswer, setSelectedFile]); + if (latestSolution && !solutionsQuery.isLoading && !solutionsQuery.isError) { + loadLatestSolution(); + } + }, [latestSolution, task.id, task.type, setAnswer, setSelectedFile]); const handleOpenHistory = () => { setIsHistoryOpen(true); }; - const handleSolutionSelect = (solution: Solution) => { - setCurrentSolution(solution); - setIsHistoryOpen(false); + const handleSolutionSelect = async (solution: Solution) => { + if (!solution.content) return; + + try { + if (task.type === TaskType.FILE) { + setSelectedFile(null); + setSelectedSolutionUrl(solution.content); + } else { + const response = await fetch(solution.content); + if (!response.ok) { + throw new Error(`Failed to fetch solution content: ${response.status}`); + } + const text = await response.text(); + setAnswer(text); + } + } catch (error) { + console.error('Error loading solution content:', error); + } }; const handleClearExistingFile = () => { setSelectedSolutionUrl(null); }; - // Handle successful submission - const handleSubmitSuccess = (newSolution: Solution) => { - // Update the current solution to the newly submitted one - setCurrentSolution(newSolution); - // Reset form - setAnswer(""); - setSelectedFile(null); - setSelectedSolutionUrl(null); - }; - - const handleSubmit = () => { - onSubmit(); - - }; - return (
- {currentSolution ? ( - + {latestSolution ? ( + ) : (
Решение еще не отправлено @@ -136,7 +120,6 @@ const TaskSolution: React.FC = ({ fileInputRef={fileInputRef} existingFileUrl={selectedSolutionUrl} onClearExistingFile={handleClearExistingFile} - isLoading={isLoading} /> )} @@ -148,7 +131,7 @@ const TaskSolution: React.FC = ({ )} @@ -158,7 +141,6 @@ const TaskSolution: React.FC = ({ solutions={solutionHistory} maxPoints={task.points} onSolutionSelect={handleSolutionSelect} - currentSolutionId={currentSolution?.id} />
);