evcook / app.py
seawolf2357's picture
Update app.py
f55df86 verified
# app.py - ์˜ฌ๋ฐ”๋ฅธ API ์ฒ˜๋ฆฌ๊ฐ€ ํฌํ•จ๋œ ์ตœ์ข… ๋ฒ„์ „
import gradio as gr
import sqlite3
import json
import uuid
from datetime import datetime
import pandas as pd
import os
import threading
# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒŒ์ผ ๊ฒฝ๋กœ
DB_FILE = "tracking_data.db"
# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”
def init_database():
"""SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™” ๋ฐ ํ…Œ์ด๋ธ” ์ƒ์„ฑ"""
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
# ๋ฐฉ๋ฌธ์ž ํ…Œ์ด๋ธ” ์ƒ์„ฑ
cursor.execute("""
CREATE TABLE IF NOT EXISTS visitors (
device_id TEXT PRIMARY KEY,
first_seen TIMESTAMP NOT NULL,
last_seen TIMESTAMP NOT NULL,
user_agent TEXT,
platform TEXT,
language TEXT,
screen_resolution TEXT,
cpu_cores INTEGER,
device_memory REAL,
gpu_info TEXT,
timezone TEXT
)
""")
# ์ด๋ฒคํŠธ ํ…Œ์ด๋ธ” ์ƒ์„ฑ
cursor.execute("""
CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
site_id TEXT NOT NULL,
event_type TEXT NOT NULL,
event_data TEXT,
page_url TEXT,
page_title TEXT,
referrer TEXT,
client_timestamp TIMESTAMP,
server_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES visitors(device_id)
)
""")
# ์ธ๋ฑ์Šค ์ƒ์„ฑ (์ฟผ๋ฆฌ ์„ฑ๋Šฅ ํ–ฅ์ƒ)
cursor.execute("CREATE INDEX IF NOT EXISTS idx_device_id ON events(device_id)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_timestamp ON events(server_timestamp)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_event_type ON events(event_type)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_site_id ON events(site_id)")
conn.commit()
conn.close()
print(f"โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™” ์™„๋ฃŒ: {DB_FILE}")
# ์•ฑ ์‹œ์ž‘ ์‹œ DB ์ดˆ๊ธฐํ™”
init_database()
# ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ์„ ์œ„ํ•œ ๋ฝ
db_lock = threading.Lock()
def process_tracking_data(data_json):
"""์ถ”์  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์„œ SQLite DB์— ์ €์žฅ"""
with db_lock:
conn = None
try:
# ๋กœ๊น… ์ถ”๊ฐ€
print(f"[API] Received data: {data_json[:200] if isinstance(data_json, str) else data_json}")
# JSON ํŒŒ์‹ฑ
data = json.loads(data_json) if isinstance(data_json, str) else data_json
# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
# ํ•„์ˆ˜ ํ•„๋“œ ํ™•์ธ
device_id = data.get('deviceId', 'unknown')
site_id = data.get('siteId', 'unknown')
# 1. ๋ฐฉ๋ฌธ์ž ์ •๋ณด ์—…๋ฐ์ดํŠธ (UPSERT)
cursor.execute("""
SELECT device_id FROM visitors WHERE device_id = ?
""", (device_id,))
if cursor.fetchone() is None:
# ์‹ ๊ทœ ๋ฐฉ๋ฌธ์ž ์‚ฝ์ž…
cursor.execute("""
INSERT INTO visitors (
device_id, first_seen, last_seen, user_agent, platform,
language, screen_resolution, cpu_cores, device_memory,
gpu_info, timezone
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
device_id,
datetime.now(),
datetime.now(),
data.get('userAgent', ''),
data.get('platform', ''),
data.get('language', ''),
data.get('screenResolution', ''),
data.get('cpuCores', 0),
data.get('deviceMemory', 0),
data.get('gpuInfo', ''),
data.get('timezone', '')
))
print(f"๐Ÿ†• ์‹ ๊ทœ ๋ฐฉ๋ฌธ์ž ๋“ฑ๋ก: {device_id}")
else:
# ๊ธฐ์กด ๋ฐฉ๋ฌธ์ž ์—…๋ฐ์ดํŠธ
cursor.execute("""
UPDATE visitors
SET last_seen = ?,
user_agent = ?,
platform = ?,
language = ?,
screen_resolution = ?,
cpu_cores = ?,
device_memory = ?,
gpu_info = ?,
timezone = ?
WHERE device_id = ?
""", (
datetime.now(),
data.get('userAgent', ''),
data.get('platform', ''),
data.get('language', ''),
data.get('screenResolution', ''),
data.get('cpuCores', 0),
data.get('deviceMemory', 0),
data.get('gpuInfo', ''),
data.get('timezone', ''),
device_id
))
# 2. ์ด๋ฒคํŠธ ์ €์žฅ
event_data = data.get('eventData', {})
cursor.execute("""
INSERT INTO events (
device_id, site_id, event_type, event_data,
page_url, page_title, referrer, client_timestamp
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", (
device_id,
site_id,
data.get('eventType', 'pageview'),
json.dumps(event_data),
data.get('pageUrl', ''),
data.get('pageTitle', ''),
data.get('referrer', ''),
data.get('timestamp', datetime.now().isoformat())
))
tracking_id = cursor.lastrowid
conn.commit()
print(f"โœ… ์ด๋ฒคํŠธ ์ €์žฅ: ID={tracking_id}, Type={data.get('eventType')}, Device={device_id}")
return {
"status": "success",
"message": "Data tracked successfully",
"trackingId": tracking_id,
"deviceId": device_id
}
except Exception as e:
if conn:
conn.rollback()
print(f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
return {"status": "error", "message": str(e)}
finally:
if conn:
conn.close()
def get_statistics():
"""DB์—์„œ ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ ์กฐํšŒ"""
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
try:
# ์ด ๋ฐฉ๋ฌธ์ž ์ˆ˜
cursor.execute("SELECT COUNT(*) FROM visitors")
total_visitors = cursor.fetchone()[0]
# ์˜ค๋Š˜ ๋ฐฉ๋ฌธ์ž ์ˆ˜
cursor.execute("""
SELECT COUNT(DISTINCT device_id) FROM events
WHERE DATE(server_timestamp) = DATE('now', 'localtime')
""")
today_visitors = cursor.fetchone()[0]
# ์ด ์ด๋ฒคํŠธ ์ˆ˜
cursor.execute("SELECT COUNT(*) FROM events")
total_events = cursor.fetchone()[0]
# ์ด๋ฒคํŠธ ํƒ€์ž…๋ณ„ ํ†ต๊ณ„
cursor.execute("""
SELECT event_type, COUNT(*) as count
FROM events
GROUP BY event_type
ORDER BY count DESC
""")
event_types = cursor.fetchall()
# ์‚ฌ์ดํŠธ๋ณ„ ํ†ต๊ณ„
cursor.execute("""
SELECT site_id, COUNT(*) as count
FROM events
GROUP BY site_id
ORDER BY count DESC
LIMIT 5
""")
site_stats = cursor.fetchall()
# ์ตœ๊ทผ ์ด๋ฒคํŠธ
cursor.execute("""
SELECT
e.server_timestamp,
e.event_type,
e.device_id,
e.page_url,
e.site_id
FROM events e
ORDER BY e.id DESC
LIMIT 15
""")
recent_events = cursor.fetchall()
# ์‹œ๊ฐ„๋Œ€๋ณ„ ํ†ต๊ณ„
cursor.execute("""
SELECT
strftime('%H', server_timestamp) as hour,
COUNT(*) as count
FROM events
WHERE DATE(server_timestamp) = DATE('now', 'localtime')
GROUP BY hour
ORDER BY hour
""")
hourly_stats = cursor.fetchall()
return {
'total_visitors': total_visitors,
'today_visitors': today_visitors,
'total_events': total_events,
'event_types': event_types,
'site_stats': site_stats,
'recent_events': recent_events,
'hourly_stats': hourly_stats
}
finally:
conn.close()
def view_statistics():
"""ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ ํฌ๋งทํŒ…ํ•˜์—ฌ ํ‘œ์‹œ"""
stats = get_statistics()
summary = f"""
### ๐Ÿ“Š ๋ฐฉ๋ฌธ์ž ํ†ต๊ณ„
- **์ด ๋ฐฉ๋ฌธ์ž**: {stats['total_visitors']}๋ช…
- **์˜ค๋Š˜ ๋ฐฉ๋ฌธ์ž**: {stats['today_visitors']}๋ช…
- **์ด ์ด๋ฒคํŠธ**: {stats['total_events']}ํšŒ
- **๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
### ๐Ÿ“ˆ ์ด๋ฒคํŠธ ํƒ€์ž…๋ณ„ ํ†ต๊ณ„
"""
for event_type, count in stats['event_types']:
summary += f"\n- **{event_type}**: {count:,}ํšŒ"
if stats['site_stats']:
summary += "\n\n### ๐ŸŒ ์‚ฌ์ดํŠธ๋ณ„ ํ†ต๊ณ„ (์ƒ์œ„ 5๊ฐœ)"
for site_id, count in stats['site_stats']:
summary += f"\n- **{site_id}**: {count:,}ํšŒ"
if stats['hourly_stats']:
summary += "\n\n### โฐ ์˜ค๋Š˜ ์‹œ๊ฐ„๋Œ€๋ณ„ ํ™œ๋™"
for hour, count in stats['hourly_stats']:
summary += f"\n- **{hour}์‹œ**: {count}ํšŒ"
summary += "\n\n### ๐Ÿ“ ์ตœ๊ทผ ์ด๋ฒคํŠธ (์ตœ๋Œ€ 15๊ฐœ)"
for event in stats['recent_events']:
timestamp, event_type, device_id, page_url, site_id = event
summary += f"\n- {timestamp} | {event_type} | {device_id[:8]}... | {site_id} | {page_url[:40]}..."
return summary
def get_recent_data():
"""์ตœ๊ทผ ๋ฐ์ดํ„ฐ๋ฅผ DataFrame์œผ๋กœ ๋ฐ˜ํ™˜"""
conn = sqlite3.connect(DB_FILE)
try:
query = """
SELECT
e.server_timestamp as 'Timestamp',
e.device_id as 'Device ID',
e.event_type as 'Event Type',
e.page_url as 'Page URL',
v.platform as 'Platform',
v.language as 'Language',
e.site_id as 'Site ID'
FROM events e
JOIN visitors v ON e.device_id = v.device_id
ORDER BY e.id DESC
LIMIT 200
"""
df = pd.read_sql_query(query, conn)
return df
finally:
conn.close()
def export_data(start_date, end_date):
"""๋ฐ์ดํ„ฐ๋ฅผ CSV๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ"""
conn = sqlite3.connect(DB_FILE)
try:
query = """
SELECT
e.id,
e.server_timestamp,
e.client_timestamp,
e.device_id,
e.site_id,
e.event_type,
e.event_data,
e.page_url,
e.page_title,
e.referrer,
v.user_agent,
v.platform,
v.language,
v.screen_resolution,
v.cpu_cores,
v.device_memory,
v.gpu_info,
v.timezone,
v.first_seen,
v.last_seen
FROM events e
JOIN visitors v ON e.device_id = v.device_id
WHERE DATE(e.server_timestamp) BETWEEN ? AND ?
ORDER BY e.id DESC
"""
df = pd.read_sql_query(query, conn, params=(start_date, end_date))
# CSV ํŒŒ์ผ๋กœ ์ €์žฅ
filename = f"tracking_export_{start_date}_to_{end_date}.csv"
df.to_csv(filename, index=False)
return filename, f"โœ… ๋ฐ์ดํ„ฐ๋ฅผ {filename} ํŒŒ์ผ๋กœ ๋‚ด๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค. ({len(df):,}๊ฐœ ๋ ˆ์ฝ”๋“œ)"
except Exception as e:
return None, f"โŒ ๋‚ด๋ณด๋‚ด๊ธฐ ์‹คํŒจ: {str(e)}"
finally:
conn.close()
def test_tracking():
"""ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ"""
test_data = {
"siteId": "test-site",
"deviceId": f"TEST_{str(uuid.uuid4())[:8]}",
"eventType": "test_event",
"eventData": {"test": True, "timestamp": datetime.now().isoformat()},
"pageUrl": "https://test.example.com/page",
"pageTitle": "Test Page",
"referrer": "https://google.com",
"userAgent": "Test Browser/1.0",
"screenResolution": "1920x1080",
"platform": "Test Platform",
"language": "ko-KR",
"timezone": "Asia/Seoul",
"cpuCores": 8,
"deviceMemory": 16,
"gpuInfo": "Test GPU",
"timestamp": datetime.now().isoformat()
}
result = process_tracking_data(test_data)
return f"ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ:\n{json.dumps(result, indent=2)}\n\nํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ:\n{json.dumps(test_data, indent=2)}"
def get_db_info():
"""๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ •๋ณด ์กฐํšŒ"""
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
try:
# DB ํŒŒ์ผ ํฌ๊ธฐ
db_size = os.path.getsize(DB_FILE) / (1024 * 1024) # MB
# ํ…Œ์ด๋ธ” ์ •๋ณด
cursor.execute("""
SELECT name, sql FROM sqlite_master
WHERE type='table' AND name NOT LIKE 'sqlite_%'
""")
tables = cursor.fetchall()
# ๋ ˆ์ฝ”๋“œ ์ˆ˜
cursor.execute("SELECT COUNT(*) FROM visitors")
visitor_count = cursor.fetchone()[0]
cursor.execute("SELECT COUNT(*) FROM events")
event_count = cursor.fetchone()[0]
info = f"""
### ๐Ÿ’พ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ •๋ณด
- **DB ํŒŒ์ผ**: {DB_FILE}
- **ํŒŒ์ผ ํฌ๊ธฐ**: {db_size:.2f} MB
- **๋ฐฉ๋ฌธ์ž ์ˆ˜**: {visitor_count:,}๊ฐœ
- **์ด๋ฒคํŠธ ์ˆ˜**: {event_count:,}๊ฐœ
### ๐Ÿ“‹ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ
"""
for table_name, sql in tables:
info += f"\n**{table_name}**\n```sql\n{sql}\n```\n"
return info
finally:
conn.close()
# ์ถ”์  ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ ํ•จ์ˆ˜
def generate_tracking_script(site_id, server_url):
"""์™ธ๋ถ€ ์‚ฌ์ดํŠธ์— ์‚ฝ์ž…ํ•  ์ถ”์  ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ"""
script = f"""
<!-- Visitor Tracking Script -->
<script>
(function() {{
// ์ถ”์  ์„œ๋ฒ„ URL
const TRACKING_SERVER = '{server_url}';
const SITE_ID = '{site_id}';
// ๋””๋ฐ”์ด์Šค ID ์ƒ์„ฑ/์กฐํšŒ
function getDeviceId() {{
let deviceId = localStorage.getItem('_tracker_device_id');
if (!deviceId) {{
deviceId = 'DEV_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
localStorage.setItem('_tracker_device_id', deviceId);
}}
return deviceId;
}}
// ์ถ”์  ๋ฐ์ดํ„ฐ ์ „์†ก
async function collectAndSend(eventType = 'pageview', eventData = {{}}) {{
const data = {{
siteId: SITE_ID,
deviceId: getDeviceId(),
eventType: eventType,
eventData: eventData,
pageUrl: window.location.href,
pageTitle: document.title,
referrer: document.referrer,
userAgent: navigator.userAgent,
screenResolution: screen.width + 'x' + screen.height,
platform: navigator.platform,
language: navigator.language,
timestamp: new Date().toISOString()
}};
try {{
const response = await fetch(TRACKING_SERVER + '/call/process_tracking', {{
method: 'POST',
headers: {{ 'Content-Type': 'application/json' }},
body: JSON.stringify({{ data: [JSON.stringify(data)] }})
}});
if (response.ok) {{
const result = await response.json();
if (result.event_id) {{
console.log('[Tracker] โœ… Event tracked');
}}
}}
}} catch(e) {{
console.error('[Tracker] Error:', e);
}}
}}
// ์ „์—ญ ์ถ”์  ํ•จ์ˆ˜
window._tracker = {{
trackEvent: function(eventName, eventData) {{
collectAndSend('custom', {{ name: eventName, data: eventData }});
}}
}};
// ํŽ˜์ด์ง€ ๋กœ๋“œ์‹œ ์ถ”์ 
if (document.readyState === 'loading') {{
document.addEventListener('DOMContentLoaded', () => collectAndSend());
}} else {{
collectAndSend();
}}
}})();
</script>
<!-- End Visitor Tracking Script -->
"""
return script.strip()
def create_site_id(server_url):
"""์ƒˆ๋กœ์šด ์ถ”์  ์‚ฌ์ดํŠธ ID ์ƒ์„ฑ"""
if not server_url:
server_url = "https://your-space-name.hf.space"
site_id = str(uuid.uuid4())[:8]
return site_id, generate_tracking_script(site_id, server_url)
# CSS ์Šคํƒ€์ผ
custom_css = """
.gradio-container {
font-family: 'Arial', sans-serif;
}
.tracking-script {
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
font-family: monospace;
font-size: 12px;
}
.db-info {
background: #e3f2fd;
padding: 15px;
border-radius: 5px;
margin: 10px 0;
}
"""
# Gradio UI
with gr.Blocks(title="๋ฐฉ๋ฌธ์ž ์ถ”์  ๊ด€๋ฆฌ ์‹œ์Šคํ…œ", css=custom_css, theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# ๐Ÿ” ๋ฐฉ๋ฌธ์ž ์ถ”์  ๊ด€๋ฆฌ ์‹œ์Šคํ…œ
### ์™ธ๋ถ€ ์›น์‚ฌ์ดํŠธ์— ์ถ”์  ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ๋ฐฉ๋ฌธ์ž๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
> โœ… **SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์˜๊ตฌ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.**
""")
# ์ˆจ๊ฒจ์ง„ API ์ฒ˜๋ฆฌ (์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ์‹)
with gr.Column(visible=False):
api_input = gr.Textbox(label="API Input")
api_output = gr.JSON(label="API Output")
api_button = gr.Button("Process Tracking Data")
# Button click์— api_name ์ง€์ •
api_button.click(
fn=process_tracking_data,
inputs=api_input,
outputs=api_output,
api_name="process_tracking"
)
with gr.Tab("๐Ÿ”ง ์ถ”์  ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ"):
gr.Markdown("### ์ƒˆ๋กœ์šด ์ถ”์  ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ")
with gr.Row():
with gr.Column():
server_url_input = gr.Textbox(
label="์ถ”์  ์„œ๋ฒ„ URL",
placeholder="https://your-space-name.hf.space",
info="์ด Hugging Face Space์˜ URL์„ ์ž…๋ ฅํ•˜์„ธ์š”"
)
gr.Markdown("""
โ„น๏ธ **์„œ๋ฒ„ URL ์ฐพ๊ธฐ**:
1. ์ด Space์˜ URL์„ ํ™•์ธํ•˜์„ธ์š”
2. ํ˜•์‹: `https://username-spacename.hf.space`
3. ์˜ˆ์‹œ: `https://seawolf2357-evcook.hf.space`
""")
create_btn = gr.Button("๐Ÿ”ง ์ƒˆ ์ถ”์  ์ฝ”๋“œ ์ƒ์„ฑ", variant="primary")
with gr.Row():
site_id_output = gr.Textbox(label="์‚ฌ์ดํŠธ ID", interactive=False)
script_output = gr.Code(
label="HTML์— ์‚ฝ์ž…ํ•  ์ถ”์  ์Šคํฌ๋ฆฝํŠธ",
language="html",
interactive=True,
elem_classes="tracking-script"
)
create_btn.click(
fn=create_site_id,
inputs=[server_url_input],
outputs=[site_id_output, script_output],
api_name="create_site_id"
)
with gr.Tab("๐Ÿ“Š ํ†ต๊ณ„ ๋Œ€์‹œ๋ณด๋“œ"):
gr.Markdown("### ๐Ÿ“Š ์‹ค์‹œ๊ฐ„ ๋ฐฉ๋ฌธ์ž ํ†ต๊ณ„")
with gr.Row():
refresh_btn = gr.Button("๐Ÿ”„ ํ†ต๊ณ„ ์ƒˆ๋กœ๊ณ ์นจ", variant="secondary")
test_btn = gr.Button("๐Ÿงช ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ", variant="secondary")
db_info_btn = gr.Button("๐Ÿ’พ DB ์ •๋ณด ๋ณด๊ธฐ", variant="secondary")
stats_output = gr.Markdown()
test_output = gr.Textbox(label="ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ", visible=False)
db_info_output = gr.Markdown(visible=False, elem_classes="db-info")
# ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ํ†ต๊ณ„ ํ‘œ์‹œ
demo.load(fn=view_statistics, outputs=stats_output, api_name="view_statistics")
refresh_btn.click(fn=view_statistics, outputs=stats_output, api_name="view_statistics_1")
def test_and_refresh():
test_result = test_tracking()
stats = view_statistics()
return stats, gr.update(visible=True, value=test_result)
test_btn.click(
fn=test_and_refresh,
outputs=[stats_output, test_output],
api_name="test_and_refresh"
)
def show_db_info():
info = get_db_info()
return gr.update(visible=True, value=info)
db_info_btn.click(
fn=show_db_info,
outputs=db_info_output,
api_name="show_db_info"
)
with gr.Tab("๐Ÿ“ ๋ฐ์ดํ„ฐ ๋ทฐ์–ด"):
gr.Markdown("### ๐Ÿ“ ์ตœ๊ทผ ์ถ”์  ๋ฐ์ดํ„ฐ")
data_refresh_btn = gr.Button("๐Ÿ”„ ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ")
data_output = gr.Dataframe(
headers=["Timestamp", "Device ID", "Event Type", "Page URL", "Platform", "Language", "Site ID"],
label="์ตœ๊ทผ ์ด๋ฒคํŠธ (์ตœ๋Œ€ 200๊ฐœ)"
)
demo.load(fn=get_recent_data, outputs=data_output, api_name="get_recent_data")
data_refresh_btn.click(fn=get_recent_data, outputs=data_output, api_name="get_recent_data_1")
gr.Markdown("### ๐Ÿ“ฅ ๋ฐ์ดํ„ฐ ๋‚ด๋ณด๋‚ด๊ธฐ")
with gr.Row():
start_date = gr.Textbox(
label="์‹œ์ž‘ ๋‚ ์งœ",
placeholder="2025-01-01",
value=datetime.now().strftime('%Y-%m-%d')
)
end_date = gr.Textbox(
label="์ข…๋ฃŒ ๋‚ ์งœ",
placeholder="2025-01-31",
value=datetime.now().strftime('%Y-%m-%d')
)
export_btn = gr.Button("๐Ÿ’พ CSV๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ", variant="primary")
export_output = gr.Textbox(label="๋‚ด๋ณด๋‚ด๊ธฐ ๊ฒฐ๊ณผ")
export_btn.click(
fn=export_data,
inputs=[start_date, end_date],
outputs=[gr.File(label="๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ"), export_output],
api_name="export_data"
)
with gr.Tab("โ“ ๋„์›€๋ง"):
gr.Markdown("""
### ๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘ ๊ฐ€์ด๋“œ
1. **์ด Space์˜ URL ํ™•์ธ**
- ๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ์ฐฝ์—์„œ URL ๋ณต์‚ฌ
- ์˜ˆ: `https://username-spacename.hf.space`
2. **์ถ”์  ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ**
- "์ถ”์  ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ" ํƒญ์œผ๋กœ ์ด๋™
- URL ์ž…๋ ฅ ํ›„ ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ
3. **์›น์‚ฌ์ดํŠธ์— ์„ค์น˜**
```html
<!-- ์—ฌ๊ธฐ์— ์ƒ์„ฑ๋œ ์Šคํฌ๋ฆฝํŠธ ๋ถ™์—ฌ๋„ฃ๊ธฐ -->
</body>
</html>
```
4. **์ถ”์  ํ™•์ธ**
- "ํ†ต๊ณ„ ๋Œ€์‹œ๋ณด๋“œ" ํƒญ์—์„œ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
### ๐Ÿ’พ ๋ฐ์ดํ„ฐ ์ €์žฅ
- **SQLite DB ํŒŒ์ผ**: `tracking_data.db`
- **์ž๋™ ์ €์žฅ**: ๋ชจ๋“  ์ด๋ฒคํŠธ๊ฐ€ ์‹ค์‹œ๊ฐ„์œผ๋กœ DB์— ์ €์žฅ
- **์˜๊ตฌ ๋ณด๊ด€**: ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ํ›„์—๋„ ๋ฐ์ดํ„ฐ ์œ ์ง€
### ๐Ÿ“ก API ์‚ฌ์šฉ๋ฒ•
```javascript
// Gradio Client API ์‚ฌ์šฉ
fetch('https://your-space.hf.space/call/process_tracking', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
data: [JSON.stringify({
siteId: 'your-site-id',
deviceId: 'device-id',
eventType: 'pageview',
// ... ๊ธฐํƒ€ ๋ฐ์ดํ„ฐ
})]
})
});
```
""")
# ์•ฑ ์‹คํ–‰
if __name__ == "__main__":
demo.queue().launch()
# requirements.txt ๋‚ด์šฉ:
"""
gradio==4.44.1
pandas==2.2.3
"""