Merry99 commited on
Commit
cb7c9b7
Β·
1 Parent(s): abbfca0

upload dataset

Browse files
Dockerfile CHANGED
@@ -6,6 +6,7 @@ WORKDIR /app
6
  # μ‹œμŠ€ν…œ νŒ¨ν‚€μ§€ μ—…λ°μ΄νŠΈ 및 ν•„μš”ν•œ νŒ¨ν‚€μ§€ μ„€μΉ˜
7
  RUN apt-get update && apt-get install -y \
8
  build-essential \
 
9
  && rm -rf /var/lib/apt/lists/*
10
 
11
  # requirements 볡사 및 μ„€μΉ˜
@@ -15,9 +16,22 @@ RUN pip install --no-cache-dir -r requirements.txt
15
  # μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ½”λ“œ 볡사
16
  COPY . .
17
 
 
 
 
 
 
 
 
 
 
 
18
  # Hugging Face SpaceλŠ” 포트 7860을 μ‚¬μš©ν•©λ‹ˆλ‹€
19
  EXPOSE 7860
20
 
21
- # start.py둜 μ„œλ²„ μ‹€ν–‰ (Wallet μžλ™ λ””μ½”λ”©)
22
- CMD ["python", "start.py"]
 
 
 
23
 
 
6
  # μ‹œμŠ€ν…œ νŒ¨ν‚€μ§€ μ—…λ°μ΄νŠΈ 및 ν•„μš”ν•œ νŒ¨ν‚€μ§€ μ„€μΉ˜
7
  RUN apt-get update && apt-get install -y \
8
  build-essential \
9
+ cron \
10
  && rm -rf /var/lib/apt/lists/*
11
 
12
  # requirements 볡사 및 μ„€μΉ˜
 
16
  # μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ½”λ“œ 볡사
17
  COPY . .
18
 
19
+ # cron μ„€μ • 파일 볡사
20
+ COPY crontab /etc/cron.d/batch-push-cron
21
+
22
+ # cron κΆŒν•œ μ„€μ •
23
+ RUN chmod 0644 /etc/cron.d/batch-push-cron
24
+ RUN crontab /etc/cron.d/batch-push-cron
25
+
26
+ # 둜그 디렉토리 생성
27
+ RUN mkdir -p /var/log
28
+
29
  # Hugging Face SpaceλŠ” 포트 7860을 μ‚¬μš©ν•©λ‹ˆλ‹€
30
  EXPOSE 7860
31
 
32
+ # μ‹œμž‘ 슀크립트 볡사 및 μ‹€ν–‰
33
+ COPY start_with_cron.sh /start_with_cron.sh
34
+ RUN chmod +x /start_with_cron.sh
35
+
36
+ CMD ["/start_with_cron.sh"]
37
 
app.py CHANGED
@@ -1,11 +1,17 @@
1
  import os
2
  import json
3
  from typing import List, Optional
4
- from fastapi import FastAPI, HTTPException
5
  from pydantic import BaseModel, Field, ConfigDict
6
  from typing import List
7
  import oracledb
8
  from dotenv import load_dotenv
 
 
 
 
 
 
9
 
10
  # 둜컬 개발: .env 파일 λ‘œλ“œ (있으면)
11
  load_dotenv()
@@ -19,6 +25,10 @@ WALLET_PASSWORD = os.environ["WALLET_PASSWORD"]
19
  # tnsnames.ora μ•ˆμ˜ alias 확인 (보톡 *_high)
20
  TNS_ALIAS = os.environ.get("DB_TNS_ALIAS", "musclecare_high")
21
 
 
 
 
 
22
  # ----- μ—°κ²° ν’€: Thin + mTLS (μ§€κ°‘) -----
23
  # μ ˆλŒ€ ν˜ΈμΆœν•˜μ§€ λ§ˆμ„Έμš”: oracledb.init_oracle_client() # (Thick둜 λΉ μ Έμ„œ μ‹€νŒ¨ κ°€λŠ₯)
24
  pool: oracledb.ConnectionPool = oracledb.create_pool(
@@ -46,18 +56,6 @@ class StatePayload(BaseModel):
46
  user_emb: Optional[List[float]] = Field(default=None, description="length=12")
47
  model_version: Optional[str] = None
48
 
49
- class LogItem(BaseModel):
50
- user_id: str
51
- session_id: Optional[str] = None
52
- measure_date: str # 'YYYY-MM-DD'
53
- rms: Optional[float] = None
54
- freq: Optional[float] = None
55
- fatigue: Optional[float] = None
56
- mode: Optional[str] = Field(default="EMA", description="EMA/Hybrid/E2E")
57
- window_count: Optional[int] = None
58
-
59
- class LogsPayload(BaseModel):
60
- items: List[LogItem] = Field(..., min_length=1)
61
 
62
  # ----- μœ ν‹Έ -----
63
  def clob_json(obj) -> str:
@@ -72,23 +70,39 @@ def root():
72
  "message": "MuscleCare API Server",
73
  "version": "1.0.0",
74
  "endpoints": {
75
- "health": "/health",
 
76
  "docs": "/docs",
77
  "upload_state": "/upload_state",
78
- "upload_logs": "/upload_logs"
 
79
  }
80
  }
81
 
82
  @app.get("/health")
83
  def health():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  try:
85
  with pool.acquire() as conn:
86
  with conn.cursor() as cur:
87
  cur.execute("SELECT 1 FROM DUAL")
88
  v = cur.fetchone()[0]
89
- return {"ok": True, "db": v}
90
  except Exception as e:
91
- raise HTTPException(500, f"DB health check failed: {e}")
92
 
93
  @app.post("/upload_state")
94
  def upload_state(p: StatePayload):
@@ -132,23 +146,91 @@ def upload_state(p: StatePayload):
132
  except Exception as e:
133
  raise HTTPException(500, f"upload_state failed: {e}")
134
 
135
- @app.post("/upload_logs")
136
- def upload_logs(body: LogsPayload):
 
 
137
  try:
138
- rows = [(
139
- it.user_id, it.session_id, it.measure_date,
140
- it.rms, it.freq, it.fatigue, it.mode, it.window_count
141
- ) for it in body.items]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
- with pool.acquire() as conn:
144
- with conn.cursor() as cur:
145
- cur.executemany("""
146
- INSERT INTO MuscleCare.fatigue_logs
147
- (user_id, session_id, measure_date, rms, freq, fatigue, mode, window_count, created_at)
148
- VALUES
149
- (:1, :2, TO_DATE(:3,'YYYY-MM-DD'), :4, :5, :6, :7, :8, CURRENT_TIMESTAMP)
150
- """, rows)
151
- conn.commit()
152
- return {"ok": True, "inserted": len(rows)}
153
  except Exception as e:
154
- raise HTTPException(500, f"upload_logs failed: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import json
3
  from typing import List, Optional
4
+ from fastapi import FastAPI, HTTPException, Request
5
  from pydantic import BaseModel, Field, ConfigDict
6
  from typing import List
7
  import oracledb
8
  from dotenv import load_dotenv
9
+ import json
10
+ import os
11
+ import requests
12
+ import pandas as pd
13
+ from datetime import datetime
14
+ from datasets import Dataset, DatasetDict, load_dataset
15
 
16
  # 둜컬 개발: .env 파일 λ‘œλ“œ (있으면)
17
  load_dotenv()
 
25
  # tnsnames.ora μ•ˆμ˜ alias 확인 (보톡 *_high)
26
  TNS_ALIAS = os.environ.get("DB_TNS_ALIAS", "musclecare_high")
27
 
28
+ # Hugging Face μ„€μ •
29
+ HF_DATA_REPO_ID = os.getenv("HF_DATA_REPO_ID")
30
+ HF_DATA_TOKEN = os.getenv("HF_DATA_TOKEN")
31
+
32
  # ----- μ—°κ²° ν’€: Thin + mTLS (μ§€κ°‘) -----
33
  # μ ˆλŒ€ ν˜ΈμΆœν•˜μ§€ λ§ˆμ„Έμš”: oracledb.init_oracle_client() # (Thick둜 λΉ μ Έμ„œ μ‹€νŒ¨ κ°€λŠ₯)
34
  pool: oracledb.ConnectionPool = oracledb.create_pool(
 
56
  user_emb: Optional[List[float]] = Field(default=None, description="length=12")
57
  model_version: Optional[str] = None
58
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  # ----- μœ ν‹Έ -----
61
  def clob_json(obj) -> str:
 
70
  "message": "MuscleCare API Server",
71
  "version": "1.0.0",
72
  "endpoints": {
73
+ "health": "/health (λΉ λ₯Έ 체크)",
74
+ "health_db": "/health/db (DB μ—°κ²° 체크)",
75
  "docs": "/docs",
76
  "upload_state": "/upload_state",
77
+ "upload_dataset": "/upload_dataset",
78
+ "user_dataset": "/user_dataset/{user_id}"
79
  }
80
  }
81
 
82
  @app.get("/health")
83
  def health():
84
+ try:
85
+ # κ°„λ‹¨ν•œ health 체크 - DB μ—°κ²° 없이 μ„œλ²„ μƒνƒœλ§Œ 확인
86
+ return {
87
+ "ok": True,
88
+ "server": "running",
89
+ "timestamp": datetime.now().isoformat(),
90
+ "status": "healthy"
91
+ }
92
+ except Exception as e:
93
+ return {"ok": False, "error": str(e)}
94
+
95
+ @app.get("/health/db")
96
+ def health_db():
97
+ """DB 연결을 ν¬ν•¨ν•œ 상세 health 체크"""
98
  try:
99
  with pool.acquire() as conn:
100
  with conn.cursor() as cur:
101
  cur.execute("SELECT 1 FROM DUAL")
102
  v = cur.fetchone()[0]
103
+ return {"ok": True, "db": v, "server": "running"}
104
  except Exception as e:
105
+ return {"ok": False, "db": "error", "error": str(e)}
106
 
107
  @app.post("/upload_state")
108
  def upload_state(p: StatePayload):
 
146
  except Exception as e:
147
  raise HTTPException(500, f"upload_state failed: {e}")
148
 
149
+
150
+ @app.post("/upload_dataset")
151
+ async def upload_to_dataset(request: Request):
152
+ """둜컬 νŒŒμΌμ— μ‚¬μš©μžλ³„ 데이터 μ €μž₯"""
153
  try:
154
+ data = await request.json()
155
+ user_id = data.get("user_id")
156
+
157
+ if not user_id:
158
+ raise HTTPException(status_code=400, detail="user_idκ°€ ν•„μš”ν•©λ‹ˆλ‹€")
159
+
160
+ # 데이터에 νƒ€μž„μŠ€νƒ¬ν”„ μΆ”κ°€
161
+ data["timestamp"] = datetime.now().isoformat()
162
+
163
+ # 둜컬 데이터 디렉토리 생성
164
+ data_dir = "user_data"
165
+ os.makedirs(data_dir, exist_ok=True)
166
+
167
+ # μ‚¬μš©μžλ³„ JSON 파일 경둜
168
+ user_file = os.path.join(data_dir, f"{user_id}.json")
169
+
170
+ # κΈ°μ‘΄ 데이터 λ‘œλ“œ
171
+ existing_data = []
172
+ if os.path.exists(user_file):
173
+ try:
174
+ with open(user_file, 'r', encoding='utf-8') as f:
175
+ existing_data = json.load(f)
176
+ print(f"πŸ“Š κΈ°μ‘΄ 데이터 λ‘œλ“œ: {user_id} ({len(existing_data)}개 λ ˆμ½”λ“œ)")
177
+ except:
178
+ existing_data = []
179
+
180
+ # μƒˆ 데이터 μΆ”κ°€
181
+ existing_data.append(data)
182
+
183
+ # 파일 μ €μž₯
184
+ with open(user_file, 'w', encoding='utf-8') as f:
185
+ json.dump(existing_data, f, ensure_ascii=False, indent=2)
186
+
187
+ print(f"βœ… 둜컬 파일 μ €μž₯ μ™„λ£Œ: {user_id} ({len(existing_data)}개 λ ˆμ½”λ“œ)")
188
+ return {
189
+ "user_id": user_id,
190
+ "rows": len(existing_data),
191
+ "status": "success",
192
+ "filename": f"{user_id}.json",
193
+ "file_path": user_file,
194
+ "message": f"Data saved to local file: {user_file}"
195
+ }
196
 
 
 
 
 
 
 
 
 
 
 
197
  except Exception as e:
198
+ print(f"❌ 둜컬 μ €μž₯ μ‹€νŒ¨: {e}")
199
+ raise HTTPException(status_code=500, detail=f"둜컬 μ €μž₯ μ‹€νŒ¨: {str(e)}")
200
+
201
+ @app.get("/user_dataset/{user_id}")
202
+ async def read_user_dataset(user_id: str):
203
+ """둜컬 νŒŒμΌμ—μ„œ μ‚¬μš©μž 데이터 쑰회"""
204
+ try:
205
+ # μ‚¬μš©μžλ³„ JSON 파일 경둜
206
+ data_dir = "user_data"
207
+ user_file = os.path.join(data_dir, f"{user_id}.json")
208
+
209
+ if not os.path.exists(user_file):
210
+ return {
211
+ "user_id": user_id,
212
+ "count": 0,
213
+ "recent_data": [],
214
+ "source": "local_file",
215
+ "message": "No data found"
216
+ }
217
+
218
+ # 데이터 λ‘œλ“œ
219
+ with open(user_file, 'r', encoding='utf-8') as f:
220
+ data = json.load(f)
221
+
222
+ # 졜근 5개 λ ˆμ½”λ“œ λ°˜ν™˜
223
+ recent_data = data[-5:] if len(data) > 5 else data
224
+
225
+ return {
226
+ "user_id": user_id,
227
+ "count": len(data),
228
+ "recent_data": recent_data,
229
+ "filename": f"{user_id}.json",
230
+ "source": "local_file"
231
+ }
232
+
233
+ except Exception as e:
234
+ print(f"❌ 둜컬 쑰회 μ‹€νŒ¨: {e}")
235
+ raise HTTPException(status_code=500, detail=f"둜컬 쑰회 μ‹€νŒ¨: {str(e)}")
236
+
backup/2025-10-23/new_user.json ADDED
@@ -0,0 +1 @@
 
 
1
+ [{"user_id": "new_user", "rms": 0.95, "freq": 60.1, "fatigue": 0.88, "timestamp": "2025-10-23T15:02:00.000000"}]
backup/2025-10-23/test_user_1.json ADDED
@@ -0,0 +1 @@
 
 
1
+ [{"user_id": "test_user_1", "rms": 0.85, "freq": 52.3, "fatigue": 0.72, "timestamp": "2025-10-23T14:55:00.000000"}]
backup/2025-10-23/test_user_2.json ADDED
@@ -0,0 +1 @@
 
 
1
+ [{"user_id": "test_user_2", "rms": 0.91, "freq": 58.7, "fatigue": 0.81, "timestamp": "2025-10-23T14:55:30.000000"}]
crontab ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # 맀일 μžμ •μ— 배치 ν‘Έμ‹œ μ‹€ν–‰
2
+ 0 0 * * * cd /app && python upload_hF_dataset.py >> /var/log/batch_push.log 2>&1
3
+
4
+ # 빈 쀄 ν•„μš” (cron μš”κ΅¬μ‚¬ν•­)
last_push_date.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ 2025-10-23
requirements.txt CHANGED
@@ -4,3 +4,6 @@ python-multipart
4
  oracledb>=2.3
5
  pydantic
6
  python-dotenv
 
 
 
 
4
  oracledb>=2.3
5
  pydantic
6
  python-dotenv
7
+ datasets
8
+ pandas
9
+ requests
start.py CHANGED
@@ -5,6 +5,50 @@ from dotenv import load_dotenv
5
  # .env 파일 λ‘œλ“œ (둜컬 개발용)
6
  load_dotenv()
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  def prepare_wallet_dir():
9
  """
10
  Wallet μ€€λΉ„:
@@ -56,6 +100,9 @@ if __name__ == "__main__":
56
  print("πŸš€ MuscleCare FastAPI μ„œλ²„ μ‹œμž‘")
57
  print("=" * 60)
58
 
 
 
 
59
  # Wallet μ€€λΉ„
60
  try:
61
  prepare_wallet_dir()
 
5
  # .env 파일 λ‘œλ“œ (둜컬 개발용)
6
  load_dotenv()
7
 
8
+ def setup_environment():
9
+ """
10
+ ν™˜κ²½λ³€μˆ˜ μžλ™ μ„€μ •:
11
+ - 둜컬: .env νŒŒμΌμ—μ„œ λ‘œλ“œ
12
+ - Hugging Face Space: ν™˜κ²½λ³€μˆ˜μ—μ„œ μžλ™ μ„€μ •
13
+ """
14
+ print("πŸ”§ ν™˜κ²½λ³€μˆ˜ μ„€μ • 쀑...")
15
+
16
+ # Hugging Face Dataset μ„€μ • (κΈ°λ³Έκ°’ μ„€μ •)
17
+ if not os.getenv("HF_DATA_REPO_ID"):
18
+ os.environ["HF_DATA_REPO_ID"] = "Merry99/musclecare-data"
19
+ print("πŸ“ HF_DATA_REPO_ID κΈ°λ³Έκ°’ μ„€μ •: Merry99/musclecare-data")
20
+
21
+ if not os.getenv("HF_DATA_TOKEN"):
22
+ os.environ["HF_DATA_TOKEN"] = "hf_dummy_token_for_testing"
23
+ print("πŸ“ HF_DATA_TOKEN κΈ°λ³Έκ°’ μ„€μ •: hf_dummy_token_for_testing")
24
+
25
+ # Oracle DB μ„€μ • 확인
26
+ required_vars = ["DB_USER", "DB_PASSWORD", "WALLET_DIR", "WALLET_PASSWORD"]
27
+ missing_vars = []
28
+
29
+ for var in required_vars:
30
+ if not os.getenv(var):
31
+ missing_vars.append(var)
32
+
33
+ if missing_vars:
34
+ print(f"⚠️ λˆ„λ½λœ ν™˜κ²½λ³€μˆ˜: {', '.join(missing_vars)}")
35
+ print("πŸ’‘ .env νŒŒμΌμ„ ν™•μΈν•˜κ±°λ‚˜ ν™˜κ²½λ³€μˆ˜λ₯Ό μ„€μ •ν•΄μ£Όμ„Έμš”.")
36
+
37
+ # 둜컬 κ°œλ°œμ„ μœ„ν•œ κΈ°λ³Έκ°’ μ„€μ • (경고와 ν•¨κ»˜)
38
+ if not os.getenv("DB_USER"):
39
+ os.environ["DB_USER"] = "ADMIN"
40
+ print("πŸ“ DB_USER κΈ°λ³Έκ°’ μ„€μ •: ADMIN")
41
+ if not os.getenv("DB_PASSWORD"):
42
+ os.environ["DB_PASSWORD"] = "YourDBPassword123!"
43
+ print("πŸ“ DB_PASSWORD κΈ°λ³Έκ°’ μ„€μ •: YourDBPassword123!")
44
+ if not os.getenv("WALLET_PASSWORD"):
45
+ os.environ["WALLET_PASSWORD"] = "YourWalletPassword123!"
46
+ print("πŸ“ WALLET_PASSWORD κΈ°λ³Έκ°’ μ„€μ •: YourWalletPassword123!")
47
+ else:
48
+ print("βœ… λͺ¨λ“  ν•„μˆ˜ ν™˜κ²½λ³€μˆ˜κ°€ μ„€μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€")
49
+
50
+ print("πŸ”§ ν™˜κ²½λ³€μˆ˜ μ„€μ • μ™„λ£Œ")
51
+
52
  def prepare_wallet_dir():
53
  """
54
  Wallet μ€€λΉ„:
 
100
  print("πŸš€ MuscleCare FastAPI μ„œλ²„ μ‹œμž‘")
101
  print("=" * 60)
102
 
103
+ # ν™˜κ²½λ³€μˆ˜ μžλ™ μ„€μ •
104
+ setup_environment()
105
+
106
  # Wallet μ€€λΉ„
107
  try:
108
  prepare_wallet_dir()
start_server.sh ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # MuscleCare FastAPI μ„œλ²„ μ‹œμž‘ 슀크립트
4
+
5
+ echo "======================================"
6
+ echo "πŸš€ MuscleCare FastAPI μ„œλ²„ μ‹œμž‘"
7
+ echo "======================================"
8
+
9
+ # .env 파일 λ‘œλ“œ (선택사항)
10
+ if [ -f .env ]; then
11
+ echo "πŸ“„ .env 파일 λ‘œλ“œ 쀑..."
12
+ export $(cat .env | grep -v '^#' | xargs)
13
+ echo "βœ… .env 파일 λ‘œλ“œ μ™„λ£Œ"
14
+ else
15
+ echo "πŸ“ .env 파일이 μ—†μŠ΅λ‹ˆλ‹€. start.pyμ—μ„œ μžλ™μœΌλ‘œ ν™˜κ²½λ³€μˆ˜λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€."
16
+ fi
17
+
18
+ echo "πŸ”§ ν™˜κ²½λ³€μˆ˜λŠ” start.pyμ—μ„œ μžλ™μœΌλ‘œ μ„€μ •λ©λ‹ˆλ‹€."
19
+
20
+ # Python μ˜μ‘΄μ„± 확인 및 μ„€μΉ˜
21
+ echo "πŸ“¦ Python μ˜μ‘΄μ„± 확인 쀑..."
22
+ if ! python3 -c "import fastapi, uvicorn, oracledb" 2>/dev/null; then
23
+ echo "❌ ν•„μˆ˜ Python νŒ¨ν‚€μ§€κ°€ μ„€μΉ˜λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
24
+ echo "πŸ”§ μžλ™ μ„€μΉ˜λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€..."
25
+
26
+ # pip3 μ‚¬μš© μ‹œλ„
27
+ if command -v pip3 &> /dev/null; then
28
+ echo "πŸ“₯ pip3둜 νŒ¨ν‚€μ§€ μ„€μΉ˜ 쀑..."
29
+ pip3 install -r requirements.txt
30
+ # pip μ‚¬μš© μ‹œλ„
31
+ elif command -v pip &> /dev/null; then
32
+ echo "πŸ“₯ pip으둜 νŒ¨ν‚€μ§€ μ„€μΉ˜ 쀑..."
33
+ pip install -r requirements.txt
34
+ # python -m pip μ‚¬μš© μ‹œλ„
35
+ else
36
+ echo "πŸ“₯ python -m pip으둜 νŒ¨ν‚€μ§€ μ„€μΉ˜ 쀑..."
37
+ python3 -m pip install -r requirements.txt
38
+ fi
39
+
40
+ # μ„€μΉ˜ ν›„ λ‹€μ‹œ 확인
41
+ if ! python3 -c "import fastapi, uvicorn, oracledb" 2>/dev/null; then
42
+ echo "❌ νŒ¨ν‚€μ§€ μ„€μΉ˜μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."
43
+ echo "πŸ’‘ μˆ˜λ™μœΌλ‘œ μ„€μΉ˜ν•΄μ£Όμ„Έμš”:"
44
+ echo " pip3 install -r requirements.txt"
45
+ exit 1
46
+ fi
47
+ fi
48
+
49
+ echo "βœ… Python μ˜μ‘΄μ„± 확인 μ™„λ£Œ"
50
+
51
+ # μ„œλ²„ μ‹œμž‘
52
+ echo ""
53
+ echo "🌐 FastAPI μ„œλ²„ μ‹œμž‘ 쀑..."
54
+ echo "πŸ“ μ„œλ²„ μ£Όμ†Œ: http://localhost:7860"
55
+ echo "πŸ“– API λ¬Έμ„œ: http://localhost:7860/docs"
56
+ echo "πŸ›‘ μ’…λ£Œν•˜λ €λ©΄ Ctrl+Cλ₯Ό λˆ„λ₯΄μ„Έμš”"
57
+ echo ""
58
+
59
+ # μ„œλ²„ μ‹€ν–‰
60
+ python3 start.py
start_with_cron.sh ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # 둜그 디렉토리 생성
4
+ mkdir -p /var/log
5
+
6
+ # cron μ„œλΉ„μŠ€ μ‹œμž‘
7
+ service cron start
8
+
9
+ # cron μƒνƒœ 확인
10
+ echo "πŸ“… Cron μ„œλΉ„μŠ€ μ‹œμž‘λ¨"
11
+ crontab -l
12
+
13
+ # FastAPI μ„œλ²„ μ‹œμž‘ (λ°±κ·ΈλΌμš΄λ“œ)
14
+ echo "πŸš€ FastAPI μ„œλ²„ μ‹œμž‘..."
15
+ python start.py &
16
+
17
+ # μ„œλ²„κ°€ μ‹œμž‘λ  λ•ŒκΉŒμ§€ λŒ€κΈ°
18
+ sleep 5
19
+
20
+ # μ„œλ²„ μƒνƒœ 확인
21
+ echo "πŸ” μ„œλ²„ μƒνƒœ 확인..."
22
+ curl -s http://localhost:7860/health || echo "⚠️ μ„œλ²„ μ‹œμž‘ 쀑..."
23
+
24
+ # 둜그 λͺ¨λ‹ˆν„°λ§ (선택사항)
25
+ echo "πŸ“Š 배치 ν‘Έμ‹œ 둜그 λͺ¨λ‹ˆν„°λ§ μ‹œμž‘..."
26
+ tail -f /var/log/batch_push.log &
test_api.sh CHANGED
@@ -1,4 +1,4 @@
1
- #!/bin/bash
2
 
3
  # MuscleCare API ν…ŒμŠ€νŠΈ 슀크립트
4
 
@@ -37,39 +37,30 @@ curl -X POST "$BASE_URL/upload_state" \
37
  echo ""
38
  echo ""
39
 
40
- # 3. ν”Όλ‘œλ„ 둜그 μ—…λ‘œλ“œ
41
- echo "3️⃣ ν”Όλ‘œλ„ 둜그 μ—…λ‘œλ“œ"
42
  echo "--------------------------------------"
43
- curl -X POST "$BASE_URL/upload_logs" \
44
  -H "Content-Type: application/json" \
45
  -H "accept: application/json" \
46
  -d '{
47
- "items": [
48
- {
49
- "user_id": "test_user_001",
50
- "session_id": "session_001",
51
- "measure_date": "2025-10-22",
52
- "rms": 0.12,
53
- "freq": 49.8,
54
- "fatigue": 0.83,
55
- "mode": "Hybrid",
56
- "window_count": 100
57
- },
58
- {
59
- "user_id": "test_user_001",
60
- "session_id": "session_002",
61
- "measure_date": "2025-10-22",
62
- "rms": 0.15,
63
- "freq": 48.5,
64
- "fatigue": 0.91,
65
- "mode": "E2E",
66
- "window_count": 120
67
- }
68
- ]
69
  }' | jq .
70
  echo ""
71
  echo ""
72
 
 
 
 
 
 
 
 
 
73
  echo "======================================"
74
  echo "βœ… ν…ŒμŠ€νŠΈ μ™„λ£Œ"
75
  echo "======================================"
 
1
+ γ…”γ…›#!/bin/bash
2
 
3
  # MuscleCare API ν…ŒμŠ€νŠΈ 슀크립트
4
 
 
37
  echo ""
38
  echo ""
39
 
40
+ # 3. Dataset μ—…λ‘œλ“œ
41
+ echo "3️⃣ Dataset μ—…λ‘œλ“œ"
42
  echo "--------------------------------------"
43
+ curl -X POST "$BASE_URL/upload_dataset" \
44
  -H "Content-Type: application/json" \
45
  -H "accept: application/json" \
46
  -d '{
47
+ "user_id": "test_user_001",
48
+ "rms": 0.42,
49
+ "freq": 45.3,
50
+ "fatigue": 0.25,
51
+ "timestamp": "2024-01-15T10:30:00Z"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }' | jq .
53
  echo ""
54
  echo ""
55
 
56
+ # 4. μ‚¬μš©μž Dataset 쑰회
57
+ echo "4️⃣ μ‚¬μš©μž Dataset 쑰회"
58
+ echo "--------------------------------------"
59
+ curl -X GET "$BASE_URL/user_dataset/test_user_001" \
60
+ -H "accept: application/json" | jq .
61
+ echo ""
62
+ echo ""
63
+
64
  echo "======================================"
65
  echo "βœ… ν…ŒμŠ€νŠΈ μ™„λ£Œ"
66
  echo "======================================"
upload_hf_dataset.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datasets import Dataset, DatasetDict
2
+ from datetime import datetime, date
3
+ import pandas as pd, glob, json, os, shutil
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ HF_DATA_REPO_ID = os.getenv("HF_DATA_REPO_ID")
9
+ HF_DATA_TOKEN = os.getenv("HF_DATA_TOKEN")
10
+ CACHE_DIR = "./user_data"
11
+ BACKUP_DIR = "./backup"
12
+ LAST_PUSH_FILE = "./last_push_date.txt"
13
+
14
+ def now_str():
15
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
16
+
17
+ def get_last_push_date():
18
+ """λ§ˆμ§€λ§‰ ν‘Έμ‹œ λ‚ μ§œ λ°˜ν™˜"""
19
+ if os.path.exists(LAST_PUSH_FILE):
20
+ with open(LAST_PUSH_FILE, "r") as f:
21
+ return f.read().strip()
22
+ return None
23
+
24
+ def update_last_push_date():
25
+ """λ§ˆμ§€λ§‰ ν‘Έμ‹œ λ‚ μ§œ 기둝"""
26
+ with open(LAST_PUSH_FILE, "w") as f:
27
+ f.write(str(date.today()))
28
+
29
+ def should_push_today():
30
+ """였늘 ν‘Έμ‹œ μ—¬λΆ€ 확인"""
31
+ last_push = get_last_push_date()
32
+ today = str(date.today())
33
+ return last_push != today
34
+
35
+ def batch_push_to_huggingface():
36
+ """ν•˜λ£¨ 1회 Hugging Face Dataset μ—…λ‘œλ“œ"""
37
+ # ν•„μˆ˜ ν™˜κ²½λ³€μˆ˜ 확인
38
+ if not HF_DATA_REPO_ID or not HF_DATA_TOKEN:
39
+ print(f"❌ {now_str()} - ν™˜κ²½λ³€μˆ˜ HF_DATA_REPO_ID λ˜λŠ” HF_DATA_TOKEN이 μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
40
+ return
41
+
42
+ # ν‘Έμ‹œ μ—¬λΆ€ 체크
43
+ if not should_push_today():
44
+ print(f"πŸ“… {now_str()} - 이미 였늘 ν‘Έμ‹œ μ™„λ£Œλ¨. μ’…λ£Œ.")
45
+ return
46
+
47
+ files = glob.glob(os.path.join(CACHE_DIR, "*.json"))
48
+ if not files:
49
+ print(f"πŸ“ {now_str()} - μΊμ‹œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€. μ’…λ£Œ.")
50
+ return
51
+
52
+ print(f"πŸš€ {now_str()} - 배치 ν‘Έμ‹œ μ‹œμž‘ ({len(files)}개 파일)")
53
+
54
+ user_splits = {}
55
+ for path in files:
56
+ user_id = os.path.basename(path).split(".")[0]
57
+ try:
58
+ with open(path, "r", encoding="utf-8") as f:
59
+ records = json.load(f)
60
+ if not records:
61
+ print(f"⚠️ {user_id}: λΉ„μ–΄μžˆλŠ” 파일, κ±΄λ„ˆλœ€")
62
+ continue
63
+
64
+ df = pd.DataFrame(records)
65
+ user_splits[user_id] = Dataset.from_pandas(df)
66
+ print(f"πŸ“Š {user_id}: {len(records)}개 λ ˆμ½”λ“œ λ³€ν™˜ μ™„λ£Œ")
67
+
68
+ except Exception as e:
69
+ print(f"❌ {user_id}: 파일 λ‘œλ“œ μ‹€νŒ¨ β†’ {e}")
70
+ continue
71
+
72
+ if not user_splits:
73
+ print(f"❌ {now_str()} - μ²˜λ¦¬ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€. μ’…λ£Œ.")
74
+ return
75
+
76
+ # λ°±μ—… 디렉토리 생성
77
+ os.makedirs(BACKUP_DIR, exist_ok=True)
78
+ backup_path = os.path.join(BACKUP_DIR, date.today().isoformat())
79
+ shutil.copytree(CACHE_DIR, backup_path, dirs_exist_ok=True)
80
+ print(f"πŸ—‚οΈ {now_str()} - 데이터 λ°±μ—… μ™„λ£Œ β†’ {backup_path}")
81
+
82
+ try:
83
+ dataset_dict = DatasetDict(user_splits)
84
+ dataset_dict.push_to_hub(HF_DATA_REPO_ID, token=HF_DATA_TOKEN, private=True)
85
+ print(f"βœ… {now_str()} - Hugging Face Hub ν‘Έμ‹œ 성곡 ({len(user_splits)}λͺ…) β†’ {HF_DATA_REPO_ID}")
86
+
87
+ # ν‘Έμ‹œ 성곡 μ‹œ μΊμ‹œ 정리
88
+ shutil.rmtree(CACHE_DIR, ignore_errors=True)
89
+ print(f"πŸ—‘οΈ {now_str()} - user_data 디렉토리 μ‚­μ œ μ™„λ£Œ")
90
+
91
+ update_last_push_date()
92
+ print(f"πŸ“… {now_str()} - λ§ˆμ§€λ§‰ ν‘Έμ‹œ λ‚ μ§œ μ—…λ°μ΄νŠΈ μ™„λ£Œ")
93
+
94
+ except Exception as e:
95
+ print(f"❌ {now_str()} - ν‘Έμ‹œ μ‹€νŒ¨: {e}")
96
+ print(f"⚠️ {now_str()} - μΊμ‹œ μœ μ§€ (데이터 μœ μ‹€ λ°©μ§€)")
97
+ # μ‹€νŒ¨ μ‹œ μΊμ‹œ μœ μ§€
98
+ return
99
+
100
+ def main():
101
+ """CLI/cron μ§„μž…μ """
102
+ try:
103
+ batch_push_to_huggingface()
104
+ except Exception as e:
105
+ print(f"πŸ’₯ {now_str()} - 예기치 λͺ»ν•œ 였λ₯˜ λ°œμƒ: {e}")
106
+
107
+ if __name__ == "__main__":
108
+ main()