From 345c571dda7e449b03b607a54ad63d2d4c898558 Mon Sep 17 00:00:00 2001 From: mxr612 Date: Mon, 16 Jun 2025 03:50:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=AE=BF=E9=97=AE=E5=8A=9F=E8=83=BD=E5=B9=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0favicon=E5=92=8Crobots.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增公共文件访问接口,允许从public目录获取文件 - 添加favicon.ico以改善网站标识 - 新增robots.txt以指导搜索引擎爬虫行为 --- app.py | 224 +++++++++++++++++++++++--------------------- public/robots.txt | 4 + static/favicon.ico | Bin 0 -> 205086 bytes templates/base.html | 1 + 4 files changed, 121 insertions(+), 108 deletions(-) create mode 100644 public/robots.txt create mode 100644 static/favicon.ico diff --git a/app.py b/app.py index c0242f2..fe93ced 100644 --- a/app.py +++ b/app.py @@ -1,109 +1,117 @@ -from fastapi import FastAPI, Request, HTTPException -from fastapi.responses import HTMLResponse -import markdown -from fastapi.staticfiles import StaticFiles -from fastapi.templating import Jinja2Templates -import json, yaml -import os -import uvicorn - -app = FastAPI() -templates = Jinja2Templates(directory="templates") -app.mount("/static", StaticFiles(directory="static"), name="static") - -# 加载所有问卷数据 -def load_all_scales(): - scales = {} - tags = [] - for root, dirs, files in os.walk(os.path.realpath('scales')): - for filename in files: - if filename.endswith(('.yaml', '.yml')): - try: - with open(os.path.join(root, filename), 'r', encoding='utf-8') as f: - scale = yaml.safe_load(f) - scale['instructions']=markdown.markdown(scale['instructions'], extensions=['fenced_code','tables','mdx_math']) - scale['descriptions']=markdown.markdown(scale['descriptions'], extensions=['fenced_code','tables','mdx_math']) - scale['abstract']=markdown.markdown(scale['abstract'], extensions=['fenced_code','tables','mdx_math']) - if 'tag' not in scale: - scale['tag']='其他' - if scale['tag'] not in tags: - tags.append(scale['tag']) - scale_id = os.path.splitext(filename)[0] # 使用文件名作为标识 - scales[scale_id] = scale - except Exception as e: - print(f"Error loading scale {filename}: {e}") - return tags, scales - -@app.get("/", response_class=HTMLResponse) -async def index(request: Request): - tags, _ = load_all_scales() - # 新增读取README.md的逻辑 - readme_content = "" - try: - with open("README.md", "r", encoding="utf-8") as f: - readme_content = markdown.markdown(f.read()) - except FileNotFoundError: - pass # 如果README不存在则静默失败 - return templates.TemplateResponse("index.html", { - "request": request, - "tags": tags, - "readme_content": readme_content # 新增模板变量 - }) - -@app.get("/tag/{tag}", response_class=HTMLResponse) -async def list(request: Request, tag: str): - tags, scales = load_all_scales() - return templates.TemplateResponse("list.html", { - "request": request, - "tags": tags, - "scales": scales, - "tag": tag - }) - -@app.get("/scales/{scale_id}", response_class=HTMLResponse) -async def scale(request: Request, scale_id: str): - tags, scales = load_all_scales() - scale = scales.get(scale_id) - if scale: - return templates.TemplateResponse("scale.html", { - "request": request, - "scale_id": scale_id, - "scale": scale, - "tags":tags - }) - raise HTTPException(status_code=404, detail="问卷未找到") - -@app.post("/scales/{scale_id}", response_class=HTMLResponse) -async def result(request: Request, scale_id: str): - form_data = await request.form() - tags, scales = load_all_scales() - scale = scales.get(scale_id) - if scale: - # 这里可以添加保存数据到数据库等逻辑 - responses = {} - average = {} - options = {} - for subscale, qids in scale['subscales'].items(): - responses[subscale] = 0 - min_val = min(scale['options'].keys()) - max_val = max(scale['options'].keys()) - options[subscale] = [min_val*len(qids),max_val*len(qids)] - for qid in qids: - if qid<0: - responses[subscale] += min_val + max_val - int(form_data[str(-qid)]) - else: - responses[subscale] += int(form_data[str(qid)]) - average[subscale] = round(responses[subscale]/len(qids),2) - return templates.TemplateResponse("result.html", { - "request": request, - "responses": responses, - "average": average, - "options": options, - "scale": scale, - "tags":tags - }) - raise HTTPException(status_code=404, detail="问卷未找到") - - -if __name__ == '__main__': +from fastapi import FastAPI, Request, HTTPException +from fastapi.responses import HTMLResponse, FileResponse +import markdown +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +import json, yaml +import os +import uvicorn + +app = FastAPI() +templates = Jinja2Templates(directory="templates") +app.mount("/static", StaticFiles(directory="static"), name="static") + +# Mount all files from public directory to root +@app.get("/{filename}") +async def get_public_file(filename: str): + public_path = os.path.join("public", filename) + if os.path.isfile(public_path): + return FileResponse(public_path) + raise HTTPException(status_code=404, detail="File not found") + +# 加载所有问卷数据 +def load_all_scales(): + scales = {} + tags = [] + for root, dirs, files in os.walk(os.path.realpath('scales')): + for filename in files: + if filename.endswith(('.yaml', '.yml')): + try: + with open(os.path.join(root, filename), 'r', encoding='utf-8') as f: + scale = yaml.safe_load(f) + scale['instructions']=markdown.markdown(scale['instructions'], extensions=['fenced_code','tables','mdx_math']) + scale['descriptions']=markdown.markdown(scale['descriptions'], extensions=['fenced_code','tables','mdx_math']) + scale['abstract']=markdown.markdown(scale['abstract'], extensions=['fenced_code','tables','mdx_math']) + if 'tag' not in scale: + scale['tag']='其他' + if scale['tag'] not in tags: + tags.append(scale['tag']) + scale_id = os.path.splitext(filename)[0] # 使用文件名作为标识 + scales[scale_id] = scale + except Exception as e: + print(f"Error loading scale {filename}: {e}") + return tags, scales + +@app.get("/", response_class=HTMLResponse) +async def index(request: Request): + tags, _ = load_all_scales() + # 新增读取README.md的逻辑 + readme_content = "" + try: + with open("README.md", "r", encoding="utf-8") as f: + readme_content = markdown.markdown(f.read()) + except FileNotFoundError: + pass # 如果README不存在则静默失败 + return templates.TemplateResponse("index.html", { + "request": request, + "tags": tags, + "readme_content": readme_content # 新增模板变量 + }) + +@app.get("/tag/{tag}", response_class=HTMLResponse) +async def list(request: Request, tag: str): + tags, scales = load_all_scales() + return templates.TemplateResponse("list.html", { + "request": request, + "tags": tags, + "scales": scales, + "tag": tag + }) + +@app.get("/scales/{scale_id}", response_class=HTMLResponse) +async def scale(request: Request, scale_id: str): + tags, scales = load_all_scales() + scale = scales.get(scale_id) + if scale: + return templates.TemplateResponse("scale.html", { + "request": request, + "scale_id": scale_id, + "scale": scale, + "tags":tags + }) + raise HTTPException(status_code=404, detail="问卷未找到") + +@app.post("/scales/{scale_id}", response_class=HTMLResponse) +async def result(request: Request, scale_id: str): + form_data = await request.form() + tags, scales = load_all_scales() + scale = scales.get(scale_id) + if scale: + # 这里可以添加保存数据到数据库等逻辑 + responses = {} + average = {} + options = {} + for subscale, qids in scale['subscales'].items(): + responses[subscale] = 0 + min_val = min(scale['options'].keys()) + max_val = max(scale['options'].keys()) + options[subscale] = [min_val*len(qids),max_val*len(qids)] + for qid in qids: + if qid<0: + responses[subscale] += min_val + max_val - int(form_data[str(-qid)]) + else: + responses[subscale] += int(form_data[str(qid)]) + average[subscale] = round(responses[subscale]/len(qids),2) + return templates.TemplateResponse("result.html", { + "request": request, + "responses": responses, + "average": average, + "options": options, + "scale": scale, + "tags":tags + }) + raise HTTPException(status_code=404, detail="问卷未找到") + + +if __name__ == '__main__': uvicorn.run(app,host='0.0.0.0',port=8000) \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..9d3dce5 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +# Sitemap: /sitemap.xml diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..32b2216e98286926b0e62d0e8e7f79953f5f4ae0 GIT binary patch literal 205086 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 literal 0 HcmV?d00001 diff --git a/templates/base.html b/templates/base.html index 8f607f4..6e416af 100644 --- a/templates/base.html +++ b/templates/base.html @@ -8,6 +8,7 @@ + {% block head_extra %}{% endblock %}