- Implement standalone Go backend API server using Gin and pure-Go SQLite (modernc.org/sqlite) - Add 11-table schema DDL migrations (000001_init.up.sql) based on DATA_TABLE.md - Add seed data ingestion tool (cmd/seed) and static TypeScript content exporter (cmd/export-content) - Add configuration loader (internal/config) supporting environment variables and .env files (HOST, PORT, DB_PATH, GIN_MODE, CORS_ALLOW_ORIGINS, AUTO_MIGRATE) - Add lightweight multi-stage Dockerfile and docker-compose.yml with persistent SQLite volume - Add comprehensive architecture specification (ARCHITECTURE.md) and REST API interface specification (API_INTERFACE.md) - Harmonize frontend publications page to consume isHighlight flag from database-synced content
176 lines
7.4 KiB
Python
Executable File
176 lines
7.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Exports curated data from refer_landing_page/content/*.ts and app/page.tsx into backend/seed/data/*.json.
|
|
Ensures is_highlight is properly tagged on the 5 representative publications.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
CONTENT_DIR = os.path.join(REPO_ROOT, "refer_landing_page", "content")
|
|
SEED_DATA_DIR = os.path.join(REPO_ROOT, "backend", "seed", "data")
|
|
PAGE_TSX = os.path.join(REPO_ROOT, "refer_landing_page", "app", "page.tsx")
|
|
|
|
os.makedirs(SEED_DATA_DIR, exist_ok=True)
|
|
|
|
HIGHLIGHT_TITLES = {
|
|
"Globally Integrated Trust Authority (GITA) for Resource-Constrained Edge Devices in IoT and 6G",
|
|
"Application Level Trust Authority (APPLETA) for Resource-Constrained Edge Devices in IoT and 6G",
|
|
"mQUIC: Use of QUIC for Handover Support with Connection Migration in Wireless/Mobile Networks",
|
|
"Use of QUIC for CoAP Transport in IoT Networks",
|
|
"Use of QUIC for Mobile-Oriented Future Internet (Q-MOFI)",
|
|
}
|
|
|
|
def clean_json_str(s):
|
|
# Remove trailing commas before ] or }
|
|
s = re.sub(r',\s*\]', ']', s)
|
|
s = re.sub(r',\s*\}', '}', s)
|
|
return s
|
|
|
|
def extract_json_array_from_ts(filepath, var_name):
|
|
with open(filepath, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
# Match export const <var_name>[: Type] = [ ... ];
|
|
pattern = rf'export\s+const\s+{var_name}(?:\s*:\s*[^=]+)?\s*=\s*(\[\s*[\s\S]*?\s*\]);'
|
|
match = re.search(pattern, content)
|
|
if not match:
|
|
raise ValueError(f"Could not find array literal for '{var_name}' in {filepath}")
|
|
|
|
array_str = clean_json_str(match.group(1))
|
|
return json.loads(array_str)
|
|
|
|
def main():
|
|
print("=== Extracting TypeScript content to JSON for SQLite seeding ===")
|
|
|
|
# 1. Projects
|
|
projects_file = os.path.join(CONTENT_DIR, "projects.ts")
|
|
projects = extract_json_array_from_ts(projects_file, "researchProjects")
|
|
with open(os.path.join(SEED_DATA_DIR, "research_projects.json"), "w", encoding="utf-8") as f:
|
|
json.dump(projects, f, indent=2, ensure_ascii=False)
|
|
print(f"✓ Extracted {len(projects)} research projects")
|
|
|
|
# 2. Research Areas (from app/page.tsx)
|
|
with open(PAGE_TSX, "r", encoding="utf-8") as f:
|
|
page_src = f.read()
|
|
areas_match = re.search(r'const\s+RESEARCH_AREAS(?:\s*:\s*[^=]+)?\s*=\s*(\[\s*[\s\S]*?\s*\]);', page_src)
|
|
if not areas_match:
|
|
raise ValueError("Could not find RESEARCH_AREAS in app/page.tsx")
|
|
areas_str = clean_json_str(areas_match.group(1))
|
|
areas_list = json.loads(areas_str)
|
|
research_areas = [{"name_en": name, "name_kr": None, "display_order": i + 1} for i, name in enumerate(areas_list)]
|
|
with open(os.path.join(SEED_DATA_DIR, "research_areas.json"), "w", encoding="utf-8") as f:
|
|
json.dump(research_areas, f, indent=2, ensure_ascii=False)
|
|
print(f"✓ Extracted {len(research_areas)} research areas")
|
|
|
|
# 3. Members & Alumni
|
|
members_file = os.path.join(CONTENT_DIR, "members.ts")
|
|
active_members = extract_json_array_from_ts(members_file, "activeMembers")
|
|
alumni = extract_json_array_from_ts(members_file, "alumni")
|
|
|
|
# Format active members with is_advisor
|
|
formatted_members = []
|
|
for i, m in enumerate(active_members):
|
|
formatted_members.append({
|
|
"name_ko": m["ko"],
|
|
"name_en": m["en"],
|
|
"degree": m["degree"],
|
|
"affiliation": m.get("affiliation"),
|
|
"email": "sjkoh@knu.ac.kr" if m["degree"] == "Professor" else None,
|
|
"is_advisor": m["degree"] == "Professor",
|
|
"display_order": i + 1,
|
|
})
|
|
with open(os.path.join(SEED_DATA_DIR, "members.json"), "w", encoding="utf-8") as f:
|
|
json.dump(formatted_members, f, indent=2, ensure_ascii=False)
|
|
print(f"✓ Extracted {len(formatted_members)} active members")
|
|
|
|
formatted_alumni = []
|
|
for a in alumni:
|
|
formatted_alumni.append({
|
|
"name_ko": a["ko"],
|
|
"name_en": a["en"],
|
|
"degree": a["degree"],
|
|
"graduated_at": a["graduatedAt"],
|
|
"major": a.get("major"),
|
|
"current_position": a.get("currentPosition"),
|
|
})
|
|
with open(os.path.join(SEED_DATA_DIR, "alumni.json"), "w", encoding="utf-8") as f:
|
|
json.dump(formatted_alumni, f, indent=2, ensure_ascii=False)
|
|
print(f"✓ Extracted {len(formatted_alumni)} alumni")
|
|
|
|
# 4. Publications & Patents
|
|
pubs_file = os.path.join(CONTENT_DIR, "publications.ts")
|
|
raw_pubs = extract_json_array_from_ts(pubs_file, "publications")
|
|
raw_patents = extract_json_array_from_ts(pubs_file, "patents")
|
|
|
|
matched_highlights = []
|
|
formatted_pubs = []
|
|
for p in raw_pubs:
|
|
title_clean = p["title"].strip()
|
|
is_hl = title_clean in HIGHLIGHT_TITLES
|
|
if is_hl:
|
|
matched_highlights.append(title_clean)
|
|
formatted_pubs.append({
|
|
"category": p["category"],
|
|
"title": p["title"],
|
|
"authors": p.get("authors"),
|
|
"venue": p["venue"],
|
|
"volume": p.get("volume"),
|
|
"published_at": p["publishedAt"],
|
|
"kci": bool(p.get("kci", False)),
|
|
"doi": p.get("doi"),
|
|
"is_highlight": is_hl,
|
|
})
|
|
|
|
if len(matched_highlights) != 5:
|
|
raise ValueError(f"Highlight matching error: expected exactly 5 matched highlights, found {len(matched_highlights)}: {matched_highlights}")
|
|
|
|
with open(os.path.join(SEED_DATA_DIR, "publications.json"), "w", encoding="utf-8") as f:
|
|
json.dump(formatted_pubs, f, indent=2, ensure_ascii=False)
|
|
print(f"✓ Extracted {len(formatted_pubs)} publications (exactly {len(matched_highlights)} highlights tagged)")
|
|
|
|
formatted_patents = []
|
|
for pat in raw_patents:
|
|
formatted_patents.append({
|
|
"title": pat["title"],
|
|
"inventors": pat["inventors"],
|
|
"application_no": pat["applicationNo"],
|
|
"application_at": pat["applicationAt"],
|
|
"registration_no": pat.get("registrationNo"),
|
|
"registration_at": pat.get("registrationAt"),
|
|
"country": pat["country"],
|
|
"published_at": pat["publishedAt"],
|
|
})
|
|
with open(os.path.join(SEED_DATA_DIR, "patents.json"), "w", encoding="utf-8") as f:
|
|
json.dump(formatted_patents, f, indent=2, ensure_ascii=False)
|
|
print(f"✓ Extracted {len(formatted_patents)} patents")
|
|
|
|
# 5. Lectures (Semesters & Courses)
|
|
lectures_file = os.path.join(CONTENT_DIR, "lectures.ts")
|
|
raw_semesters = extract_json_array_from_ts(lectures_file, "semesters")
|
|
with open(os.path.join(SEED_DATA_DIR, "lectures.json"), "w", encoding="utf-8") as f:
|
|
json.dump(raw_semesters, f, indent=2, ensure_ascii=False)
|
|
|
|
total_courses = sum(len(s.get("courses", [])) for s in raw_semesters)
|
|
print(f"✓ Extracted {len(raw_semesters)} semesters with {total_courses} courses")
|
|
|
|
# 6. Standardization
|
|
standards_file = os.path.join(CONTENT_DIR, "standards.ts")
|
|
raw_standards = extract_json_array_from_ts(standards_file, "standardsBodies")
|
|
with open(os.path.join(SEED_DATA_DIR, "standards.json"), "w", encoding="utf-8") as f:
|
|
json.dump(raw_standards, f, indent=2, ensure_ascii=False)
|
|
|
|
total_docs = sum(
|
|
sum(len(p.get("documents", [])) for p in b.get("projects", []))
|
|
for b in raw_standards
|
|
)
|
|
print(f"✓ Extracted {len(raw_standards)} standards bodies with {total_docs} documents")
|
|
|
|
print("=== Extraction completed successfully ===")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|