From ea7ce02eeff812160260625e4295f97d75cf44f2 Mon Sep 17 00:00:00 2001 From: lostecho Date: Mon, 7 Jul 2025 02:51:20 +0800 Subject: [PATCH] change app location --- __pycache__/main.cpython-312.pyc | Bin 372 -> 5716 bytes app/__pycache__/main.cpython-312.pyc | Bin 5720 -> 0 bytes app/__pycache__/main.cpython-313.pyc | Bin 5733 -> 0 bytes app/main.py | 131 --------------------- app/templates/error.html | 0 app/templates/index.html | 156 ------------------------- app/templates/result.html | 164 --------------------------- main.py | 134 +++++++++++++++++++++- 8 files changed, 129 insertions(+), 456 deletions(-) delete mode 100644 app/__pycache__/main.cpython-312.pyc delete mode 100644 app/__pycache__/main.cpython-313.pyc delete mode 100644 app/main.py delete mode 100644 app/templates/error.html delete mode 100644 app/templates/index.html delete mode 100644 app/templates/result.html diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index d8bdf90406651fc194d037fe9b50f2b0ce25e4e6..a5ad2d8a487cc0ed61ea4c1b3bb8c8220be55b97 100644 GIT binary patch literal 5716 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 str: - """处理文本并为每种词性添加 CSS 类,同时保留段落换行""" - words = thu.cut(text) - colored_text = "" - - for word, tag in words: - if tag: # 确保 tag 不为空 - colored_text += f"{word}" - else: - colored_text += word # 没有词性时直接添加词语 - - # 替换换行符为
- return colored_text.replace("\n", "
") - - - -@app.post("/export") -async def export_file(request: ExportRequest): - format = request.format - content = request.content - - if format == "html": - file_name = "exported_result.html" - file_content = f"{content}" - return create_file_response(file_name, file_content.encode("utf-8"), "text/html") - - elif format == "pdf": - with NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file: - generate_pdf(temp_file.name, content) - return FileResponse(temp_file.name, filename="exported_result.pdf", media_type="application/pdf") - - elif format == "rtf": - file_name = "exported_result.rtf" - file_content = generate_rtf(content) - return create_file_response(file_name, file_content.encode("utf-8"), "application/rtf") - - return {"error": "Unsupported format"} - - -def create_file_response(file_name, file_content, media_type): - """ - 创建文件响应 - """ - with NamedTemporaryFile(delete=False, suffix=os.path.splitext(file_name)[1]) as temp_file: - temp_file.write(file_content) - temp_file.flush() - return FileResponse(temp_file.name, filename=file_name, media_type=media_type) - - -def generate_pdf(file_path, content): - """ - 生成 PDF 文件 - """ - c = canvas.Canvas(file_path, pagesize=letter) - c.setFont("Helvetica", 12) - width, height = letter - y = height - 40 - - for line in content.split("
"): - c.drawString(40, y, line.strip()) - y -= 20 - if y < 40: # 换页条件 - c.showPage() - c.setFont("Helvetica", 12) - y = height - 40 - c.save() - - -def generate_rtf(content): - """ - 生成 RTF 文件 - """ - rtf_header = r"{\rtf1\ansi\deff0" - rtf_footer = r"}" - rtf_body = content.replace("
", r"\line ") - return rtf_header + rtf_body + rtf_footer - diff --git a/app/templates/error.html b/app/templates/error.html deleted file mode 100644 index e69de29..0000000 diff --git a/app/templates/index.html b/app/templates/index.html deleted file mode 100644 index ef8dd38..0000000 --- a/app/templates/index.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - 词性标注工具 - - - -
-

词性标注工具

- - -
- - -
- - -
-
- - -
- -
-
-
- - -
-
- - -
- -
-
-
-
- - - - diff --git a/app/templates/result.html b/app/templates/result.html deleted file mode 100644 index b67b313..0000000 --- a/app/templates/result.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - 标注结果 - - - -

标注结果

- - - 返回 - - -
- 词性显示控制: - - - - - -
- - -
{{ content | safe }}
- -
- 导出结果: - - - -
- - - - - - diff --git a/main.py b/main.py index fb182fd..4e9616f 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,131 @@ -def main(): - print("Starting mark-word-fastapi...") - print("Hello from mark-word-fastapi!") +# from docx import Document +import os +from tempfile import NamedTemporaryFile + +import thulac +from fastapi import FastAPI, Form, UploadFile, File, Request +from fastapi.responses import FileResponse +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates +from pydantic import BaseModel +from reportlab.lib.pagesizes import letter +from reportlab.pdfgen import canvas -if __name__ == "__main__": - main() +class ExportRequest(BaseModel): + format: str # 导出格式(html、pdf、rtf) + content: str # 要导出的内容 + +# 初始化 THULAC 分词工具 +thu = thulac.thulac() + +# 初始化 FastAPI 应用和模板 +app = FastAPI() +templates = Jinja2Templates(directory="templates") + + +@app.get("/", response_class=HTMLResponse) +async def home(request: Request): + return templates.TemplateResponse("index.html", {"request": request}) + + +@app.post("/process", response_class=HTMLResponse) +async def process(request: Request, file: UploadFile = File(None), text: str = Form(None)): + """ + 统一处理上传文件和粘贴文本,返回标注结果页面 + """ + if file: + # 读取上传文件内容 + content = await file.read() + text_content = content.decode("utf-8") + elif text: + # 读取粘贴文本内容 + text_content = text + else: + return templates.TemplateResponse( + "error.html", {"request": request, "message": "未提供文件或文本"} + ) + + # 处理文本,返回标注结果 + processed_text = process_text(text) + + return templates.TemplateResponse("result.html", {"request": request, "content": processed_text}) + + +def process_text(text: str) -> str: + """处理文本并为每种词性添加 CSS 类,同时保留段落换行""" + words = thu.cut(text) + colored_text = "" + + for word, tag in words: + if tag: # 确保 tag 不为空 + colored_text += f"{word}" + else: + colored_text += word # 没有词性时直接添加词语 + + # 替换换行符为
+ return colored_text.replace("\n", "
") + + + +@app.post("/export") +async def export_file(request: ExportRequest): + format = request.format + content = request.content + + if format == "html": + file_name = "exported_result.html" + file_content = f"{content}" + return create_file_response(file_name, file_content.encode("utf-8"), "text/html") + + elif format == "pdf": + with NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file: + generate_pdf(temp_file.name, content) + return FileResponse(temp_file.name, filename="exported_result.pdf", media_type="application/pdf") + + elif format == "rtf": + file_name = "exported_result.rtf" + file_content = generate_rtf(content) + return create_file_response(file_name, file_content.encode("utf-8"), "application/rtf") + + return {"error": "Unsupported format"} + + +def create_file_response(file_name, file_content, media_type): + """ + 创建文件响应 + """ + with NamedTemporaryFile(delete=False, suffix=os.path.splitext(file_name)[1]) as temp_file: + temp_file.write(file_content) + temp_file.flush() + return FileResponse(temp_file.name, filename=file_name, media_type=media_type) + + +def generate_pdf(file_path, content): + """ + 生成 PDF 文件 + """ + c = canvas.Canvas(file_path, pagesize=letter) + c.setFont("Helvetica", 12) + width, height = letter + y = height - 40 + + for line in content.split("
"): + c.drawString(40, y, line.strip()) + y -= 20 + if y < 40: # 换页条件 + c.showPage() + c.setFont("Helvetica", 12) + y = height - 40 + c.save() + + +def generate_rtf(content): + """ + 生成 RTF 文件 + """ + rtf_header = r"{\rtf1\ansi\deff0" + rtf_footer = r"}" + rtf_body = content.replace("
", r"\line ") + return rtf_header + rtf_body + rtf_footer +