112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
from urllib.parse import quote
|
|
|
|
from fastapi import HTTPException
|
|
from fastapi.responses import FileResponse, Response
|
|
|
|
|
|
BUSINESS_LEDGER_DEFAULT_SOURCE_KEY = "business_ledger_default"
|
|
|
|
|
|
def sync_default_business_ledger_source(cur, incoming_files_dir: Path, served_dir: Path) -> None:
|
|
cur.execute("SELECT to_regclass('public.integration_binary_sources') IS NOT NULL AS table_exists")
|
|
row = cur.fetchone()
|
|
table_exists = bool(row["table_exists"]) if row is not None else False
|
|
if not table_exists:
|
|
return
|
|
|
|
business_dashboard_dir = incoming_files_dir / "사업관리대장"
|
|
business_ledger_served_dir = served_dir / "ledger"
|
|
candidates = [
|
|
business_ledger_served_dir / "사업관리대장-1.xlsx",
|
|
business_dashboard_dir / "사업관리대장-1.xlsx",
|
|
business_dashboard_dir / "사업관리 대장-1.xlsx",
|
|
business_dashboard_dir / "사업관리대장.xlsx",
|
|
business_dashboard_dir / "사업관리 대장.xlsx",
|
|
]
|
|
source_path = next((candidate for candidate in candidates if candidate.exists()), None)
|
|
if source_path is None:
|
|
return
|
|
|
|
content = source_path.read_bytes()
|
|
content_sha256 = hashlib.sha256(content).hexdigest()
|
|
meta_json = {
|
|
"byte_size": len(content),
|
|
"source_path": str(source_path),
|
|
"synced_from": "startup",
|
|
}
|
|
cur.execute(
|
|
"""
|
|
INSERT INTO integration_binary_sources (
|
|
source_key, source_name, filename, mime_type, content, content_sha256, meta_json, imported_at
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s::jsonb, NOW())
|
|
ON CONFLICT (source_key) DO UPDATE
|
|
SET source_name = EXCLUDED.source_name,
|
|
filename = EXCLUDED.filename,
|
|
mime_type = EXCLUDED.mime_type,
|
|
content = EXCLUDED.content,
|
|
content_sha256 = EXCLUDED.content_sha256,
|
|
meta_json = EXCLUDED.meta_json,
|
|
imported_at = NOW()
|
|
WHERE integration_binary_sources.content_sha256 IS DISTINCT FROM EXCLUDED.content_sha256
|
|
OR integration_binary_sources.filename IS DISTINCT FROM EXCLUDED.filename
|
|
OR integration_binary_sources.mime_type IS DISTINCT FROM EXCLUDED.mime_type
|
|
OR integration_binary_sources.meta_json IS DISTINCT FROM EXCLUDED.meta_json
|
|
""",
|
|
(
|
|
BUSINESS_LEDGER_DEFAULT_SOURCE_KEY,
|
|
"사업관리대장 기본 원본",
|
|
source_path.name,
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
content,
|
|
content_sha256,
|
|
json.dumps(meta_json, ensure_ascii=False),
|
|
),
|
|
)
|
|
|
|
|
|
def build_business_ledger_default_response(cur) -> Response:
|
|
cur.execute(
|
|
"""
|
|
SELECT filename, mime_type, content
|
|
FROM integration_binary_sources
|
|
WHERE source_key = %s
|
|
ORDER BY imported_at DESC
|
|
LIMIT 1
|
|
""",
|
|
(BUSINESS_LEDGER_DEFAULT_SOURCE_KEY,),
|
|
)
|
|
row = cur.fetchone()
|
|
if not row:
|
|
raise HTTPException(status_code=404, detail="Business ledger default source not found.")
|
|
|
|
filename = str(row["filename"] or "사업관리대장-1.xlsx")
|
|
headers = {
|
|
"Content-Disposition": 'inline; filename="business-ledger-default.xlsx"',
|
|
"X-Source-Filename": "business-ledger-default.xlsx",
|
|
"X-Original-Filename": quote(filename),
|
|
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
"Pragma": "no-cache",
|
|
}
|
|
return Response(
|
|
content=bytes(row["content"]),
|
|
media_type=str(
|
|
row["mime_type"] or "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
),
|
|
headers=headers,
|
|
)
|
|
|
|
|
|
def build_ledger_index_response(ledger_index_path: Path) -> FileResponse:
|
|
if not ledger_index_path.exists():
|
|
raise HTTPException(status_code=404, detail="Business ledger integration file not found.")
|
|
response = FileResponse(ledger_index_path)
|
|
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
|
response.headers["Pragma"] = "no-cache"
|
|
return response
|