422 lines
16 KiB
Python
422 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import re
|
|
import json
|
|
|
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
ISL_DIR = os.path.join(BASE_DIR, "..", "ISL-2026")
|
|
CONTENT_DIR = os.path.join(BASE_DIR, "content")
|
|
|
|
def read_file(filename):
|
|
path = os.path.join(ISL_DIR, filename)
|
|
with open(path, "r", encoding="utf-8-sig") as f:
|
|
return f.read()
|
|
|
|
def parse_members():
|
|
html = read_file("member.html")
|
|
parts = re.split(r'<H2>\s*Alumni.*?</H2>', html, flags=re.S | re.I)
|
|
active_html = parts[0]
|
|
alumni_html = parts[1] if len(parts) > 1 else ""
|
|
|
|
degree_map = {
|
|
"Professor": "Professor",
|
|
"Ph. D.": "PhD",
|
|
"Ph.D.": "PhD",
|
|
"Ph. D. Candidate": "PhDCandidate",
|
|
"Ph.D. Candidate": "PhDCandidate",
|
|
"Ph. D. Student": "PhDStudent",
|
|
"Ph.D. Student": "PhDStudent",
|
|
"M. S. Candidate": "MSCandidate",
|
|
"M.S. Candidate": "MSCandidate",
|
|
"M. S. Student": "MSStudent",
|
|
"M.S. Student": "MSStudent",
|
|
}
|
|
|
|
# Active members
|
|
active_members = []
|
|
active_items = re.findall(r'<LI><H4>(.*?)</H4>', active_html, flags=re.S | re.I)
|
|
for item in active_items:
|
|
clean_item = re.sub(r'<a.*?>.*?</a>', '', item, flags=re.I)
|
|
clean_item = re.sub(r'<.*?>', '', clean_item)
|
|
clean_item = clean_item.strip().replace('\n', ' ')
|
|
if not clean_item:
|
|
continue
|
|
|
|
m = re.search(r'^(.*?)\s*\((.*?),\s*(.*?)\)(?:\s*with\s*(.*))?$', clean_item)
|
|
if m:
|
|
en = m.group(1).strip()
|
|
ko = m.group(2).strip()
|
|
deg_str = m.group(3).strip()
|
|
aff = m.group(4).strip() if m.group(4) else None
|
|
else:
|
|
# Case like Hoang Anh Dang (M. S. Student)
|
|
m2 = re.search(r'^(.*?)\s*\((.*?)\)(?:\s*with\s*(.*))?$', clean_item)
|
|
if m2:
|
|
en = m2.group(1).strip()
|
|
ko = en
|
|
deg_str = m2.group(2).strip()
|
|
aff = m2.group(3).strip() if m2.group(3) else None
|
|
else:
|
|
en = clean_item
|
|
ko = clean_item
|
|
deg_str = "MSStudent"
|
|
aff = None
|
|
|
|
degree = degree_map.get(deg_str, "MSStudent")
|
|
mem = {"ko": ko, "en": en, "degree": degree}
|
|
if aff:
|
|
mem["affiliation"] = aff
|
|
active_members.append(mem)
|
|
|
|
# Alumni
|
|
alumni = []
|
|
alumni_items = re.findall(r'<LI><H4>(.*?)</H4>', alumni_html, flags=re.S | re.I)
|
|
if len(alumni_items) < 60:
|
|
alumni_items = re.findall(r'<LI>(.*?)(?=<LI>|<OL>|</OL>|$)', alumni_html, flags=re.S | re.I)
|
|
|
|
month_map = {"January":"01","February":"02","March":"03","April":"04","May":"05","June":"06","July":"07","August":"08","September":"09","October":"10","November":"11","December":"12"}
|
|
|
|
for item in alumni_items:
|
|
clean = re.sub(r'<.*?>', '\n', item).strip()
|
|
lines = [l.strip() for l in clean.split('\n') if l.strip()]
|
|
if not lines:
|
|
continue
|
|
first_line = lines[0]
|
|
if "Alumni" in first_line:
|
|
continue
|
|
|
|
m = re.search(r'^(.*?)\s*\((?:(.*?),\s*)?(Ph\.\s*D\.|Ph\.D\.|M\.\s*S\.|M\.S\.)\s+([A-Za-z]+\s+\d{4})', first_line)
|
|
if not m:
|
|
m = re.search(r'^(.*?)\s*\((?:(.*?),\s*)?(Ph\.\s*D\.|Ph\.D\.|M\.\s*S\.|M\.S\.)', first_line)
|
|
|
|
if m:
|
|
en = m.group(1).strip()
|
|
ko = m.group(2).strip() if m.group(2) else en
|
|
deg_raw = m.group(3).strip()
|
|
date_raw = m.group(4).strip() if len(m.groups()) >= 4 and m.group(4) else "August 2026"
|
|
major = None
|
|
if " in " in first_line:
|
|
m_maj = re.search(r'\s+in\s+([A-Za-z\s]+)', first_line)
|
|
if m_maj: major = m_maj.group(1).strip()
|
|
|
|
degree = "PhD" if "Ph" in deg_raw else "MS"
|
|
parts = date_raw.split()
|
|
grad_date = f"{parts[1]}-{month_map.get(parts[0], '01')}" if len(parts)==2 else "2026-01"
|
|
cur_pos = " ".join(lines[1:]) if len(lines) > 1 else None
|
|
|
|
alum = {
|
|
"ko": ko,
|
|
"en": en,
|
|
"degree": degree,
|
|
"graduatedAt": grad_date,
|
|
}
|
|
if major: alum["major"] = major
|
|
if cur_pos: alum["currentPosition"] = cur_pos
|
|
alumni.append(alum)
|
|
|
|
return active_members, alumni
|
|
|
|
def parse_publications():
|
|
intl_html = read_file("paper-international.html")
|
|
dom_html = read_file("paper-domestic.html")
|
|
pat_html = read_file("patent.html")
|
|
month_map = {"January":"01","February":"02","March":"03","April":"04","May":"05","June":"06","July":"07","August":"08","September":"09","October":"10","November":"11","December":"12"}
|
|
|
|
intl_pubs = []
|
|
intl_items = re.findall(r'<LI>(.*?)</LI>', intl_html, flags=re.S | re.I)
|
|
for item in intl_items:
|
|
m_title = re.search(r'<U>\s*"(.*?)"\s*<br/?>\s*</U>', item, flags=re.S | re.I)
|
|
if not m_title:
|
|
m_title = re.search(r'<U>\s*"(.*?)"\s*</U>', item, flags=re.S | re.I)
|
|
title = m_title.group(1).replace('\n', ' ').strip() if m_title else ""
|
|
|
|
m_venue = re.search(r'<I>\s*(.*?)\s*</I>', item, flags=re.S | re.I)
|
|
venue = m_venue.group(1).replace('\n', ' ').strip() if m_venue else ""
|
|
|
|
rest = item[m_venue.end():] if m_venue else item
|
|
rest_clean = re.sub(r'<.*?>', '', rest).replace('\n', ' ').strip()
|
|
rest_clean = re.sub(r'^\s*,\s*', '', rest_clean)
|
|
|
|
m_date = re.search(r'([A-Za-z]+)\s+(\d{4})', rest_clean)
|
|
pub_date = "2026-01"
|
|
if m_date:
|
|
mo, yr = m_date.group(1), m_date.group(2)
|
|
if mo in month_map:
|
|
pub_date = f"{yr}-{month_map[mo]}"
|
|
|
|
intl_pubs.append({
|
|
"category": "intl-journal-conf",
|
|
"title": title,
|
|
"venue": venue,
|
|
"volume": rest_clean,
|
|
"publishedAt": pub_date
|
|
})
|
|
|
|
dom_pubs = []
|
|
dom_items = re.findall(r'<LI>(.*?)</LI>', dom_html, flags=re.S | re.I)
|
|
for item in dom_items:
|
|
m_title = re.search(r'<U>\s*"(.*?)"\s*<br/?>\s*</U>', item, flags=re.S | re.I)
|
|
if not m_title:
|
|
m_title = re.search(r'<U>\s*"(.*?)"\s*</U>', item, flags=re.S | re.I)
|
|
title = m_title.group(1).replace('\n', ' ').strip() if m_title else ""
|
|
|
|
m_venue = re.search(r'<I>\s*(.*?)\s*</I>', item, flags=re.S | re.I)
|
|
venue = m_venue.group(1).replace('\n', ' ').strip() if m_venue else ""
|
|
|
|
rest = item[m_venue.end():] if m_venue else item
|
|
rest_clean = re.sub(r'<.*?>', '', rest).replace('\n', ' ').strip()
|
|
rest_clean = re.sub(r'^\s*,\s*', '', rest_clean)
|
|
|
|
is_kci = "(KCI)" in item or "(KCI)" in rest_clean
|
|
|
|
m_date = re.search(r'(\d{4})년\s*(\d{1,2})월', rest_clean)
|
|
if m_date:
|
|
pub_date = f"{m_date.group(1)}-{int(m_date.group(2)):02d}"
|
|
else:
|
|
pub_date = "2026-01"
|
|
|
|
dom_pubs.append({
|
|
"category": "domestic-journal-conf",
|
|
"title": title,
|
|
"venue": venue,
|
|
"volume": rest_clean,
|
|
"publishedAt": pub_date,
|
|
"kci": is_kci
|
|
})
|
|
|
|
patents = []
|
|
pat_items = re.findall(r'<LI>(.*?)</LI>', pat_html, flags=re.S | re.I)
|
|
for item in pat_items:
|
|
clean = re.sub(r'<.*?>', '\n', item)
|
|
lines = [l.strip() for l in clean.split('\n') if l.strip()]
|
|
if not lines:
|
|
continue
|
|
title = lines[0]
|
|
inventors = ""
|
|
app_no = ""
|
|
app_date = ""
|
|
reg_no = None
|
|
reg_date = None
|
|
country = "한국"
|
|
|
|
for l in lines[1:]:
|
|
if l.startswith("발명자:"):
|
|
inventors = l.replace("발명자:", "").strip()
|
|
elif l.startswith("출원번호:"):
|
|
m = re.search(r'출원번호:\s*([\d-]+)\s*\((.*?),\s*([\d.]+)\)', l)
|
|
if m:
|
|
app_no = m.group(1)
|
|
country = m.group(2)
|
|
app_date = m.group(3).replace('.', '-')
|
|
else:
|
|
app_no = l.replace("출원번호:", "").strip()
|
|
elif l.startswith("등록번호:"):
|
|
m = re.search(r'등록번호:\s*([\d-]+)\s*\((.*?),\s*([\d.]+)\)', l)
|
|
if m:
|
|
reg_no = m.group(1)
|
|
reg_date = m.group(3).replace('.', '-')
|
|
else:
|
|
reg_no = l.replace("등록번호:", "").strip()
|
|
|
|
pub_date = app_date[:7] if app_date else "2026-01"
|
|
patents.append({
|
|
"category": "patent",
|
|
"title": title,
|
|
"inventors": inventors,
|
|
"applicationNo": app_no,
|
|
"applicationAt": app_date,
|
|
"registrationNo": reg_no,
|
|
"registrationAt": reg_date,
|
|
"country": country,
|
|
"publishedAt": pub_date
|
|
})
|
|
|
|
return intl_pubs, dom_pubs, patents
|
|
|
|
def parse_lectures():
|
|
html = read_file("lecture.html")
|
|
semesters = []
|
|
sections = re.split(r'<H1 ALIGN="CENTER">(.*?)</H1>', html, flags=re.I)
|
|
for i in range(1, len(sections), 2):
|
|
term_str = sections[i].strip()
|
|
body = sections[i+1]
|
|
parts = term_str.split()
|
|
if len(parts) != 2:
|
|
continue
|
|
term = parts[0]
|
|
year = int(parts[1])
|
|
|
|
courses = []
|
|
c_items = re.findall(r'<H3>(.*?)(?:</H3>|<BR>|$)', body, flags=re.S | re.I)
|
|
for item in c_items:
|
|
clean = re.sub(r'<.*?>', '', item).strip()
|
|
if not clean:
|
|
continue
|
|
if "No Class" in clean:
|
|
courses.append({"code": "SABBATICAL", "en": "No Class (Sabbatical)", "note": "Sabbatical"})
|
|
else:
|
|
m = re.search(r'^(.*?)\s*\((.*?)\)$', clean)
|
|
if m:
|
|
courses.append({"code": m.group(2).strip(), "en": m.group(1).strip()})
|
|
else:
|
|
courses.append({"code": "COMP", "en": clean})
|
|
|
|
semesters.append({
|
|
"term": term,
|
|
"year": year,
|
|
"courses": courses
|
|
})
|
|
|
|
return semesters
|
|
|
|
def parse_standards():
|
|
html = read_file("standard.html")
|
|
bodies = []
|
|
sections = re.split(r'<H3>(.*?)</H3>', html, flags=re.I)
|
|
for i in range(1, len(sections), 2):
|
|
header = sections[i].strip()
|
|
body = sections[i+1]
|
|
|
|
m_head = re.search(r'^(.*?)\s*\((.*?)\):\s*(.*)$', header)
|
|
if not m_head:
|
|
continue
|
|
org = m_head.group(1).strip()
|
|
full = m_head.group(2).strip()
|
|
period = m_head.group(3).strip()
|
|
scope = "domestic" if "TTA" in org else "international"
|
|
role = "Convenor" if "Convenor" in header or "Convenor" in body else None
|
|
|
|
projects = []
|
|
# If 'o ' exists, split by 'o '
|
|
if re.search(r'^\s*o\s+', body, flags=re.M):
|
|
proj_blocks = re.split(r'\s*o\s+', body)
|
|
for proj_b in proj_blocks:
|
|
if not proj_b.strip():
|
|
continue
|
|
lines = proj_b.strip().split('<OL>')
|
|
p_head = lines[0].replace('<BR>', '').strip()
|
|
|
|
m_proj = re.search(r'^(.*?):\s*(.*)$', p_head)
|
|
wg = m_proj.group(1).strip() if m_proj else "WG"
|
|
p_name = m_proj.group(2).strip() if m_proj else p_head
|
|
|
|
docs = []
|
|
if len(lines) > 1:
|
|
doc_items = re.findall(r'<LI>(.*?)</LI>', lines[1], flags=re.S | re.I)
|
|
for d_item in doc_items:
|
|
d_clean = re.sub(r'<.*?>', '\n', d_item)
|
|
d_lines = [l.strip() for l in d_clean.split('\n') if l.strip()]
|
|
if not d_lines:
|
|
continue
|
|
d_title = d_lines[0].strip('" ')
|
|
d_rest = " ".join(d_lines[1:]) if len(d_lines) > 1 else ""
|
|
|
|
status = "IS"
|
|
if "In Progress" in d_rest: status = "InProgress"
|
|
elif "WDTR" in d_rest or "CDTR" in d_rest: status = "WDTR"
|
|
elif "CDV" in d_rest: status = "CDV"
|
|
elif "TR" in d_rest: status = "TR"
|
|
elif "TS" in d_rest: status = "TS"
|
|
|
|
m_date = re.search(r'\((.*?)\)', d_rest)
|
|
pub_date = m_date.group(1) if m_date and "In Progress" not in m_date.group(1) else None
|
|
|
|
docs.append({
|
|
"title": d_title,
|
|
"ref": d_rest,
|
|
"status": status,
|
|
"publishedAt": pub_date
|
|
})
|
|
|
|
projects.append({
|
|
"wg": wg,
|
|
"name": p_name,
|
|
"documents": docs
|
|
})
|
|
else:
|
|
# Single project body
|
|
doc_items = re.findall(r'<LI>(.*?)</LI>', body, flags=re.S | re.I)
|
|
docs = []
|
|
for d_item in doc_items:
|
|
d_clean = re.sub(r'<.*?>', '\n', d_item)
|
|
d_lines = [l.strip() for l in d_clean.split('\n') if l.strip()]
|
|
if not d_lines:
|
|
continue
|
|
d_title = d_lines[0].strip('" ')
|
|
d_rest = " ".join(d_lines[1:]) if len(d_lines) > 1 else ""
|
|
|
|
status = "IS"
|
|
if "In Progress" in d_rest: status = "InProgress"
|
|
elif "WDTR" in d_rest or "CDTR" in d_rest: status = "WDTR"
|
|
elif "CDV" in d_rest: status = "CDV"
|
|
elif "TR" in d_rest: status = "TR"
|
|
elif "TS" in d_rest: status = "TS"
|
|
|
|
m_date = re.search(r'\((.*?)\)', d_rest)
|
|
pub_date = m_date.group(1) if m_date and "In Progress" not in m_date.group(1) else None
|
|
|
|
docs.append({
|
|
"title": d_title,
|
|
"ref": d_rest,
|
|
"status": status,
|
|
"publishedAt": pub_date
|
|
})
|
|
|
|
projects.append({
|
|
"wg": "Main",
|
|
"name": full,
|
|
"documents": docs
|
|
})
|
|
|
|
bodies.append({
|
|
"org": org,
|
|
"full": full,
|
|
"scope": scope,
|
|
"period": period,
|
|
"role": role,
|
|
"projects": projects
|
|
})
|
|
|
|
return bodies
|
|
|
|
def clean_dict(obj):
|
|
if isinstance(obj, dict):
|
|
return {k: clean_dict(v) for k, v in obj.items() if v is not None}
|
|
elif isinstance(obj, list):
|
|
return [clean_dict(v) for v in obj if v is not None]
|
|
return obj
|
|
|
|
def main():
|
|
active_m, alumni_m = parse_members()
|
|
intl_p, dom_p, pat_p = parse_publications()
|
|
semesters = parse_lectures()
|
|
bodies = parse_standards()
|
|
|
|
os.makedirs(CONTENT_DIR, exist_ok=True)
|
|
|
|
with open(os.path.join(CONTENT_DIR, "members.ts"), "w", encoding="utf-8") as f:
|
|
f.write('// AUTO-GENERATED by scripts/extract/generate_all.py — DO NOT EDIT BY HAND\n')
|
|
f.write('import { Member, Alumnus } from "./types";\n\n')
|
|
f.write(f'export const activeMembers: Member[] = {json.dumps(clean_dict(active_m), ensure_ascii=False, indent=2)};\n\n')
|
|
f.write(f'export const alumni: Alumnus[] = {json.dumps(clean_dict(alumni_m), ensure_ascii=False, indent=2)};\n')
|
|
|
|
with open(os.path.join(CONTENT_DIR, "publications.ts"), "w", encoding="utf-8") as f:
|
|
f.write('// AUTO-GENERATED by scripts/extract/generate_all.py — DO NOT EDIT BY HAND\n')
|
|
f.write('import { Publication, Patent } from "./types";\n\n')
|
|
all_pubs = intl_p + dom_p
|
|
f.write(f'export const publications: Publication[] = {json.dumps(clean_dict(all_pubs), ensure_ascii=False, indent=2)};\n\n')
|
|
f.write(f'export const patents: Patent[] = {json.dumps(clean_dict(pat_p), ensure_ascii=False, indent=2)};\n')
|
|
|
|
with open(os.path.join(CONTENT_DIR, "lectures.ts"), "w", encoding="utf-8") as f:
|
|
f.write('// AUTO-GENERATED by scripts/extract/generate_all.py — DO NOT EDIT BY HAND\n')
|
|
f.write('import { Semester } from "./types";\n\n')
|
|
f.write(f'export const semesters: Semester[] = {json.dumps(clean_dict(semesters), ensure_ascii=False, indent=2)};\n')
|
|
|
|
with open(os.path.join(CONTENT_DIR, "standards.ts"), "w", encoding="utf-8") as f:
|
|
f.write('// AUTO-GENERATED by scripts/extract/generate_all.py — DO NOT EDIT BY HAND\n')
|
|
f.write('import { StandardsBody } from "./types";\n\n')
|
|
f.write(f'export const standardsBodies: StandardsBody[] = {json.dumps(clean_dict(bodies), ensure_ascii=False, indent=2)};\n')
|
|
|
|
if __name__ == "__main__":
|
|
main()
|