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

PoC: assisted translation #8

Merged
merged 6 commits into from
May 21, 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
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@fortawesome/free-regular-svg-icons": "^5.11.2",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/react-fontawesome": "^0.1.7",
"@jokester/ts-commonutil": "^0.5.0",
"@reduxjs/toolkit": "^1.2.5",
"@zip.js/zip.js": "^2.7.20",
"antd": "^4.15.1",
Expand Down
6 changes: 5 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ResetPassword from './pages/ResetPassword';
import { NotFoundPage } from './pages/404';
import { AppState } from './store';
import style from './style';
import { MitPreprocessDemo } from './pages/mit/MitPreprocessDemo';
import { routes } from './pages/routes';

// 公共的页面
Expand All @@ -20,7 +21,7 @@ const publicPaths = [
routes.login,
routes.signUp,
routes.resetPassword,
// routes.mit.preprocessDemo,
routes.mit.preprocessDemo,
] as readonly string[];

const App: React.FC = () => {
Expand Down Expand Up @@ -168,6 +169,9 @@ const App: React.FC = () => {
<Route path={routes.dashboard}>
<Dashboard />
</Route>
<Route path={routes.mit.preprocessDemo}>
<MitPreprocessDemo />
</Route>
{userIsAdmin && (
<Route path={routes.admin}>
<Admin />
Expand Down
16 changes: 16 additions & 0 deletions src/apis/_request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BasicSuccessResult, request } from '.';
import { AxiosRequestConfig } from 'axios';

export async function uploadRequest<T = unknown>(
data: FormData,
configs: AxiosRequestConfig,
): Promise<BasicSuccessResult<T>> {
return request({
data,
...configs,
headers: {
...configs.headers,
'Content-Type': 'multipart/form-data',
},
});
}
29 changes: 29 additions & 0 deletions src/apis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import group from './group';
import insight from './insight';
import siteSetting from './siteSetting';
import { mitPreprocess } from './mit_preprocess';

Check warning on line 32 in src/apis/index.ts

View workflow job for this annotation

GitHub Actions / check-pr

'mitPreprocess' is defined but never used

// TODO: move instance/request to a peer file, to prevent circular imports
// TODO: can we hide this from API callsites?
Expand Down Expand Up @@ -57,10 +58,10 @@
} as const;

/** 成功的响应 */
export interface BasicSuccessResult<T = any> {

Check warning on line 61 in src/apis/index.ts

View workflow job for this annotation

GitHub Actions / check-pr

Unexpected any. Specify a different type
type: typeof resultTypes.SUCCESS;
data: T;
headers: any;

Check warning on line 64 in src/apis/index.ts

View workflow job for this annotation

GitHub Actions / check-pr

Unexpected any. Specify a different type
}

/** 基础错误响应结果的数据 */
Expand Down Expand Up @@ -123,7 +124,7 @@
| CancelFailureResult
| OtherFailureResult;

export const request = <T = any>(

Check warning on line 127 in src/apis/index.ts

View workflow job for this annotation

GitHub Actions / check-pr

Unexpected any. Specify a different type
axiosConfig: AxiosRequestConfig,
): Promise<BasicSuccessResult<T>> => {
return instance({
Expand Down Expand Up @@ -247,6 +248,34 @@
});
};

export const api = {
// TODO switch to this nested / drop use of default export
application,
auth,
file,
group,
insight,
instance,
invitation,
language,
// mitPreprocess,
me,
member,
output,
project,
projectSet,
siteSetting,
source,
target,
team,
tip,
translation,
type,
user,
} as const;
/**
* @deprecated use named import
*/
export default {
instance,
...auth,
Expand Down
109 changes: 109 additions & 0 deletions src/apis/mit_preprocess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { request } from '.';
import { uploadRequest } from './_request';
import { wait } from '@jokester/ts-commonutil/lib/concurrency/timing';

const mitApiPrefix = `/v1/mit`;

interface MitPreprocessResponse {

Check warning on line 7 in src/apis/mit_preprocess.ts

View workflow job for this annotation

GitHub Actions / check-pr

'MitPreprocessResponse' is defined but never used
id: string;
result?: MitPreprocessResult;
status: 'success' | 'pending' | 'fail';
message?: string;
}

export interface MitPreprocessResult {
target_lang: string;
text_quads: TextQuad[];
}

type CoordTuple = [number, number]; // x, y in non-normalized pixels
export type BBox = [CoordTuple, CoordTuple, CoordTuple, CoordTuple]; // left-top, right-top, right-bottom, left-bottom

export interface TextQuad {
pts: BBox;
raw_text: string;
translated: string;
}

async function uploadImg(file: File) {
const formData = new FormData();
formData.append('file', file);

return uploadRequest<{ filename: string }>(formData, {
method: 'POST',
url: `${mitApiPrefix}/images`,
});
}

async function createImgTask(
filename: string,
taskName: 'mit_ocr' | 'mit_detect_text',
payload: object,
) {
return request<{ task_id: string }>({
method: 'POST',
url: `${mitApiPrefix}/image-tasks`,
data: {
task_name: taskName,
filename,
...payload,
},
});
}

interface TaskState<Result> {
task_id: string;
status: 'success' | 'pending' | 'fail';
result?: Result;
message?: string;
}

async function waitImgTask<Result>(taskId: string) {
while (true) {
const r = await request<TaskState<Result>>({
method: 'GET',
url: `${mitApiPrefix}/image-tasks/${taskId}`,
});
if (r.data.status === 'success') {
return r.data.result!;
} else if (r.data.status === 'pending') {
await wait(2e3);
} else {
throw new Error(`task failed: ${r.data.message ?? 'unknown'}`);
}
}
}

async function createTranslateTask(payload: object) {
return request<{ task_id: string }>({
method: 'POST',
url: `${mitApiPrefix}/translate-tasks`,
data: {
...payload,
},
});
}

async function waitTranslateTask(taskId: string) {
while (true) {
const r = await request<TaskState<string[]>>({
method: 'GET',
url: `${mitApiPrefix}/translate-tasks/${taskId}`,
});
if (r.data.status === 'success') {
return r.data.result!;
} else if (r.data.status === 'pending') {
await wait(1e3);
} else {
throw new Error(`task failed: ${r.data.message ?? 'unknown'}`);
}
}
}

export const mitPreprocess = {
uploadImg,
createImgTask,
waitImgTask,
createTranslateTask,
waitTranslateTask,
} as const;
28 changes: 13 additions & 15 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { configs } from '../configs';
interface HeaderProps {
className?: string;
}
const showMitExperimentLink = configs.mitUiEnabled;
/**
* 头部
*/
Expand Down Expand Up @@ -71,6 +72,11 @@ export const Header: FC<HeaderProps> = ({ className }) => {
</Menu.Item>
</Menu>
);
const mitLink = showMitExperimentLink && (
<a className="login" href={routes.mit.preprocessDemo}>
{formatMessage({ id: 'mit.title' })}
</a>
);

return (
<div
Expand Down Expand Up @@ -101,7 +107,7 @@ export const Header: FC<HeaderProps> = ({ className }) => {
.register {
color: #7f7a7a;
font-size: 20px;
line-height: 25x;
line-height: 25px;
padding: 7px 12px;
border-radius: ${style.borderRadiusBase};
${clickEffect()};
Expand Down Expand Up @@ -134,6 +140,7 @@ export const Header: FC<HeaderProps> = ({ className }) => {
</div>
{currentUser.token ? (
<div className="right">
{mitLink}
<Dropdown
overlay={menu}
placement="bottomRight"
Expand All @@ -150,22 +157,13 @@ export const Header: FC<HeaderProps> = ({ className }) => {
</div>
) : (
<div className="right">
<div
className="login"
onClick={() => {
history.push('/login');
}}
>
{mitLink}
<a className="login" href={routes.login}>
{formatMessage({ id: 'auth.login' })}
</div>
<div
className="register"
onClick={() => {
history.push('/register');
}}
>
</a>
<a className="register" href={routes.signUp}>
{formatMessage({ id: 'auth.register' })}
</div>
</a>
</div>
)}
</div>
Expand Down
Loading
Loading