Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/translationProgress #38

Merged
merged 4 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ services:
restart: always
ports:
- '6379:6379'
command: redis-server --save 20 1 --loglevel warning --requirepass eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81
command: redis-server --save 20 1 --loglevel warning
volumes:
- redis:/data

Comment on lines 17 to 23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [1-19]

エラーメッセージの詳細を追加し、ユーザーにとってわかりやすくするために、エラーハンドリングを改善できます。

-    throw new Response("Page version ID is required", { status: 400 });
+    throw new Response("Bad Request: Page version ID is required in the query parameters.", { status: 400 });

-    throw new Response("Page version not found", { status: 404 });
+    throw new Response("Not Found: The requested page version does not exist.", { status: 404 });

Comment on lines 17 to 23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [1-17]

@@foreignKeyは正しくありません。代わりに@relation属性を使用してください。

-  @@foreignKey([userId], references: [id], onDelete: Cascade)
-  @@foreignKey([pageVersionId], references: [id], onDelete: Cascade)
+  user   User         @relation(fields: [userId], references: [id], onDelete: Cascade)
+  pageVersion PageVersion @relation(fields: [pageVersionId], references: [id], onDelete: Cascade)

Expand Down
8 changes: 2 additions & 6 deletions web/app/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Link } from "@remix-run/react";
import { Form, useSubmit } from "@remix-run/react";
import { LogIn, LogOut } from "lucide-react"; // Lucide アイコンをインポート
import { LogIn, LogOut } from "lucide-react";
import { ModeToggle } from "~/components/dark-mode-toggle";
import type { SafeUser } from "../types";
import { TargetLanguageSelect } from "./TargetLanguageSelect";
Expand Down Expand Up @@ -35,11 +35,7 @@ export function Header({ safeUser, targetLanguage }: HeaderProps) {
<ModeToggle />
{safeUser ? (
<>
<Link
to="/auth/logout"
className="text-gray-600 hover:text-gray-800"
title="Logout"
>
<Link to="/auth/logout" title="Logout">
<LogOut className="w-6 h-6" />
</Link>
</>
Expand Down
2 changes: 1 addition & 1 deletion web/app/components/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export function LoadingSpinner() {
return (
<>
<div className="animate-spin rounded-full h-4 w-4 border-t-2 border-b-2 border-white"></div>
<div className="animate-spin rounded-full h-4 w-4 border-t-2 border-b-2 border-white" />
</>
);
}
54 changes: 27 additions & 27 deletions web/app/components/ui/badge.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { type VariantProps, cva } from "class-variance-authority";
import type * as React from "react";

import { cn } from "~/utils/cn"
import { cn } from "~/utils/cn";

const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);

export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}

function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
);
}

export { Badge, badgeVariants }
export { Badge, badgeVariants };
42 changes: 21 additions & 21 deletions web/app/components/ui/progress.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import * as React from "react"
import * as ProgressPrimitive from "@radix-ui/react-progress"
import * as ProgressPrimitive from "@radix-ui/react-progress";
import * as React from "react";

import { cn } from "~/utils/cn"
import { cn } from "~/utils/cn";

const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
className
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
))
Progress.displayName = ProgressPrimitive.Root.displayName
<ProgressPrimitive.Root
ref={ref}
className={cn(
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
className,
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
));
Progress.displayName = ProgressPrimitive.Root.displayName;

export { Progress }
export { Progress };
76 changes: 38 additions & 38 deletions web/app/components/ui/scroll-area.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import * as React from "react";

import { cn } from "~/utils/cn"
import { cn } from "~/utils/cn";

const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;

const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
))
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;

export { ScrollArea, ScrollBar }
export { ScrollArea, ScrollBar };
28 changes: 28 additions & 0 deletions web/app/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as React from "react";

import { cn } from "~/utils/cn";

const TooltipProvider = TooltipPrimitive.Provider;

const Tooltip = TooltipPrimitive.Root;

const TooltipTrigger = TooltipPrimitive.Trigger;

const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { prisma } from "./prisma";
import { prisma } from "../utils/prisma";

export async function getOrCreatePageId(url: string): Promise<number> {
const page = await prisma.page.upsert({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createHash } from "node:crypto";
import { prisma } from "./prisma";
import { prisma } from "../utils/prisma";

export async function getOrCreatePageVersionId(
url: string,
Comment on lines 1 to 5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [8-9]

requireUserId関数はユーザー認証を行いますが、エラーハンドリングが不足しています。ユーザーが認証されていない場合の処理を追加する必要があります。

+ try {
+   const userId = await requireUserId(request);
+ } catch (error) {
+   return redirect("/login");
+ }

Comment on lines 1 to 5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [24-26]

findUniqueメソッドの結果がnullの場合に404エラーを返すのは良いですが、セキュリティ上の理由から、存在しないページバージョンIDについての詳細な情報を提供しない方が良いです。

- throw new Response("Page version not found", { status: 404 });
+ throw new Response("Not Found", { status: 404 });

Comment on lines 1 to 5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [38-39]

フォームデータの取得時に型チェックが不足しています。texttargetLanguageがnullである可能性を考慮する必要があります。

- const text = formData.get("text");
- const targetLanguage = formData.get("targetLanguage");
+ const text = formData.get("text")?.toString() || "";
+ const targetLanguage = formData.get("targetLanguage")?.toString() || "";

Changes made to web/app/utils/gemini.ts for your review

---new_hunk---

import axios from "axios";

export async function getGeminiModelResponse(input: string): Promise<any> {
  let attempts = 0;
  const maxAttempts = 3;
  const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

  while (attempts < maxAttempts) {
    try {
      const response = await axios.post("https://api.gemini.com/v1/model", { input });
      return response.data;
    } catch (error) {
      attempts++;
      if (attempts >= maxAttempts) {
        throw new Error("Failed to get response from Gemini model after multiple attempts");
      }
      await delay(1000); // Wait for 1 second before retrying
    }
  }
}

---comment_chains---

Please review this change.

---end_change_section---

Response

Comment on lines 1 to 5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [7-7]

delay関数は毎回新しく作成されていますが、ループ外に移動して再利用可能にすることでパフォーマンスを向上させることができます。

+ const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
while (attempts < maxAttempts) {

ttizze marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
22 changes: 22 additions & 0 deletions web/app/libs/pageVersionTranslationInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { prisma } from "~/utils/prisma";

export async function getOrCreatePageVersionTranslationInfo(
pageVersionId: number,
targetLanguage: string,
translationTitle: string,
) {
return await prisma.pageVersionTranslationInfo.upsert({
where: {
pageVersionId_targetLanguage: {
pageVersionId,
targetLanguage,
},
},
update: {}, // 既存のレコードがある場合は更新しない
create: {
pageVersionId,
targetLanguage,
translationTitle,
},
});
Comment on lines +3 to +21

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

関数名 getOrCreatePageVersionTranslationInfo は、既存のレコードがある場合に更新しないことを示していますが、upsert メソッドの update フィールドが空であるため、実際には何も更新されません。これは誤解を招く可能性があります。関数名を変更するか、コメントを追加して意図を明確にすることをお勧めします。

- update: {}, // 既存のレコードがある場合は更新しない
+ update: {}, // 既存のレコードがある場合は何も更新しない

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createHash } from "node:crypto";
import { prisma } from "./prisma";
import { prisma } from "../utils/prisma";

export async function getOrCreateSourceTextId(
text: string,
Comment on lines 1 to 5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [16-18]

requireUserId関数の呼び出しが不要です。ユーザーIDは使用されていないため、削除することをお勧めします。

-  const userId = await requireUserId(request);

Expand Down
57 changes: 57 additions & 0 deletions web/app/libs/userAITranslationInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { prisma } from "~/utils/prisma";

export async function getOrCreateUserAITranslationInfo(
userId: number,
pageVersionId: number,
targetLanguage: string,
) {
try {
const userAITranslationInfo = await prisma.userAITranslationInfo.upsert({
where: {
userId_pageVersionId_targetLanguage: {
userId,
pageVersionId,
targetLanguage,
},
},
update: {
aiTranslationStatus: "pending",
aiTranslationProgress: 0,
},
create: {
userId,
pageVersionId,
targetLanguage,
aiTranslationStatus: "pending",
aiTranslationProgress: 0,
},
});
return userAITranslationInfo;
} catch (error) {
console.error("Error in getOrCreateUserAITranslationInfo:", error);
throw error;
}
Comment on lines +8 to +33

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

エラーハンドリングを改善するために、errorオブジェクトの詳細を含めると良いでしょう。これにより、デバッグが容易になります。

- 		console.error("Error in getOrCreateUserAITranslationInfo:", error);
+ 		console.error("Error in getOrCreateUserAITranslationInfo:", {
+ 			message: error.message,
+ 			stack: error.stack,
+ 			context: { userId, pageVersionId, targetLanguage }
+ 		});

}

export async function updateUserAITranslationInfo(
userId: number,
pageVersionId: number,
targetLanguage: string,
status: string,
progress: number,
) {
return await prisma.userAITranslationInfo.update({
where: {
userId_pageVersionId_targetLanguage: {
userId,
pageVersionId,
targetLanguage,
},
},
data: {
aiTranslationStatus: status,
aiTranslationProgress: progress,
lastTranslatedAt: new Date(), // 明示的に更新
},
});
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { prisma } from "./prisma";
import { prisma } from "../utils/prisma";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

インポートの順序を整理し、標準ライブラリ、サードパーティライブラリ、ローカルモジュールの順に並べると良いでしょう。

+ import { json, LoaderFunction, ActionFunction } from "@remix-run/node";
+ import { useLoaderData, useActionData } from "@remix-run/react";
+ import { z } from "zod";
+ import { db } from "~/utils/db.server";
+ import { requireUserId } from "~/utils/session.server";
+ import { TranslatePage } from "~/components/TranslatePage";

export async function updateUserReadHistory(
userId: number,
Expand Down
4 changes: 2 additions & 2 deletions web/app/utils/userService.ts → web/app/libs/userService.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { prisma } from "./prisma";
import { prisma } from "../utils/prisma";

export async function getOrCreateAIUser(name: string): Promise<number> {
const user = await prisma.user.upsert({
where: { email: `${name}@ai.com` },
update: {},
create: { name, email: `${name}@ai.com`, isAI: true },
create: { name, email: `${name}@ai.com`, isAI: true, image: "" },
});

return user.id;
Expand Down
Loading
Loading