parent
638d9fe8f3
commit
2a96ce0a3d
|
|
@ -20,16 +20,17 @@ MAX_USAGE_PER = 100
|
||||||
CHAT_TTL = 7200
|
CHAT_TTL = 7200
|
||||||
|
|
||||||
|
|
||||||
@ai_router.post("/exp")
|
@ai_router.post("/word/exp", deprecated=False)
|
||||||
async def dict_exp(
|
async def dict_exp(
|
||||||
request: Request,
|
request: Request,
|
||||||
Q: AIQuestionRequest,
|
Q: AIQuestionRequest,
|
||||||
user: Tuple[User, Dict] = Depends(get_current_user)
|
user: Tuple[User, Dict] = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
该接口仅用于查词页面且为具有MCP功能的
|
||||||
:param word:
|
:param request:
|
||||||
:param question: 不允许question为空调用
|
:param Q:
|
||||||
|
:param user:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if user[0].token_usage > CHAT_TTL and not user[0].is_admin:
|
if user[0].token_usage > CHAT_TTL and not user[0].is_admin:
|
||||||
|
|
@ -95,6 +96,11 @@ async def dict_exp(
|
||||||
raise HTTPException(status_code=500, detail=f"AI调用失败: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"AI调用失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
@ai_router.post("/univer")
|
||||||
|
async def universal_main():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ai_router.post("/clear")
|
@ai_router.post("/clear")
|
||||||
async def clear_history(word: str, request: Request, user: Tuple[User, Dict] = Depends(get_current_user)):
|
async def clear_history(word: str, request: Request, user: Tuple[User, Dict] = Depends(get_current_user)):
|
||||||
redis = request.app.state.redis
|
redis = request.app.state.redis
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@ CHAT_TTL = 7200
|
||||||
async def get_and_set_last_key(redis: Redis, word: str, user_id: str):
|
async def get_and_set_last_key(redis: Redis, word: str, user_id: str):
|
||||||
last_key = f"last_word:{user_id}"
|
last_key = f"last_word:{user_id}"
|
||||||
last_word = await redis.get(last_key)
|
last_word = await redis.get(last_key)
|
||||||
|
print(last_word)
|
||||||
|
|
||||||
# 如果上一次查的词和这次不同,就清空旧词的记录
|
# 如果上一次查的词和这次不同,就清空旧词的记录
|
||||||
if last_word and last_word.decode() != word:
|
if last_word and last_word != word:
|
||||||
await clear_chat_history(redis, user_id, last_word.decode())
|
await clear_chat_history(redis, user_id, last_word.decode())
|
||||||
|
|
||||||
# 更新当前词
|
# 更新当前词
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
|
||||||
|
|
||||||
from app.models import User, CommentFr, CommentJp
|
|
||||||
from app.schemas.comment_schemas import CommentUpload
|
|
||||||
from app.utils.security import get_current_user
|
|
||||||
|
|
||||||
comment_router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
@comment_router.post("/make-comment")
|
|
||||||
async def new_word_comment(
|
|
||||||
upload: CommentUpload,
|
|
||||||
user: Tuple[User, dict] = Depends(get_current_user)
|
|
||||||
) -> None:
|
|
||||||
if upload.lang == "fr":
|
|
||||||
await CommentFr.create(
|
|
||||||
user=user[0],
|
|
||||||
comment_text=upload.comment_content,
|
|
||||||
comment_word=upload.comment_word,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
await CommentJp.create(
|
|
||||||
user=user[0],
|
|
||||||
comment_text=upload.comment_content,
|
|
||||||
comment_word=upload.comment_word,
|
|
||||||
)
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
from pydantic import BaseModel, field_validator, ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class Feedback(BaseModel):
|
||||||
|
report_part: str
|
||||||
|
text: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@field_validator("report_part")
|
||||||
|
def report_part_validator(cls, v):
|
||||||
|
types = (
|
||||||
|
"ui_design",
|
||||||
|
"dict_fr",
|
||||||
|
"dict_jp",
|
||||||
|
"user",
|
||||||
|
"translate",
|
||||||
|
"writting",
|
||||||
|
"ai_assist",
|
||||||
|
"pronounce",
|
||||||
|
"comment_api_test", # 该类型仅作测试使用,不对外暴露
|
||||||
|
)
|
||||||
|
if v not in types:
|
||||||
|
raise ValidationError("Invalid feedback report type")
|
||||||
|
return v
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@field_validator("text")
|
||||||
|
def text_validator(cls, v):
|
||||||
|
if v is None:
|
||||||
|
raise ValidationError("Feedback text cannot be NULL")
|
||||||
|
return v
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
|
from app.api.make_comments.comment_schemas import Feedback
|
||||||
|
from app.core.email_utils import send_email
|
||||||
|
from app.models import User
|
||||||
|
from app.utils.security import get_current_user
|
||||||
|
|
||||||
|
comment_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@comment_router.post("/improvements")
|
||||||
|
async def new_comment(
|
||||||
|
upload: Feedback,
|
||||||
|
user: Tuple[User, dict] = Depends(get_current_user)
|
||||||
|
):
|
||||||
|
user_id = user[0].id
|
||||||
|
username = user[0].name
|
||||||
|
type = upload.report_part
|
||||||
|
mail_text = upload.text
|
||||||
|
sender = "no-reply@lexiverse.com.cn"
|
||||||
|
receivers = ["GodricTan@gmail.com"]
|
||||||
|
|
||||||
|
if type == "dict_fr":
|
||||||
|
receivers.append("aurora@lexiverse.com.cn")
|
||||||
|
|
||||||
|
content = f"""<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
<title>用户反馈通知</title>
|
||||||
|
<style>
|
||||||
|
@media (prefers-color-scheme: dark) {{
|
||||||
|
body, .email-body {{ background: #0f172a !important; color: #e5e7eb !important; }}
|
||||||
|
.card {{ background: #111827 !important; border-color: #374151 !important; }}
|
||||||
|
.muted {{ color: #9ca3af !important; }}
|
||||||
|
.badge {{ background: #1f2937 !important; color: #e5e7eb !important; border-color:#374151 !important; }}
|
||||||
|
}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background:#f5f7fb;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f7fb;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:24px;">
|
||||||
|
<table role="presentation" width="600" cellpadding="0" cellspacing="0" class="email-body" style="width:600px;max-width:600px;background:#ffffff;border-radius:12px;overflow:hidden;border:1px solid #e5e7eb;">
|
||||||
|
<tr>
|
||||||
|
<td style="background:linear-gradient(90deg,#4f46e5,#06b6d4);padding:22px 24px;">
|
||||||
|
<h1 style="margin:0;font-size:18px;line-height:1.4;color:#ffffff;">新的用户反馈</h1>
|
||||||
|
<p class="muted" style="margin:4px 0 0 0;font-size:12px;color:rgba(255,255,255,.85);">来自平台反馈中心</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 0 12px 0;font-size:14px;color:#111827;">
|
||||||
|
<strong>用户:</strong><span>{username}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 0 12px 0;">
|
||||||
|
<span class="badge" style="display:inline-block;padding:6px 10px;border:1px solid #e5e7eb;border-radius:999px;font-size:12px;line-height:1;color:#374151;background:#f9fafb;">
|
||||||
|
反馈板块:{type}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="card" style="margin-top:8px;border:1px solid #e5e7eb;border-radius:10px;background:#ffffff;">
|
||||||
|
<div style="padding:16px 18px;">
|
||||||
|
<div style="font-size:13px;color:#6b7280;margin-bottom:8px;">反馈内容</div>
|
||||||
|
<div style="font-size:15px;line-height:1.7;color:#111827;white-space:pre-wrap;">
|
||||||
|
{mail_text}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="muted" style="margin:16px 0 0 0;font-size:12px;color:#6b7280;">
|
||||||
|
您收到此邮件是因为系统检测到有新的反馈提交。请在后台查看详情并进行处理。
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:16px 24px;border-top:1px solid #e5e7eb;">
|
||||||
|
<table width="100%" role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td align="left" class="muted" style="font-size:12px;color:#9ca3af;">
|
||||||
|
这是一封系统通知邮件,请勿直接回复。
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>"""
|
||||||
|
|
||||||
|
for receiver in receivers:
|
||||||
|
send_email(to_email=receiver, subject="用户反馈", content=content)
|
||||||
|
|
||||||
|
return {"massages": "feedback succeed"}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
pron_test_router = APIRouter()
|
||||||
|
|
@ -2,9 +2,9 @@ from typing import Literal, List
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||||
|
|
||||||
|
from app.api.word_comment.word_comment_schemas import CommentSet
|
||||||
from app.models import DefinitionJp, CommentFr, CommentJp
|
from app.models import DefinitionJp, CommentFr, CommentJp
|
||||||
from app.models.fr import DefinitionFr
|
from app.models.fr import DefinitionFr
|
||||||
from app.schemas.comment_schemas import CommentSet
|
|
||||||
from app.schemas.search_schemas import SearchRequest, SearchResponse, SearchItemFr, SearchItemJp
|
from app.schemas.search_schemas import SearchRequest, SearchResponse, SearchItemFr, SearchItemJp
|
||||||
from app.utils.all_kana import all_in_kana
|
from app.utils.all_kana import all_in_kana
|
||||||
from app.utils.autocomplete import suggest_autocomplete
|
from app.utils.autocomplete import suggest_autocomplete
|
||||||
|
|
@ -147,3 +147,5 @@ async def search_list(query_word: SearchRequest, user=Depends(get_current_user))
|
||||||
print(query_word.query, query_word.language, query_word.sort, query_word.order)
|
print(query_word.query, query_word.language, query_word.sort, query_word.order)
|
||||||
word_contents = await suggest_autocomplete(query=query_word)
|
word_contents = await suggest_autocomplete(query=query_word)
|
||||||
return {"list": word_contents}
|
return {"list": word_contents}
|
||||||
|
|
||||||
|
#TODO 用户搜索历史
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ async def validate_password(password: str):
|
||||||
detail=f"密码只能包含字母、数字和常见特殊字符 {allowed_specials}"
|
detail=f"密码只能包含字母、数字和常见特殊字符 {allowed_specials}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def validate_email_exists(email: str):
|
async def validate_email_exists(email: str):
|
||||||
user = await User.get_or_none(email=email)
|
user = await User.get_or_none(email=email)
|
||||||
if user:
|
if user:
|
||||||
|
|
@ -99,28 +100,81 @@ async def send_email_code(redis: Redis, email: str, code: str, ops_type: Literal
|
||||||
await save_email_code(redis, email, code)
|
await save_email_code(redis, email, code)
|
||||||
|
|
||||||
ops_dict = {
|
ops_dict = {
|
||||||
"reg" : "用户注册",
|
"reg": "用户注册",
|
||||||
"reset" : "密码重置",
|
"reset": "密码重置",
|
||||||
}
|
}
|
||||||
subject = "Lexiverse 用户邮箱验证码"
|
subject = "Lexiverse 用户邮箱验证码"
|
||||||
content = f"""
|
content = f"""<!DOCTYPE html>
|
||||||
<html>
|
<html lang="zh-CN">
|
||||||
<body style="font-family: Arial, sans-serif; line-height:1.6;">
|
<head>
|
||||||
<h2 style="color:#4CAF50;">Lexiverse 验证码</h2>
|
<meta charset="utf-8">
|
||||||
<p>您好,</p>
|
<meta name="color-scheme" content="light dark">
|
||||||
<p>您正在进行 <b>{ops_dict[ops_type]}</b> 操作。</p>
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
<p>
|
<title>Lexiverse 验证码</title>
|
||||||
您的验证码是:
|
<style>
|
||||||
<span style="font-size: 24px; font-weight: bold; color: #d9534f;">{code}</span>
|
@media (prefers-color-scheme: dark) {{
|
||||||
</p>
|
body, .email-body {{ background: #0f172a !important; color: #e5e7eb !important; }}
|
||||||
<p>有效期 5 分钟,请勿泄露给他人。</p>
|
.card {{ background: #111827 !important; border-color: #374151 !important; }}
|
||||||
<hr>
|
.muted {{ color: #9ca3af !important; }}
|
||||||
<p style="font-size: 12px; color: #999;">
|
.code-box {{ background:#1f2937 !important; color:#fff !important; border-color:#374151 !important; }}
|
||||||
如果这不是您本人的操作,请忽略此邮件。
|
}}
|
||||||
</p>
|
</style>
|
||||||
</body>
|
</head>
|
||||||
</html>
|
<body style="margin:0;padding:0;background:#f5f7fb;font-family:'Microsoft Yahei','Arial',sans-serif;">
|
||||||
"""
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f7fb;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:24px;">
|
||||||
|
<table role="presentation" width="600" cellpadding="0" cellspacing="0" class="email-body" style="width:600px;max-width:600px;background:#ffffff;border-radius:12px;overflow:hidden;border:1px solid #e5e7eb;">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<tr>
|
||||||
|
<td style="background:linear-gradient(90deg,#4f46e5,#06b6d4);padding:22px 24px;">
|
||||||
|
<h1 style="margin:0;font-size:18px;line-height:1.4;color:#ffffff;">Lexiverse 验证码</h1>
|
||||||
|
<p class="muted" style="margin:4px 0 0;font-size:12px;color:rgba(255,255,255,.85);">安全身份校验</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px 24px;">
|
||||||
|
|
||||||
|
<p style="margin-top:0;font-size:15px;color:#111827;">您好,</p>
|
||||||
|
<p style="font-size:15px;color:#111827;">
|
||||||
|
您正在进行 <strong>{ops_dict[ops_type]}</strong> 操作
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card" style="margin:18px 0;padding:18px;border:1px solid #e5e7eb;border-radius:10px;background:#ffffff;text-align:center;">
|
||||||
|
<div style="font-size:13px;color:#6b7280;margin-bottom:6px;">您的验证码</div>
|
||||||
|
|
||||||
|
<div class="code-box" style="display:inline-block;padding:12px 24px;border:1px solid #e5e7eb;border-radius:8px;background:#f9fafb;font-size:26px;font-weight:bold;color:#d9534f;letter-spacing:4px;">
|
||||||
|
{code}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="muted" style="margin-top:10px;font-size:12px;color:#6b7280;">
|
||||||
|
有效期 5 分钟,请勿泄露给他人
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="muted" style="margin-top:16px;font-size:12px;color:#9ca3af;">
|
||||||
|
如果这不是您本人的操作,请忽略此邮件
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:16px 24px;border-top:1px solid #e5e7eb;">
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td align="left" class="muted" style="font-size:12px;color:#9ca3af;">
|
||||||
|
这是一封系统通知邮件,请勿直接回复
|
||||||
|
</td>
|
||||||
|
<td align="right" class="muted" style="font-size:12px;color:#9ca3af;">
|
||||||
|
Lexiverse 安全中心
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
send_email(email, subject, content)
|
send_email(email, subject, content)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from typing import Literal, Tuple
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
|
from app.api.word_comment.word_comment_schemas import CommentUpload
|
||||||
from app.models import User, CommentFr, CommentJp
|
from app.models import User, CommentFr, CommentJp
|
||||||
from app.schemas.comment_schemas import CommentUpload
|
|
||||||
from app.utils.security import get_current_user
|
from app.utils.security import get_current_user
|
||||||
|
|
||||||
word_comment_router = APIRouter()
|
word_comment_router = APIRouter()
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,4 @@ class CommentSet(BaseModel):
|
||||||
class CommentUpload(BaseModel):
|
class CommentUpload(BaseModel):
|
||||||
comment_word: str
|
comment_word: str
|
||||||
comment_content: str
|
comment_content: str
|
||||||
# lang: Literal["fr", "jp"]
|
# lang: Literal["fr", "jp"]
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
@ -11,7 +11,7 @@ from app.utils.textnorm import normalize_text
|
||||||
from settings import TORTOISE_ORM
|
from settings import TORTOISE_ORM
|
||||||
|
|
||||||
|
|
||||||
async def suggest_autocomplete(query: SearchRequest, limit: int = 10) -> List[Tuple[str, str]]:
|
async def suggest_autocomplete(query: SearchRequest, limit: int = 10):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param query: 当前用户输入的内容
|
:param query: 当前用户输入的内容
|
||||||
|
|
@ -22,7 +22,7 @@ async def suggest_autocomplete(query: SearchRequest, limit: int = 10) -> List[Tu
|
||||||
query_word = normalize_text(query.query)
|
query_word = normalize_text(query.query)
|
||||||
exact = await (
|
exact = await (
|
||||||
WordlistFr
|
WordlistFr
|
||||||
.get_or_none(text=query.query)
|
.get_or_none(search_text=query.query)
|
||||||
.values("text", "freq")
|
.values("text", "freq")
|
||||||
)
|
)
|
||||||
if exact:
|
if exact:
|
||||||
|
|
@ -33,7 +33,7 @@ async def suggest_autocomplete(query: SearchRequest, limit: int = 10) -> List[Tu
|
||||||
qs_prefix = (
|
qs_prefix = (
|
||||||
WordlistFr
|
WordlistFr
|
||||||
.filter(Q(search_text__startswith=query_word) | Q(text__startswith=query.query))
|
.filter(Q(search_text__startswith=query_word) | Q(text__startswith=query.query))
|
||||||
.exclude(text=query.query)
|
.exclude(search_text=query.query)
|
||||||
.only("text", "freq")
|
.only("text", "freq")
|
||||||
)
|
)
|
||||||
prefix_objs = await qs_prefix[:limit]
|
prefix_objs = await qs_prefix[:limit]
|
||||||
|
|
@ -53,6 +53,17 @@ async def suggest_autocomplete(query: SearchRequest, limit: int = 10) -> List[Tu
|
||||||
contains_objs = await qs_contain[: need * 2]
|
contains_objs = await qs_contain[: need * 2]
|
||||||
contains = [(o.text, o.freq) for o in contains_objs]
|
contains = [(o.text, o.freq) for o in contains_objs]
|
||||||
|
|
||||||
|
seen_text, out = set(), []
|
||||||
|
for text, freq in list(exact_word) + list(prefix) + list(contains):
|
||||||
|
key = text
|
||||||
|
if key not in seen_text:
|
||||||
|
seen_text.add(key)
|
||||||
|
out.append((text, freq))
|
||||||
|
if len(out) >= limit:
|
||||||
|
break
|
||||||
|
out = sorted(out, key=lambda w: (-w[2], len(w[0]), w[0]))
|
||||||
|
return [text for text, _ in out]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
query_word = all_in_kana(query.query)
|
query_word = all_in_kana(query.query)
|
||||||
exact = await (
|
exact = await (
|
||||||
|
|
@ -89,16 +100,16 @@ async def suggest_autocomplete(query: SearchRequest, limit: int = 10) -> List[Tu
|
||||||
contains_objs = qs_contain[:need * 2]
|
contains_objs = qs_contain[:need * 2]
|
||||||
contains: List[Tuple[str, str, int]] = [(o.text, o.hiragana, o.freq) for o in contains_objs]
|
contains: List[Tuple[str, str, int]] = [(o.text, o.hiragana, o.freq) for o in contains_objs]
|
||||||
|
|
||||||
seen_text, out = set(), []
|
seen_text, out = set(), []
|
||||||
for text, hiragana, freq in list(exact_word) + list(prefix) + list(contains):
|
for text, hiragana, freq in list(exact_word) + list(prefix) + list(contains):
|
||||||
key = (text, hiragana)
|
key = (text, hiragana)
|
||||||
if key not in seen_text:
|
if key not in seen_text:
|
||||||
seen_text.add(key)
|
seen_text.add(key)
|
||||||
out.append((text, hiragana, freq))
|
out.append((text, hiragana, freq))
|
||||||
if len(out) >= limit:
|
if len(out) >= limit:
|
||||||
break
|
break
|
||||||
out = sorted(out, key=lambda w: (-w[2], len(w[0]), w[0]))
|
out = sorted(out, key=lambda w: (-w[2], len(w[0]), w[0]))
|
||||||
return [(text, hiragana) for text, hiragana, _ in out]
|
return [(text, hiragana) for text, hiragana, _ in out]
|
||||||
|
|
||||||
|
|
||||||
async def __test():
|
async def __test():
|
||||||
|
|
|
||||||
10
main.py
10
main.py
|
|
@ -8,6 +8,8 @@ from tortoise.contrib.fastapi import register_tortoise
|
||||||
import app.models.signals
|
import app.models.signals
|
||||||
from app.api.admin.router import admin_router
|
from app.api.admin.router import admin_router
|
||||||
from app.api.ai_assist.routes import ai_router
|
from app.api.ai_assist.routes import ai_router
|
||||||
|
from app.api.make_comments.routes import comment_router
|
||||||
|
from app.api.pronounciation_test.routes import pron_test_router
|
||||||
from app.api.redis_test import redis_test_router
|
from app.api.redis_test import redis_test_router
|
||||||
from app.api.search import dict_search
|
from app.api.search import dict_search
|
||||||
from app.api.translator import translator_router
|
from app.api.translator import translator_router
|
||||||
|
|
@ -15,7 +17,7 @@ from app.api.user.routes import users_router
|
||||||
from app.api.word_comment.routes import word_comment_router
|
from app.api.word_comment.routes import word_comment_router
|
||||||
from app.core.redis import init_redis, close_redis
|
from app.core.redis import init_redis, close_redis
|
||||||
from app.utils.phone_encrypt import PhoneEncrypt
|
from app.utils.phone_encrypt import PhoneEncrypt
|
||||||
from settings import ONLINE_SETTINGS
|
from settings import TORTOISE_ORM
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
|
|
@ -43,7 +45,7 @@ app.add_middleware(
|
||||||
|
|
||||||
register_tortoise(
|
register_tortoise(
|
||||||
app=app,
|
app=app,
|
||||||
config=ONLINE_SETTINGS,
|
config=TORTOISE_ORM,
|
||||||
)
|
)
|
||||||
|
|
||||||
app.include_router(users_router, tags=["User API"], prefix="/users")
|
app.include_router(users_router, tags=["User API"], prefix="/users")
|
||||||
|
|
@ -56,7 +58,11 @@ app.include_router(translator_router, tags=["Translation API"])
|
||||||
|
|
||||||
app.include_router(ai_router, tags=["AI Assist API"], prefix="/ai_assist")
|
app.include_router(ai_router, tags=["AI Assist API"], prefix="/ai_assist")
|
||||||
|
|
||||||
|
app.include_router(comment_router, tags=["Comment API"])
|
||||||
|
|
||||||
app.include_router(word_comment_router, tags=["Word Comment API"], prefix="/comment/word")
|
app.include_router(word_comment_router, tags=["Word Comment API"], prefix="/comment/word")
|
||||||
|
|
||||||
|
app.include_router(pron_test_router, tags=["Pron Test API"], prefix="/test")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
|
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue