1842f27d89
- 채널 인지화: news_sources.source_channel(324, documents enum 재사용) → 문서 생성 정체성(_doc_identity)·embed/chunk 30일 게이트(crawl=전량 색인)· extract 후속 override(crawl→classify, preview 스킵) 분기. - B-2 Guardian Open Platform: API 디스패치(호스트 분기, 미지 호스트=명시 실패) + show-fields=bodyText 전문 어댑터. fixture live 박제 + call-shape 테스트. - B-3 구독지: playwright-fetcher 격리 컨테이너(동시 1·요청당 브라우저·storage_state ro mount) + politeness 사람속도(30-60s) 브라우저 경로 + fulltext 인증 라우팅 (내용 기반 probe 게이트·relogin_requested 소비=open-스킵보다 앞·본문 페이월 마커 게이트) + source_health probe 컬럼(325) + 세션 박제 스크립트(맥북용). - C-2 KOSHA: 3 API live 검증·fixture 박제(board/attach/guide) — 재해사례 daily diff +첨부 PDF/HWP→extract 파이프라인, GUIDE 일일 cap 점진 백필(silent cap 금지 로그). 키는 URL 직결합(재인코딩 함정 회피). daily 06:40 KST. - C-3 정적 코퍼스: National Board 86 + TWI job-knowledge 153 일괄 CLI(멱등·politeness ·crawl_raw 보존·fulltext_worker 승격 필드 규약 동일). - C-1/C-5 시드(326): 전 URL live 검증 — UK HSE(feed-full)/안전신문/고용노동부 3종 (rss/*.do)/OSHA/EU-OSHA(후보)/SEP/1000-Word(feed-full)/Doing Philosophy/Aeon/Psyche (skip-video quirk). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
56 lines
2.9 KiB
Python
56 lines
2.9 KiB
Python
"""news_sources 테이블 ORM"""
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import Boolean, DateTime, Enum, Integer, String, Text
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from core.database import Base
|
|
|
|
|
|
class NewsSource(Base):
|
|
__tablename__ = "news_sources"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
country: Mapped[str | None] = mapped_column(String(10))
|
|
feed_url: Mapped[str] = mapped_column(Text, nullable=False)
|
|
feed_type: Mapped[str] = mapped_column(String(20), default="rss")
|
|
category: Mapped[str | None] = mapped_column(String(50))
|
|
language: Mapped[str | None] = mapped_column(String(10))
|
|
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
last_fetched_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=datetime.now
|
|
)
|
|
|
|
# ── A-3 (plan crawl-24x7-1) 레지스트리 증축 — migration 319 ──
|
|
# fetch_method: rss / rss+page / sitemap+page / page / api / signal-only
|
|
fetch_method: Mapped[str] = mapped_column(String(20), default="rss")
|
|
# fulltext_policy: none(현행) / page(기사 페이지 fetch 후 4-tier 승격) / feed-full(피드 본문이 전문)
|
|
fulltext_policy: Mapped[str] = mapped_column(String(20), default="none")
|
|
# NULL=공개, 값=구독 세션 키 (B-3 Playwright 어댑터 슬롯)
|
|
auth_profile: Mapped[str | None] = mapped_column(String(50))
|
|
# 소스별 차등 폴링 (NULL=전역 6h 사이클)
|
|
poll_interval_minutes: Mapped[int | None] = mapped_column(Integer)
|
|
# 조건부 GET 워터마크 — 서버가 준 값 그대로 저장·재전송 (A-1)
|
|
etag: Mapped[str | None] = mapped_column(Text)
|
|
last_modified: Mapped[str | None] = mapped_column(Text)
|
|
# CDN ETag 회전 대비 콘텐츠 해시 변경감지 병행 (A-1)
|
|
feed_content_hash: Mapped[str | None] = mapped_column(String(64))
|
|
# 추출 실패 잦은 소스의 site-specific CSS selector (A-2)
|
|
selector_override: Mapped[dict | None] = mapped_column(JSONB)
|
|
# rdf / table-strip / gn-redirect / skip-video 등 파서 특이 케이스 (B-5)
|
|
parser_quirk: Mapped[str | None] = mapped_column(String(30))
|
|
# 채널 — 'news'(다이제스트/브리핑 대상) / 'crawl'(도메인 재료, 0-5 (a)) — migration 324.
|
|
# documents.source_channel 로 전파, crawl 채널은 embed/chunk 30일 게이트 미적용.
|
|
# documents 와 동일 PG enum 재사용 (Document 모델과 값 목록 동기 유지).
|
|
source_channel: Mapped[str] = mapped_column(
|
|
Enum("law_monitor", "devonagent", "email", "web_clip",
|
|
"tksafety", "inbox_route", "manual", "drive_sync", "news", "memo",
|
|
"voice", "hermes", "crawl",
|
|
name="source_channel"),
|
|
default="news",
|
|
)
|