Update app.py
Browse files
app.py
CHANGED
|
@@ -17,7 +17,106 @@ if not os.path.exists(UPLOAD_DIR):
|
|
| 17 |
db_registry = []
|
| 18 |
|
| 19 |
# --------------------------
|
| 20 |
-
# ์ ํธ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
# --------------------------
|
| 22 |
def blob_to_base64_html(blob_data):
|
| 23 |
if blob_data is None or pd.isna(blob_data):
|
|
@@ -204,9 +303,12 @@ def display_data(standard, version, selection):
|
|
| 204 |
return pd.DataFrame({"Info": ["์ ํ ํ์"]})
|
| 205 |
|
| 206 |
try:
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
| 210 |
conn.text_factory = safe_text_factory
|
| 211 |
|
| 212 |
tables = pd.read_sql(
|
|
@@ -219,7 +321,7 @@ def display_data(standard, version, selection):
|
|
| 219 |
main_t = tables[0]
|
| 220 |
|
| 221 |
# --------------------------
|
| 222 |
-
# ๐น ํ
์ด๋ธ
|
| 223 |
# --------------------------
|
| 224 |
full_table_name = None
|
| 225 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
|
@@ -234,51 +336,26 @@ def display_data(standard, version, selection):
|
|
| 234 |
break
|
| 235 |
|
| 236 |
# ==========================================
|
| 237 |
-
#
|
|
|
|
| 238 |
# ==========================================
|
| 239 |
-
if full_table_name:
|
| 240 |
-
|
| 241 |
-
cursor.execute(f"SELECT * FROM [{full_table_name}]")
|
| 242 |
-
rows = cursor.fetchall()
|
| 243 |
-
cols = [desc[0] for desc in cursor.description]
|
| 244 |
-
df = pd.DataFrame(rows, columns=cols)
|
| 245 |
-
df.columns = [c.strip() for c in df.columns]
|
| 246 |
-
df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
|
| 247 |
|
| 248 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
for col in df.columns:
|
| 250 |
df[col] = df[col].apply(blob_to_base64_html)
|
| 251 |
|
| 252 |
-
orig_cols = df.columns.tolist()
|
| 253 |
-
lower_cols = [c.lower() for c in orig_cols]
|
| 254 |
-
|
| 255 |
-
# 2. Section/Description ๋์ ์กฐ๋ฆฝ ๋ก์ง
|
| 256 |
-
if 'section' in lower_cols and 'description' in lower_cols:
|
| 257 |
-
# ์ด๋ฏธ ๊ท๊ฒฉ์ ๋ง๋ ๊ฒฝ์ฐ ์๋ฌธ์๋ก ์ด๋ฆ๋ง ๋ง์ถค
|
| 258 |
-
df.columns = lower_cols
|
| 259 |
-
df = df[['section', 'description']]
|
| 260 |
-
else:
|
| 261 |
-
# TableB_C ์ฒ๋ผ ๊ณ ์ ์ปฌ๋ผ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ๊ฒฝ์ฐ์ ๋์ ๋ณํฉ ์ฒ๋ฆฌ
|
| 262 |
-
if len(orig_cols) >= 2:
|
| 263 |
-
first_col = orig_cols[0] # ์ฒซ ๋ฒ์งธ ์ด (์: Threat Code)๊ฐ section์ด ๋จ
|
| 264 |
-
other_cols = orig_cols[1:] # ๋๋จธ์ง ์ด๋ค์ description์ผ๋ก ํฉ์ณ์ง
|
| 265 |
-
|
| 266 |
-
def build_combined_desc(row):
|
| 267 |
-
parts = []
|
| 268 |
-
for col in other_cols:
|
| 269 |
-
val = "" if pd.isna(row[col]) else str(row[col])
|
| 270 |
-
parts.append(f"**[{col}]**<br>{val}")
|
| 271 |
-
return "<br><br>".join(parts)
|
| 272 |
-
|
| 273 |
-
df['description'] = df.apply(build_combined_desc, axis=1)
|
| 274 |
-
df = df.rename(columns={first_col: 'section'})
|
| 275 |
-
df = df[['section', 'description']]
|
| 276 |
-
|
| 277 |
conn.close()
|
| 278 |
return df
|
| 279 |
|
| 280 |
# --------------------------
|
| 281 |
-
# ๐น ๋ฉ์ธ ํ
์ด๋ธ
|
|
|
|
| 282 |
# --------------------------
|
| 283 |
cursor = conn.cursor()
|
| 284 |
cursor.execute(f"PRAGMA table_info([{main_t}])")
|
|
@@ -288,20 +365,11 @@ def display_data(standard, version, selection):
|
|
| 288 |
|
| 289 |
real_ch, real_cat = None, None
|
| 290 |
for c, lc in zip(cols, lower):
|
| 291 |
-
if lc == "chapter":
|
| 292 |
-
|
| 293 |
-
elif lc == "category":
|
| 294 |
-
real_cat = c
|
| 295 |
|
| 296 |
-
# --------------------------
|
| 297 |
-
# ๐น ALL ์ ํ
|
| 298 |
-
# --------------------------
|
| 299 |
if selection == "ALL":
|
| 300 |
cursor.execute(f"SELECT * FROM [{main_t}]")
|
| 301 |
-
|
| 302 |
-
# --------------------------
|
| 303 |
-
# ๐น chapter.category
|
| 304 |
-
# --------------------------
|
| 305 |
elif "." in selection and real_ch and real_cat:
|
| 306 |
ch, ca = selection.split(".", 1)
|
| 307 |
query = f"""
|
|
@@ -310,21 +378,14 @@ def display_data(standard, version, selection):
|
|
| 310 |
AND REPLACE(TRIM(CAST([{real_cat}] AS TEXT)), ' ', '') = ?
|
| 311 |
"""
|
| 312 |
cursor.execute(query, (ch.replace(" ", ""), ca.replace(" ", "")))
|
| 313 |
-
|
| 314 |
-
# --------------------------
|
| 315 |
-
# ๐น fallback
|
| 316 |
-
# --------------------------
|
| 317 |
else:
|
| 318 |
cursor.execute(f"SELECT * FROM [{main_t}]")
|
| 319 |
|
| 320 |
rows = cursor.fetchall()
|
| 321 |
df = pd.DataFrame(rows, columns=cols)
|
| 322 |
|
| 323 |
-
#
|
| 324 |
-
# ๐น ํ์ฒ๋ฆฌ
|
| 325 |
-
# --------------------------
|
| 326 |
df.columns = [c.lower().strip() for c in df.columns]
|
| 327 |
-
|
| 328 |
if 'section' in df.columns and 'description' in df.columns:
|
| 329 |
df = df[['section', 'description']]
|
| 330 |
|
|
@@ -335,7 +396,6 @@ def display_data(standard, version, selection):
|
|
| 335 |
return df
|
| 336 |
|
| 337 |
except Exception as e:
|
| 338 |
-
print(f"[Error in display_data]")
|
| 339 |
traceback.print_exc()
|
| 340 |
return pd.DataFrame({"Error": [str(e)]})
|
| 341 |
|
|
|
|
| 17 |
db_registry = []
|
| 18 |
|
| 19 |
# --------------------------
|
| 20 |
+
# ์ ํธdef display_data(standard, version, selection):
|
| 21 |
+
if not all([standard, version, selection]):
|
| 22 |
+
return pd.DataFrame({"Info": ["์ ํ ํ์"]})
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
# ์ค์๊ฐ ๊ฒฝ๋ก ํ์ธ์ ์ํด ์ง์ ๊ฒฝ๋ก ์์ฑ
|
| 26 |
+
db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
|
| 27 |
+
if not os.path.exists(db_path):
|
| 28 |
+
return pd.DataFrame({"Error": [f"ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค: {db_path}"]})
|
| 29 |
+
|
| 30 |
+
conn = sqlite3.connect(db_path)
|
| 31 |
+
conn.text_factory = safe_text_factory
|
| 32 |
+
|
| 33 |
+
tables = pd.read_sql(
|
| 34 |
+
"SELECT name FROM sqlite_master WHERE type='table';",
|
| 35 |
+
conn
|
| 36 |
+
)['name'].tolist()
|
| 37 |
+
|
| 38 |
+
main_t = f"{standard}_{version}"
|
| 39 |
+
if main_t not in tables:
|
| 40 |
+
main_t = tables[0]
|
| 41 |
+
|
| 42 |
+
# --------------------------
|
| 43 |
+
# ๐น ํ
์ด๋ธ ๋ช
์นญ ๋งค์นญ (selection์ด ํ
์ด๋ธ ๋ช
์นญ์ธ์ง ํ์ธ)
|
| 44 |
+
# --------------------------
|
| 45 |
+
full_table_name = None
|
| 46 |
+
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
| 47 |
+
|
| 48 |
+
for t in tables:
|
| 49 |
+
if t == selection:
|
| 50 |
+
full_table_name = t
|
| 51 |
+
break
|
| 52 |
+
short = pattern.sub("", t).strip(" _")
|
| 53 |
+
if short == selection:
|
| 54 |
+
full_table_name = t
|
| 55 |
+
break
|
| 56 |
+
|
| 57 |
+
# ==========================================
|
| 58 |
+
# ๐ ๋ถ์ ํ
์ด๋ธ ์ ํ ์ (TableA, TableB_C ๋ฑ)
|
| 59 |
+
# ์๋ณธ ์ด ๊ตฌ์กฐ(4์ด, 5์ด ๋ฑ)๋ฅผ ๊ทธ๋๋ก ์ ์ง
|
| 60 |
+
# ==========================================
|
| 61 |
+
if full_table_name and full_table_name != main_t:
|
| 62 |
+
df = pd.read_sql(f"SELECT * FROM [{full_table_name}]", conn)
|
| 63 |
+
|
| 64 |
+
# version ์ด ์ ์ธ ๋ฐ ์ปฌ๋ผ๋ช
์ ๋ฆฌ
|
| 65 |
+
df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
|
| 66 |
+
df.columns = [c.replace("_", " ").title() for c in df.columns]
|
| 67 |
+
|
| 68 |
+
# ๋ชจ๋ ์
์ ๋ํด ์ด๋ฏธ์ง ๋ ๋๋ง(BLOB ๋ณํ) ์ ์ฉ
|
| 69 |
+
for col in df.columns:
|
| 70 |
+
df[col] = df[col].apply(blob_to_base64_html)
|
| 71 |
+
|
| 72 |
+
conn.close()
|
| 73 |
+
return df
|
| 74 |
+
|
| 75 |
+
# --------------------------
|
| 76 |
+
# ๐น ๋ฉ์ธ ํ
์ด๋ธ (ALL ๋๋ ํน์ Category)
|
| 77 |
+
# ๊ธฐ์กด ๊ท๊ฒฉ(section, description) ์ ์ง
|
| 78 |
+
# --------------------------
|
| 79 |
+
cursor = conn.cursor()
|
| 80 |
+
cursor.execute(f"PRAGMA table_info([{main_t}])")
|
| 81 |
+
cols_info = cursor.fetchall()
|
| 82 |
+
cols = [c[1] for c in cols_info]
|
| 83 |
+
lower = [c.lower().strip() for c in cols]
|
| 84 |
+
|
| 85 |
+
real_ch, real_cat = None, None
|
| 86 |
+
for c, lc in zip(cols, lower):
|
| 87 |
+
if lc == "chapter": real_ch = c
|
| 88 |
+
elif lc == "category": real_cat = c
|
| 89 |
+
|
| 90 |
+
if selection == "ALL":
|
| 91 |
+
cursor.execute(f"SELECT * FROM [{main_t}]")
|
| 92 |
+
elif "." in selection and real_ch and real_cat:
|
| 93 |
+
ch, ca = selection.split(".", 1)
|
| 94 |
+
query = f"""
|
| 95 |
+
SELECT * FROM [{main_t}]
|
| 96 |
+
WHERE REPLACE(TRIM(CAST([{real_ch}] AS TEXT)), ' ', '') = ?
|
| 97 |
+
AND REPLACE(TRIM(CAST([{real_cat}] AS TEXT)), ' ', '') = ?
|
| 98 |
+
"""
|
| 99 |
+
cursor.execute(query, (ch.replace(" ", ""), ca.replace(" ", "")))
|
| 100 |
+
else:
|
| 101 |
+
cursor.execute(f"SELECT * FROM [{main_t}]")
|
| 102 |
+
|
| 103 |
+
rows = cursor.fetchall()
|
| 104 |
+
df = pd.DataFrame(rows, columns=cols)
|
| 105 |
+
|
| 106 |
+
# ํ์ฒ๋ฆฌ: ๋ฉ์ธ ํ
์ด๋ธ์ ๊ฐ๋
์ฑ์ ์ํด 2์ด(section, description)๋ก ์ ํ
|
| 107 |
+
df.columns = [c.lower().strip() for c in df.columns]
|
| 108 |
+
if 'section' in df.columns and 'description' in df.columns:
|
| 109 |
+
df = df[['section', 'description']]
|
| 110 |
+
|
| 111 |
+
for col in df.columns:
|
| 112 |
+
df[col] = df[col].apply(blob_to_base64_html)
|
| 113 |
+
|
| 114 |
+
conn.close()
|
| 115 |
+
return df
|
| 116 |
+
|
| 117 |
+
except Exception as e:
|
| 118 |
+
traceback.print_exc()
|
| 119 |
+
return pd.DataFrame({"Error": [str(e)]})
|
| 120 |
# --------------------------
|
| 121 |
def blob_to_base64_html(blob_data):
|
| 122 |
if blob_data is None or pd.isna(blob_data):
|
|
|
|
| 303 |
return pd.DataFrame({"Info": ["์ ํ ํ์"]})
|
| 304 |
|
| 305 |
try:
|
| 306 |
+
# ์ค์๊ฐ ๊ฒฝ๋ก ํ์ธ์ ์ํด ์ง์ ๊ฒฝ๋ก ์์ฑ
|
| 307 |
+
db_path = os.path.join(UPLOAD_DIR, f"{standard}_{version}.db")
|
| 308 |
+
if not os.path.exists(db_path):
|
| 309 |
+
return pd.DataFrame({"Error": [f"ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค: {db_path}"]})
|
| 310 |
+
|
| 311 |
+
conn = sqlite3.connect(db_path)
|
| 312 |
conn.text_factory = safe_text_factory
|
| 313 |
|
| 314 |
tables = pd.read_sql(
|
|
|
|
| 321 |
main_t = tables[0]
|
| 322 |
|
| 323 |
# --------------------------
|
| 324 |
+
# ๐น ๏ฟฝ๏ฟฝ๏ฟฝ์ด๋ธ ๋ช
์นญ ๋งค์นญ (selection์ด ํ
์ด๋ธ ๋ช
์นญ์ธ์ง ํ์ธ)
|
| 325 |
# --------------------------
|
| 326 |
full_table_name = None
|
| 327 |
pattern = re.compile(f"^{standard}[_\\s-]*{version}[_\\s-]*", re.IGNORECASE)
|
|
|
|
| 336 |
break
|
| 337 |
|
| 338 |
# ==========================================
|
| 339 |
+
# ๐ ๋ถ์ ํ
์ด๋ธ ์ ํ ์ (TableA, TableB_C ๋ฑ)
|
| 340 |
+
# ์๋ณธ ์ด ๊ตฌ์กฐ(4์ด, 5์ด ๋ฑ)๋ฅผ ๊ทธ๋๋ก ์ ์ง
|
| 341 |
# ==========================================
|
| 342 |
+
if full_table_name and full_table_name != main_t:
|
| 343 |
+
df = pd.read_sql(f"SELECT * FROM [{full_table_name}]", conn)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
|
| 345 |
+
# version ์ด ์ ์ธ ๋ฐ ์ปฌ๋ผ๋ช
์ ๋ฆฌ
|
| 346 |
+
df = df.drop(columns=[c for c in df.columns if c.lower() == "version"], errors="ignore")
|
| 347 |
+
df.columns = [c.replace("_", " ").title() for c in df.columns]
|
| 348 |
+
|
| 349 |
+
# ๋ชจ๋ ์
์ ๋ํด ์ด๋ฏธ์ง ๋ ๋๋ง(BLOB ๋ณํ) ์ ์ฉ
|
| 350 |
for col in df.columns:
|
| 351 |
df[col] = df[col].apply(blob_to_base64_html)
|
| 352 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
conn.close()
|
| 354 |
return df
|
| 355 |
|
| 356 |
# --------------------------
|
| 357 |
+
# ๐น ๋ฉ์ธ ํ
์ด๋ธ (ALL ๋๋ ํน์ Category)
|
| 358 |
+
# ๊ธฐ์กด ๊ท๊ฒฉ(section, description) ์ ์ง
|
| 359 |
# --------------------------
|
| 360 |
cursor = conn.cursor()
|
| 361 |
cursor.execute(f"PRAGMA table_info([{main_t}])")
|
|
|
|
| 365 |
|
| 366 |
real_ch, real_cat = None, None
|
| 367 |
for c, lc in zip(cols, lower):
|
| 368 |
+
if lc == "chapter": real_ch = c
|
| 369 |
+
elif lc == "category": real_cat = c
|
|
|
|
|
|
|
| 370 |
|
|
|
|
|
|
|
|
|
|
| 371 |
if selection == "ALL":
|
| 372 |
cursor.execute(f"SELECT * FROM [{main_t}]")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
elif "." in selection and real_ch and real_cat:
|
| 374 |
ch, ca = selection.split(".", 1)
|
| 375 |
query = f"""
|
|
|
|
| 378 |
AND REPLACE(TRIM(CAST([{real_cat}] AS TEXT)), ' ', '') = ?
|
| 379 |
"""
|
| 380 |
cursor.execute(query, (ch.replace(" ", ""), ca.replace(" ", "")))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
else:
|
| 382 |
cursor.execute(f"SELECT * FROM [{main_t}]")
|
| 383 |
|
| 384 |
rows = cursor.fetchall()
|
| 385 |
df = pd.DataFrame(rows, columns=cols)
|
| 386 |
|
| 387 |
+
# ํ์ฒ๋ฆฌ: ๋ฉ์ธ ํ
์ด๋ธ์ ๊ฐ๋
์ฑ์ ์ํด 2์ด(section, description)๋ก ์ ํ
|
|
|
|
|
|
|
| 388 |
df.columns = [c.lower().strip() for c in df.columns]
|
|
|
|
| 389 |
if 'section' in df.columns and 'description' in df.columns:
|
| 390 |
df = df[['section', 'description']]
|
| 391 |
|
|
|
|
| 396 |
return df
|
| 397 |
|
| 398 |
except Exception as e:
|
|
|
|
| 399 |
traceback.print_exc()
|
| 400 |
return pd.DataFrame({"Error": [str(e)]})
|
| 401 |
|