feat(jamie-journal): implement LinkRecommender (procedure cross-linking suggestions)
This commit is contained in:
@@ -388,8 +388,58 @@ class SpellingChecker:
|
|||||||
class LinkRecommender:
|
class LinkRecommender:
|
||||||
"""Recommends internal/external links and detects broken or missing ones."""
|
"""Recommends internal/external links and detects broken or missing ones."""
|
||||||
|
|
||||||
|
PROCEDURE_MAP: Dict[str, Tuple[str, List[str]]] = {
|
||||||
|
'double_eyelid': ('쌍꺼풀 수술', ['쌍꺼풀', '매몰법', '절개법', '퀵매몰']),
|
||||||
|
'ptosis': ('안검하수 눈매교정', ['안검하수', '눈매교정', '졸린 눈', '눈매 교정']),
|
||||||
|
'under_eye_fat': ('눈밑 지방 재배치', ['눈밑 지방', '다크서클', '눈밑지방', '지방 재배치']),
|
||||||
|
'dual_epicanthoplasty': ('듀얼 트임', ['듀얼 트임', '앞트임', '뒤트임', '눈꼬리']),
|
||||||
|
'forehead_lift': ('내시경 이마거상술', ['이마거상', '이마리프팅', '이마 거상', '이마 리프팅']),
|
||||||
|
'brow_lift': ('내시경 눈썹거상술', ['눈썹거상', '눈썹리프팅', '눈썹 거상', '눈썹 리프팅']),
|
||||||
|
'sub_brow': ('눈썹밑 피부절개술', ['눈썹밑', '상안검', '눈꺼풀 처짐']),
|
||||||
|
'smas_lifting': ('스마스 리프팅', ['스마스', 'SMAS', '안면거상', '페이스리프트']),
|
||||||
|
'fat_graft': ('자가지방 이식', ['자가지방', '지방이식', '지방 이식', '지방 생착']),
|
||||||
|
'hifu': ('하이푸 리프팅', ['하이푸', 'HIFU', '초음파 리프팅']),
|
||||||
|
'cheek_lift': ('앞광대 리프팅', ['앞광대', '팔자 주름', '팔자주름', '광대 리프팅']),
|
||||||
|
'endotine': ('엔도타인 고정', ['엔도타인', 'endotine', '3점 고정']),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _detect_main_topic(self, lines: List[str]) -> Optional[str]:
|
||||||
|
"""Scan first 10 lines to find the main procedure topic."""
|
||||||
|
opening_text = '\n'.join(lines[:10])
|
||||||
|
for topic_key, (display_name, keywords) in self.PROCEDURE_MAP.items():
|
||||||
|
for kw in keywords:
|
||||||
|
if kw in opening_text:
|
||||||
|
return topic_key
|
||||||
|
return None
|
||||||
|
|
||||||
def check(self, content: str) -> List[ValidationIssue]:
|
def check(self, content: str) -> List[ValidationIssue]:
|
||||||
return []
|
issues: List[ValidationIssue] = []
|
||||||
|
lines = content.split('\n')
|
||||||
|
|
||||||
|
main_topic = self._detect_main_topic(lines)
|
||||||
|
|
||||||
|
for topic_key, (display_name, keywords) in self.PROCEDURE_MAP.items():
|
||||||
|
if topic_key == main_topic:
|
||||||
|
continue
|
||||||
|
for line_idx, line in enumerate(lines, start=1):
|
||||||
|
matched_kw = None
|
||||||
|
for kw in keywords:
|
||||||
|
if kw in line:
|
||||||
|
matched_kw = kw
|
||||||
|
break
|
||||||
|
if matched_kw:
|
||||||
|
issues.append(ValidationIssue(
|
||||||
|
checker='LinkRecommender',
|
||||||
|
issue_type='internal_link_opportunity',
|
||||||
|
severity='info',
|
||||||
|
line_number=line_idx,
|
||||||
|
matched_text=matched_kw,
|
||||||
|
message=f'"{matched_kw}" 언급 발견 — "{display_name}" 글로의 내부 링크를 고려하세요.',
|
||||||
|
suggestion=f'"{display_name}" 시술 페이지로 연결되는 앵커 텍스트 링크를 추가하세요.',
|
||||||
|
))
|
||||||
|
break # one recommendation per topic
|
||||||
|
|
||||||
|
return issues
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user