diff --git a/src/lib/actions/update_task_result.ts b/src/lib/actions/update_task_result.ts
new file mode 100644
index 000000000..35eb98827
--- /dev/null
+++ b/src/lib/actions/update_task_result.ts
@@ -0,0 +1,31 @@
+import { fail } from '@sveltejs/kit';
+
+import * as crud from '$lib/services/task_results';
+import { BAD_REQUEST, UNAUTHORIZED } from '$lib/constants/http-response-status-codes';
+
+// HACK: clickを1回実行するとactionsが2回実行されてしまう。原因と修正方法が分かっていない状態。
+export const updateTaskResult = async (
+ { request, locals }: { request: Request; locals: App.Locals },
+ operationLog: string,
+) => {
+ console.log(operationLog);
+ const response = await request.formData();
+ const session = await locals.auth.validate();
+
+ if (!session || !session.user || !session.user.userId) {
+ return fail(UNAUTHORIZED, {
+ message: 'ログインしていないか、もしくは、ログイン情報が不正です。',
+ });
+ }
+
+ const userId = session.user.userId;
+
+ try {
+ const taskId = response.get('taskId') as string;
+ const submissionStatus = response.get('submissionStatus') as string;
+
+ await crud.updateTaskResult(taskId, submissionStatus, userId);
+ } catch (error) {
+ return fail(BAD_REQUEST);
+ }
+};
diff --git a/src/lib/components/SubmissionStatus/SubmissionStatusImage.svelte b/src/lib/components/SubmissionStatus/SubmissionStatusImage.svelte
new file mode 100644
index 000000000..e18dd3bab
--- /dev/null
+++ b/src/lib/components/SubmissionStatus/SubmissionStatusImage.svelte
@@ -0,0 +1,28 @@
+
+
+
+{#if isLoggedIn}
+
+{/if}
diff --git a/src/lib/components/TaskList.svelte b/src/lib/components/TaskList.svelte
index c81ec4e35..24670a43d 100644
--- a/src/lib/components/TaskList.svelte
+++ b/src/lib/components/TaskList.svelte
@@ -2,7 +2,6 @@
import {
AccordionItem,
Accordion,
- Img,
Table,
TableBody,
TableBodyCell,
@@ -10,14 +9,13 @@
TableHead,
TableHeadCell,
} from 'flowbite-svelte';
- // @ts-ignore
- import ChevronDownOutline from 'flowbite-svelte-icons/ChevronDownOutline.svelte';
import type { TaskResult, TaskResults } from '$lib/types/task';
import type { SubmissionRatios } from '$lib/types/submission';
import ThermometerProgressBar from '$lib/components/ThermometerProgressBar.svelte';
import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte';
+ import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
import { getBackgroundColorFrom, submission_statuses } from '$lib/services/submission_status';
import { ATCODER_BASE_CONTEST_URL } from '$lib/constants/urls';
import { getContestNameLabel } from '$lib/utils/contest';
@@ -84,6 +82,7 @@
+
@@ -98,24 +97,15 @@
{#each taskResults as taskResult}
-
+
updatingModal.openModal(taskResult)}
>
-
- {#if isLoggedIn}
-
- {/if}
+
> {
+ const taskResultsWithTaskId = workBookTasks.map((workBookTask: WorkBookTaskBase) =>
+ getTaskResultWithErrorHandling(workBookTask.taskId, userId).then((taskResult: TaskResult) => ({
+ taskId: workBookTask.taskId,
+ taskResult: taskResult,
+ })),
+ );
+
+ const taskResultsMap = (await Promise.all(taskResultsWithTaskId)).reduce(
+ (map, { taskId, taskResult }: { taskId: string; taskResult: TaskResult }) =>
+ map.set(taskId, taskResult),
+ new Map(),
+ );
+
+ return taskResultsMap;
+}
+
+async function getTaskResultWithErrorHandling(taskId: string, userId: string): Promise {
+ try {
+ return await getTaskResult(taskId, userId);
+ } catch (error) {
+ return await handleTaskResultError(taskId, userId);
+ }
+}
+
+async function handleTaskResultError(taskId: string, userId: string): Promise {
+ try {
+ const task: Tasks = await getTask(taskId);
+ return await createDefaultTaskResult(userId, task[0]);
+ } catch (innerError) {
+ console.error(`Failed to create a default task result for taskId ${taskId}:`, innerError);
+ throw new Error(`問題id: ${taskId} の作成に失敗しました。`);
+ }
+}
+
export function createDefaultTaskResult(userId: string, task: Task): TaskResult {
const taskResult: TaskResult = {
contest_id: task.contest_id,
@@ -93,6 +132,7 @@ export function createDefaultTaskResult(userId: string, task: Task): TaskResult
return taskResult;
}
+
export async function getTaskResult(slug: string, userId: string) {
const task = await getTask(slug);
@@ -108,7 +148,7 @@ export async function getTaskResult(slug: string, userId: string) {
}
const status = statusById.get(taskanswer.status_id);
- taskResult.status_id = status.status_id;
+ taskResult.status_id = status.id;
taskResult.status_name = status.status_name;
taskResult.submission_status_image_path = status.image_path;
taskResult.submission_status_label_name = status.label_name;
diff --git a/src/routes/problems/+page.server.ts b/src/routes/problems/+page.server.ts
index 6ffdf6a7f..7c053eb24 100644
--- a/src/routes/problems/+page.server.ts
+++ b/src/routes/problems/+page.server.ts
@@ -1,9 +1,9 @@
-import { fail, type Actions } from '@sveltejs/kit';
+import { type Actions } from '@sveltejs/kit';
import * as crud from '$lib/services/task_results';
import type { TaskResults } from '$lib/types/task';
import { Roles } from '$lib/types/user';
-import { BAD_REQUEST, UNAUTHORIZED } from '$lib/constants/http-response-status-codes';
+import * as action from '$lib/actions/update_task_result';
// 問題一覧ページは、ログインしていなくても閲覧できるようにする
export async function load({ locals, url }) {
@@ -30,28 +30,9 @@ export async function load({ locals, url }) {
}
}
-// HACK: Actionを切り出すことができれば、問題集の回答状況の更新でほぼそのまま利用できる
export const actions = {
update: async ({ request, locals }) => {
- console.log('problems -> actions -> update');
- const response = await request.formData();
- const session = await locals.auth.validate();
-
- if (!session || !session.user || !session.user.userId) {
- return fail(UNAUTHORIZED, {
- message: 'ログインしていないか、もしくは、ログイン情報が不正です。',
- });
- }
-
- const userId = session.user.userId;
-
- try {
- const taskId = response.get('taskId') as string;
- const submissionStatus = response.get('submissionStatus') as string;
-
- await crud.updateTaskResult(taskId, submissionStatus, userId);
- } catch (error) {
- return fail(BAD_REQUEST);
- }
+ const operationLog = 'problems -> actions -> update';
+ return await action.updateTaskResult({ request, locals }, operationLog);
},
} satisfies Actions;
diff --git a/src/routes/workbooks/[slug]/+page.server.ts b/src/routes/workbooks/[slug]/+page.server.ts
index 22f3f4529..83db2e1b0 100644
--- a/src/routes/workbooks/[slug]/+page.server.ts
+++ b/src/routes/workbooks/[slug]/+page.server.ts
@@ -1,10 +1,12 @@
-import { error } from '@sveltejs/kit';
+import { error, type Actions } from '@sveltejs/kit';
import { getLoggedInUser, isAdmin, canRead } from '$lib/utils/authorship';
import { Roles } from '$lib/types/user';
import { getWorkbookWithAuthor, parseWorkBookId } from '$lib/utils/workbook';
-import * as taskCrud from '$lib/services/tasks';
+import * as taskResultsCrud from '$lib/services/task_results';
+import type { TaskResult } from '$lib/types/task';
import { BAD_REQUEST, FORBIDDEN } from '$lib/constants/http-response-status-codes';
+import * as action from '$lib/actions/update_task_result';
export async function load({ locals, params }) {
const loggedInUser = await getLoggedInUser(locals);
@@ -24,8 +26,22 @@ export async function load({ locals, params }) {
error(FORBIDDEN, `問題集id: ${params.slug} にアクセスする権限がありません。`);
}
- // FIXME: ユーザの回答状況を反映させるため、taskResultsに置き換え
- const tasks = await taskCrud.getTasksByTaskId();
+ const taskResults: Map = await taskResultsCrud.getTaskResultsByTaskId(
+ workBook.workBookTasks,
+ loggedInUser?.id as string,
+ );
- return { loggedInAsAdmin: loggedInAsAdmin, ...workbookWithAuthor, tasks: tasks };
+ return {
+ isLoggedIn: loggedInUser !== null,
+ loggedInAsAdmin: loggedInAsAdmin,
+ ...workbookWithAuthor,
+ taskResults: taskResults,
+ };
}
+
+export const actions = {
+ update: async ({ request, locals }) => {
+ const operationLog = 'workbook -> actions -> update';
+ return await action.updateTaskResult({ request, locals }, operationLog);
+ },
+} satisfies Actions;
diff --git a/src/routes/workbooks/[slug]/+page.svelte b/src/routes/workbooks/[slug]/+page.svelte
index c41f02e39..7fd705499 100644
--- a/src/routes/workbooks/[slug]/+page.svelte
+++ b/src/routes/workbooks/[slug]/+page.svelte
@@ -11,26 +11,31 @@
} from 'flowbite-svelte';
import HeadingOne from '$lib/components/HeadingOne.svelte';
+ import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte';
+ import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
import ExternalLinkWrapper from '$lib/components/ExternalLinkWrapper.svelte';
+ import { getBackgroundColorFrom } from '$lib/services/submission_status';
import { getContestUrl } from '$lib/utils/contest';
import { taskUrl } from '$lib/utils/task';
import { getContestNameLabel } from '$lib/utils/contest';
import type { WorkBookTaskBase } from '$lib/types/workbook';
- import type { Task } from '$lib/types/task';
+ import type { TaskResult } from '$lib/types/task';
export let data;
let workBook = data.workBook;
- let workBookTasks: WorkBookTaskBase[] = workBook.workBookTasks;
- let tasks = data.tasks; // workBookTasksのtaskIdから問題情報を取得
+ let workBookTasks: WorkBookTaskBase[];
+ let taskResults: Map;
+ $: taskResults = data.taskResults;
+ let isLoggedIn = data.isLoggedIn;
// TODO: 関数をutilへ移動させる
- const getTask = (taskId: string): Task | undefined => {
- return tasks.get(taskId);
+ const getTaskResult = (taskId: string): TaskResult => {
+ return taskResults.get(taskId)!;
};
const getContestIdFrom = (taskId: string): string => {
- return getTask(taskId)?.contest_id as string;
+ return getTaskResult(taskId)?.contest_id as string;
};
const getContestNameFrom = (taskId: string): string => {
@@ -39,8 +44,23 @@
};
const getTaskName = (taskId: string): string => {
- return getTask(taskId)?.title as string;
+ return getTaskResult(taskId)?.title as string;
};
+
+ let updatingModal: UpdatingModal;
+
+ // FIXME: clickを1回実行するとactionsが2回実行されてしまう。原因と修正方法が分かっていない。
+ function handleClick(taskId: string) {
+ updatingModal.openModal(getTaskResult(taskId));
+ }
+
+ $: if (taskResults && workBook && Array.isArray(workBook.workBookTasks)) {
+ workBookTasks = workBook.workBookTasks;
+ } else if (!taskResults) {
+ console.error('Not found taskResults.');
+ } else if (!workBook || !Array.isArray(workBook.workBookTasks)) {
+ console.error('Not found workBook or workBook.workBookTasks is not an array.');
+ }
@@ -79,8 +99,19 @@
{#each workBookTasks as workBookTask}
-
- {'準備中'}
+
+ handleClick(workBookTask.taskId)}
+ >
+
+
+
+
{:else}
{'問題を1問以上登録してください。'}
{/if}