Spaces:
Runtime error
Runtime error
Ultra-simple version: direct CSV loading, no database
Browse files- Dockerfile +9 -30
- apps/api/main_hf_simple.py +115 -0
Dockerfile
CHANGED
|
@@ -1,47 +1,26 @@
|
|
| 1 |
-
# Hugging Face Spaces Dockerfile
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
| 4 |
WORKDIR /app
|
| 5 |
|
| 6 |
-
# 安装
|
| 7 |
-
RUN apt-get update && apt-get install -y \
|
| 8 |
-
sqlite3 \
|
| 9 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
-
|
| 11 |
-
# 安装Python依赖(精简版)
|
| 12 |
RUN pip install --no-cache-dir \
|
| 13 |
fastapi==0.111.0 \
|
| 14 |
uvicorn[standard]==0.30.0 \
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
pydantic==2.7.4 \
|
| 18 |
-
pydantic-settings==2.3.0 \
|
| 19 |
-
python-multipart==0.0.9
|
| 20 |
-
|
| 21 |
-
# 复制配置文件(使用HF版本)
|
| 22 |
-
COPY packages/core/config_hf.py ./packages/core/config.py
|
| 23 |
-
COPY packages/core/__init__.py ./packages/core/
|
| 24 |
-
COPY packages/core/models.py ./packages/core/
|
| 25 |
-
COPY packages/core/database.py ./packages/core/
|
| 26 |
-
COPY packages/core/logger.py ./packages/core/
|
| 27 |
-
|
| 28 |
-
# 复制API路由(使用精简版trade路由)
|
| 29 |
-
COPY apps/api/routers/__init__.py ./apps/api/routers/
|
| 30 |
-
COPY apps/api/routers/trade_hf.py ./apps/api/routers/trade.py
|
| 31 |
-
COPY apps/api/schemas/ ./apps/api/schemas/
|
| 32 |
|
| 33 |
-
# 复制
|
| 34 |
-
COPY apps/api/main_hf.py ./apps/api/main.py
|
| 35 |
COPY apps/api/static/ ./apps/api/static/
|
| 36 |
|
| 37 |
# 复制数据文件
|
| 38 |
COPY data/standard_trade_records_sample.csv ./data/
|
| 39 |
|
| 40 |
-
#
|
| 41 |
-
COPY
|
| 42 |
|
| 43 |
# 暴露端口
|
| 44 |
EXPOSE 7860
|
| 45 |
|
| 46 |
-
# 启动命令
|
| 47 |
-
CMD ["
|
|
|
|
| 1 |
+
# Hugging Face Spaces Dockerfile - 超简版
|
| 2 |
FROM python:3.11-slim
|
| 3 |
|
| 4 |
WORKDIR /app
|
| 5 |
|
| 6 |
+
# 只安装必要的Python依赖
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
RUN pip install --no-cache-dir \
|
| 8 |
fastapi==0.111.0 \
|
| 9 |
uvicorn[standard]==0.30.0 \
|
| 10 |
+
pandas>=2.0.0 \
|
| 11 |
+
pydantic==2.7.4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
+
# 复制静态文件
|
|
|
|
| 14 |
COPY apps/api/static/ ./apps/api/static/
|
| 15 |
|
| 16 |
# 复制数据文件
|
| 17 |
COPY data/standard_trade_records_sample.csv ./data/
|
| 18 |
|
| 19 |
+
# 复制超简版主文件
|
| 20 |
+
COPY apps/api/main_hf_simple.py ./apps/api/main.py
|
| 21 |
|
| 22 |
# 暴露端口
|
| 23 |
EXPOSE 7860
|
| 24 |
|
| 25 |
+
# 启动命令
|
| 26 |
+
CMD ["uvicorn", "apps.api.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
apps/api/main_hf_simple.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Hugging Face Spaces 超简版 - 直接内联数据加载
|
| 3 |
+
"""
|
| 4 |
+
from fastapi import FastAPI, Request
|
| 5 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 6 |
+
from fastapi.staticfiles import StaticFiles
|
| 7 |
+
from fastapi.responses import FileResponse
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
import pandas as pd
|
| 10 |
+
|
| 11 |
+
app = FastAPI(
|
| 12 |
+
title="海关数据查询系统",
|
| 13 |
+
description="巴西海关贸易数据查询服务",
|
| 14 |
+
version="1.0.0"
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
app.add_middleware(
|
| 18 |
+
CORSMiddleware,
|
| 19 |
+
allow_origins=["*"],
|
| 20 |
+
allow_credentials=True,
|
| 21 |
+
allow_methods=["*"],
|
| 22 |
+
allow_headers=["*"],
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
| 26 |
+
STATIC_DIR = BASE_DIR / "apps" / "api" / "static"
|
| 27 |
+
|
| 28 |
+
# 挂载静态文件
|
| 29 |
+
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
| 30 |
+
|
| 31 |
+
@app.get("/")
|
| 32 |
+
async def serve_ui():
|
| 33 |
+
"""返回前端页面"""
|
| 34 |
+
return FileResponse(str(STATIC_DIR / "index.html"))
|
| 35 |
+
|
| 36 |
+
@app.get("/api/v1/health")
|
| 37 |
+
async def health_check():
|
| 38 |
+
"""健康检查"""
|
| 39 |
+
return {"status": "ok", "service": "Customs Data API"}
|
| 40 |
+
|
| 41 |
+
@app.post("/api/v1/trade/search")
|
| 42 |
+
async def search_trade_records(request: Request):
|
| 43 |
+
"""
|
| 44 |
+
查询贸易记录 - 直接从CSV读取
|
| 45 |
+
"""
|
| 46 |
+
try:
|
| 47 |
+
body = await request.json()
|
| 48 |
+
except:
|
| 49 |
+
body = {}
|
| 50 |
+
|
| 51 |
+
# 提取参数
|
| 52 |
+
source_country = body.get('source_country')
|
| 53 |
+
trade_direction = body.get('trade_direction')
|
| 54 |
+
hs_code = body.get('hs_code')
|
| 55 |
+
product_name = body.get('product_name')
|
| 56 |
+
importer_name = body.get('importer_name')
|
| 57 |
+
exporter_name = body.get('exporter_name')
|
| 58 |
+
page = int(body.get('page', 1))
|
| 59 |
+
limit = int(body.get('limit', 20))
|
| 60 |
+
|
| 61 |
+
# 加载CSV
|
| 62 |
+
csv_path = BASE_DIR / "data" / "standard_trade_records_sample.csv"
|
| 63 |
+
df = pd.read_csv(csv_path)
|
| 64 |
+
|
| 65 |
+
# 过滤
|
| 66 |
+
filtered_df = df.copy()
|
| 67 |
+
if source_country:
|
| 68 |
+
filtered_df = filtered_df[filtered_df['source_country'] == source_country]
|
| 69 |
+
if trade_direction:
|
| 70 |
+
filtered_df = filtered_df[filtered_df['trade_direction'] == trade_direction]
|
| 71 |
+
if hs_code:
|
| 72 |
+
filtered_df = filtered_df[filtered_df['hs_code'].astype(str).str.startswith(str(hs_code))]
|
| 73 |
+
if product_name:
|
| 74 |
+
filtered_df = filtered_df[filtered_df['product_name'].str.contains(product_name, na=False, case=False)]
|
| 75 |
+
if importer_name:
|
| 76 |
+
filtered_df = filtered_df[filtered_df['importer_name'].str.contains(importer_name, na=False, case=False)]
|
| 77 |
+
if exporter_name:
|
| 78 |
+
filtered_df = filtered_df[filtered_df['exporter_name'].str.contains(exporter_name, na=False, case=False)]
|
| 79 |
+
|
| 80 |
+
total = len(filtered_df)
|
| 81 |
+
|
| 82 |
+
# 分页
|
| 83 |
+
offset = (page - 1) * limit
|
| 84 |
+
page_df = filtered_df.iloc[offset:offset + limit]
|
| 85 |
+
|
| 86 |
+
# 构建响应
|
| 87 |
+
items = []
|
| 88 |
+
for _, row in page_df.iterrows():
|
| 89 |
+
trade_date_str = str(row['trade_date']).split()[0] if pd.notna(row['trade_date']) else '2023-01-01'
|
| 90 |
+
items.append({
|
| 91 |
+
'record_id': str(row['record_id']),
|
| 92 |
+
'source_record_id': str(row['source_record_id']),
|
| 93 |
+
'source_country': str(row['source_country']),
|
| 94 |
+
'trade_direction': str(row['trade_direction']),
|
| 95 |
+
'trade_date': trade_date_str,
|
| 96 |
+
'importer_name': str(row['importer_name']) if pd.notna(row['importer_name']) else "",
|
| 97 |
+
'exporter_name': str(row['exporter_name']) if pd.notna(row['exporter_name']) else "",
|
| 98 |
+
'hs_code': str(row['hs_code']) if pd.notna(row['hs_code']) else None,
|
| 99 |
+
'product_name': str(row['product_name']) if pd.notna(row['product_name']) else None,
|
| 100 |
+
'amount': float(row['amount']) if pd.notna(row['amount']) else None,
|
| 101 |
+
'currency': str(row['currency']) if pd.notna(row['currency']) else None,
|
| 102 |
+
'weight': float(row['weight']) if pd.notna(row['weight']) else None,
|
| 103 |
+
'weight_unit': str(row['weight_unit']) if pd.notna(row['weight_unit']) else None,
|
| 104 |
+
'origin_country': str(row['origin_country']) if pd.notna(row['origin_country']) else None,
|
| 105 |
+
'destination_country': str(row['destination_country']) if pd.notna(row['destination_country']) else None,
|
| 106 |
+
'departure_port': str(row['departure_port']) if pd.notna(row['departure_port']) else None,
|
| 107 |
+
'arrival_port': str(row['arrival_port']) if pd.notna(row['arrival_port']) else None,
|
| 108 |
+
'transport_mode': str(row['transport_mode']) if pd.notna(row['transport_mode']) else None
|
| 109 |
+
})
|
| 110 |
+
|
| 111 |
+
return {'total': total, 'items': items}
|
| 112 |
+
|
| 113 |
+
if __name__ == "__main__":
|
| 114 |
+
import uvicorn
|
| 115 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|