# ========================= # models.py (scrapper) # ========================= # The scrapper only needs the tables required to (a) know what to fetch and # (b) hand raw HTML off to the LLM service. All parsing / sentiment / sector / # stock-metric tables live in the LLM service. from sqlalchemy import ( Column, String, Text, Integer, text as text_func, ) import time import uuid from database import Base # raw_html.type is stored as one of these plain lowercase strings. NEWS = "news" STOCK = "stock" # Macro/market series types. Each MacroSource row carries one of these as its # raw_html.type, so the LLM side can route it to the right processor. FII_DII = "fii_dii" VIX = "vix" COMMODITY = "commodity" INDEX = "index" FX = "fx" YIELD = "yield" MACRO_TYPES = (FII_DII, VIX, COMMODITY, INDEX, FX, YIELD) class RawHTML(Base): """A single fetched page. Stored verbatim (true raw HTML) and served to the LLM service via GET_RAW_HTML, then hard-deleted via DELETE_RAW_HTML once the consumer has stored its own copy. `type` is a plain lowercase string ("news" / "stock") to keep value handling unambiguous across the HTTP boundary. """ __tablename__ = "raw_html" id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) source = Column(String, nullable=False) html = Column(Text, nullable=False) created_at = Column(Integer, default=lambda: int(time.time() * 1000)) type = Column(String, nullable=False) # Market country for stock pages (e.g. "INDIA", "UNITED STATES"), derived # from the StockSources row at fetch time. NULL for news pages. country = Column(String, nullable=True) # ========================= # NEWS SOURCES TABLE # ========================= class NewsSource(Base): __tablename__ = "news_sources" id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) name = Column(String, nullable=False, unique=True) # `website` is the URL the scraper fetches for this news source. website = Column(String, nullable=False) created_at = Column(Integer, default=lambda: int(time.time() * 1000)) last_fetched_at = Column(Integer, nullable=True) last_attempted_at = Column(Integer, nullable=True) failure_count = Column( Integer, nullable=False, default=0, server_default=text_func("0") ) failure_reason = Column(Text, nullable=True) type = Column(String, nullable=False) # ========================= # STOCK SOURCES TABLE # ========================= class StockSources(Base): __tablename__ = "stock_sources" id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) created_at = Column(Integer, default=lambda: int(time.time() * 1000)) updated_at = Column(Integer, nullable=True) company_name = Column(String, nullable=False) ticker = Column(String, nullable=False) country = Column(String, nullable=False) exchange = Column(String, nullable=False) sector = Column(String, nullable=False) # Pre-built Google Finance URL, computed once when the CSV is loaded so the # stock_enhancer worker just fetches it instead of constructing it per tick. url = Column(String, nullable=True) last_fetched_at = Column(Integer, nullable=True) last_attempted_at = Column(Integer, nullable=True) failure_count = Column( Integer, nullable=False, default=0, server_default=text_func("0") ) failure_reason = Column(Text, nullable=True) # ========================= # MACRO SOURCES TABLE # ========================= # Config-driven market/macro series (FII/DII, VIX, commodities, indices, FX, # bond yields). Mirrors StockSources' fetch/failure shape but carries a `type` # (one of MACRO_TYPES) and a `market` (key into config.MARKET_CONFIG) instead of # ticker/sector semantics. The same generic fetch-after-close worker drives it. class MacroSource(Base): __tablename__ = "macro_sources" id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4())) created_at = Column(Integer, default=lambda: int(time.time() * 1000)) updated_at = Column(Integer, nullable=True) name = Column(String, nullable=False) type = Column(String, nullable=False) # one of MACRO_TYPES country = Column(String, nullable=True) market = Column(String, nullable=False) # key into MARKET_CONFIG exchange = Column(String, nullable=True) # Pre-built fetch URL (Google Finance quote URL or a raw page URL), computed # once when the CSV is loaded. url = Column(String, nullable=True) last_fetched_at = Column(Integer, nullable=True) last_attempted_at = Column(Integer, nullable=True) failure_count = Column( Integer, nullable=False, default=0, server_default=text_func("0") ) failure_reason = Column(Text, nullable=True)