From ee9e6a5ccae05e5aa33a995bfd38dcd372f02b95 Mon Sep 17 00:00:00 2001 From: mxr612 Date: Mon, 16 Jun 2025 06:13:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BAIP=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=8A=9F=E8=83=BD=E4=BB=A5=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=9C=B0=E7=90=86=E4=BD=8D=E7=BD=AE=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在app.py中新增GeoIP2数据库支持,获取用户IP的地理位置信息 - 更新RawResponse模型,新增location、raw_response、sum_response和avg_response字段以存储更多用户响应数据 - 修改结果处理逻辑,保存用户的地理位置信息和响应数据 - 更新.gitignore以排除GeoLite2-City.mmdb文件 --- .gitignore | 3 ++- app.py | 52 +++++++++++++++++++++++++++++++++++------------ database.py | 13 ++++++++---- requirements.txt | Bin 212 -> 228 bytes 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index cac1531..5b889eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ scales/ +__pycache__/ psychoscales.db -__pycache__/ \ No newline at end of file +GeoLite2-City.mmdb \ No newline at end of file diff --git a/app.py b/app.py index bf754f4..7ba1d72 100644 --- a/app.py +++ b/app.py @@ -10,11 +10,34 @@ from datetime import datetime from xml.etree import ElementTree as ET from sqlalchemy.orm import Session from database import get_db, RawResponse +import geoip2.database app = FastAPI() templates = Jinja2Templates(directory="templates") app.mount("/static", StaticFiles(directory="static"), name="static") +# Initialize GeoIP2 reader +try: + geoip_reader = geoip2.database.Reader('GeoLite2-City.mmdb') +except FileNotFoundError: + print("Warning: GeoLite2-City.mmdb not found. IP location lookup will be disabled.") + geoip_reader = None + +def get_location_from_ip(ip): + if not geoip_reader: + return None + try: + response = geoip_reader.city(ip) + return { + 'country': response.country.name, + 'city': response.city.name, + 'latitude': response.location.latitude, + 'longitude': response.location.longitude + } + except Exception as e: + print(f"Error getting location for IP {ip}: {e}") + return None + # 加载所有问卷数据 def load_all_scales(): scales = {} @@ -83,19 +106,6 @@ async def result(request: Request, scale_id: str, db: Session = Depends(get_db)) tags, scales = load_all_scales() scale = scales.get(scale_id) if scale: - # Save response to database - # Get real IP address considering proxy headers - ip = request.headers.get("X-Forwarded-For", "").split(",")[0].strip() or \ - request.headers.get("X-Real-IP", "") or \ - request.client.host - db_response = RawResponse( - scale_id=scale_id, - user_agent=request.headers.get("user-agent", "Unknown"), - ip_address=ip, - response=dict(form_data) - ) - db.add(db_response) - db.commit() responses = {} average = {} options = {} @@ -110,6 +120,22 @@ async def result(request: Request, scale_id: str, db: Session = Depends(get_db)) else: responses[subscale] += int(form_data[str(qid)]) average[subscale] = round(responses[subscale]/len(qids),2) + # Save response to database + ip = request.headers.get("X-Forwarded-For", "").split(",")[0].strip() or \ + request.headers.get("X-Real-IP", "") or \ + request.client.host # Get real IP address considering proxy headers + location = get_location_from_ip(ip)# Get location information + db_response = RawResponse( + scale_id=scale_id, + user_agent=request.headers.get("user-agent", "Unknown"), + ip_address=ip, + location=json.dumps(location) if location else None, + raw_response=dict(form_data), + sum_response=responses, + avg_response=average + ) + db.add(db_response) + db.commit() return templates.TemplateResponse("result.html", { "request": request, "responses": responses, diff --git a/database.py b/database.py index 77038a0..344e544 100644 --- a/database.py +++ b/database.py @@ -2,24 +2,29 @@ from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship from datetime import datetime, UTC +import json SQLALCHEMY_DATABASE_URL = "sqlite:///./psychoscales.db" engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} + SQLALCHEMY_DATABASE_URL, + connect_args={"check_same_thread": False}, + json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False) ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() class RawResponse(Base): - __tablename__ = "responses_raw" - + __tablename__ = "responses" id = Column(Integer, primary_key=True, index=True) scale_id = Column(String, index=True) user_agent = Column(String) ip_address = Column(String) - response = Column(JSON) + location = Column(String) + raw_response = Column(JSON) + sum_response = Column(JSON) + avg_response = Column(JSON) created_at = Column(DateTime, default=datetime.now(UTC)) # Create tables diff --git a/requirements.txt b/requirements.txt index 84fd9a0ee438a8e5ec2be88ee022c87024d138c6..e18128ebdbaeab1e10aef6637dde07ff0c6951f1 100644 GIT binary patch delta 23 ecmcb@_=Iu76#-rbE{1f5REB(pOojpmBL)Cc-35#Q delta 6 NcmaFDc!hDo6#xpV10MhY