3feddd012b
plan safety-library-1 A-2 (classify-skip 경로 전수 커버): - news_sources 에 material_type/license_scheme/license_redistribute + 안전·공학 12행 시드 - news_collector: 레지스트리 → documents 전파 (_material_axis — paper 는 jurisdiction NULL 강제) - kosha(사례·첨부=incident, GUIDE=guide)/csb(incident·US)/api_std(standard·US)/law_monitor(law·KR) /file_watcher(KGS=law·KR 타깃 매핑) deterministic 부여 + extract_meta.license 주입 - published_date: 소스별 가용 날짜 (GUIDE 공표일·CSB lastmod·API 공지일·법령 공포일·뉴스 발행일) - classify_worker: document_type→material_type 결정적 매핑 제안 (자동 전이 금지) - accept-suggestion: material 제안 적용 + law=jurisdiction 필수(기본값 없음) + 청크 미러 1문 동기화 - chunk_worker: 비뉴스 문서 country=jurisdiction 미러 (R3-m3: 검색측 country 소비자 0 실측) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
3.5 KiB
Python
65 lines
3.5 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",
|
|
)
|
|
|
|
# ── 안전 자료실 분류 축 (plan safety-library-1 A-2, migrations 352~355) ──
|
|
# 자료유형 기본값 — documents.material_type 으로 ingest 시점 전파 (NULL=비대상).
|
|
# jurisdiction 은 별도 컬럼 없이 country 전파, 단 paper 는 코드에서 NULL 강제.
|
|
material_type: Mapped[str | None] = mapped_column(Text)
|
|
# extract_meta.license 주입용 — kogl/ogl/public_domain/proprietary/unknown.
|
|
# 미확정 = 보수적(unknown + redistribute=false), 근거 확보 시 완화.
|
|
license_scheme: Mapped[str | None] = mapped_column(Text)
|
|
license_redistribute: Mapped[bool | None] = mapped_column(Boolean)
|