feat: 增强IP地址记录功能以支持地理位置获取
- 在app.py中新增GeoIP2数据库支持,获取用户IP的地理位置信息 - 更新RawResponse模型,新增location、raw_response、sum_response和avg_response字段以存储更多用户响应数据 - 修改结果处理逻辑,保存用户的地理位置信息和响应数据 - 更新.gitignore以排除GeoLite2-City.mmdb文件
This commit is contained in:
parent
e08909eacd
commit
ee9e6a5cca
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
scales/
|
scales/
|
||||||
|
__pycache__/
|
||||||
psychoscales.db
|
psychoscales.db
|
||||||
__pycache__/
|
GeoLite2-City.mmdb
|
52
app.py
52
app.py
@ -10,11 +10,34 @@ from datetime import datetime
|
|||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from database import get_db, RawResponse
|
from database import get_db, RawResponse
|
||||||
|
import geoip2.database
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
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():
|
def load_all_scales():
|
||||||
scales = {}
|
scales = {}
|
||||||
@ -83,19 +106,6 @@ async def result(request: Request, scale_id: str, db: Session = Depends(get_db))
|
|||||||
tags, scales = load_all_scales()
|
tags, scales = load_all_scales()
|
||||||
scale = scales.get(scale_id)
|
scale = scales.get(scale_id)
|
||||||
if scale:
|
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 = {}
|
responses = {}
|
||||||
average = {}
|
average = {}
|
||||||
options = {}
|
options = {}
|
||||||
@ -110,6 +120,22 @@ async def result(request: Request, scale_id: str, db: Session = Depends(get_db))
|
|||||||
else:
|
else:
|
||||||
responses[subscale] += int(form_data[str(qid)])
|
responses[subscale] += int(form_data[str(qid)])
|
||||||
average[subscale] = round(responses[subscale]/len(qids),2)
|
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", {
|
return templates.TemplateResponse("result.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
"responses": responses,
|
"responses": responses,
|
||||||
|
13
database.py
13
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.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import sessionmaker, relationship
|
from sqlalchemy.orm import sessionmaker, relationship
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
|
import json
|
||||||
|
|
||||||
SQLALCHEMY_DATABASE_URL = "sqlite:///./psychoscales.db"
|
SQLALCHEMY_DATABASE_URL = "sqlite:///./psychoscales.db"
|
||||||
|
|
||||||
engine = create_engine(
|
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)
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
class RawResponse(Base):
|
class RawResponse(Base):
|
||||||
__tablename__ = "responses_raw"
|
__tablename__ = "responses"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
scale_id = Column(String, index=True)
|
scale_id = Column(String, index=True)
|
||||||
user_agent = Column(String)
|
user_agent = Column(String)
|
||||||
ip_address = 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))
|
created_at = Column(DateTime, default=datetime.now(UTC))
|
||||||
|
|
||||||
# Create tables
|
# Create tables
|
||||||
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user