from sqlalchemy import Column, Integer, String, DateTime, Float, ForeignKey, Boolean, Enum as SQLEnum from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr from datetime import datetime import enum from .database import Base # --- Priority Enum --- class Priority(enum.Enum): """News priority levels""" LOW = "low" MEDIUM = "medium" HIGH = "high" CRITICAL = "critical" # Deprem, katliam gibi önemli haberler # --- Article Model --- class Article(Base): """ Represents a single news article fetched from an RSS feed. """ __tablename__ = "articles" # Primary fields from RSS feed id = Column(Integer, primary_key=True, index=True) title = Column(String, index=True) link = Column(String, unique=True, index=True) # Unique link to prevent duplicates summary = Column(String) published_date = Column(DateTime, default=datetime.utcnow) source_name = Column(String) # e.g., "BBC News World" # Priority field for critical news (deprem, katliam, etc.) priority = Column(SQLEnum(Priority), default=Priority.MEDIUM, index=True) # Sentiment Analysis fields sentiment_score = Column(Float, nullable=True, index=True) # -1 to 1 sentiment_label = Column(String, nullable=True) # positive, neutral, negative sentiment_analyzer = Column(String, nullable=True) # textblob, vader, hybrid analyzed_at = Column(DateTime, nullable=True) # Status fields for processing pipeline is_processed = Column(Boolean, default=False) # Has NLP and Geocoding been run? processing_error = Column(String, nullable=True) # To store error if processing fails # Relationship to Locations: An article can be related to multiple locations locations = relationship("LocationBase", back_populates="article") def __repr__(self): return f"" # --- Location Base Class (Abstract) --- class LocationBase(Base): """ Base class for all location types (City, Country, Continent). Uses single table inheritance pattern. """ __tablename__ = "locations" id = Column(Integer, primary_key=True, index=True) # Foreign key link to the article article_id = Column(Integer, ForeignKey("articles.id"), nullable=True) article = relationship("Article", back_populates="locations") # Common fields for all location types entity_name = Column(String, index=True) # The text entity (e.g., "Paris", "Ukraine", "Europe") entity_type = Column(String) # The spaCy NER label (e.g., 'GPE', 'LOC') latitude = Column(Float, index=True) longitude = Column(Float, index=True) geocoder_source = Column(String, default="Nominatim") # Discriminator column for inheritance location_type = Column(String(20), nullable=False) # 'city', 'country', 'continent' __mapper_args__ = { 'polymorphic_identity': 'location', 'polymorphic_on': location_type } def __repr__(self): return f"<{self.__class__.__name__}(entity='{self.entity_name}', coords=({self.latitude}, {self.longitude}))>" # --- City Model --- class City(LocationBase): """ Represents a city location. """ __mapper_args__ = { 'polymorphic_identity': 'city', } country_id = Column(Integer, ForeignKey("locations.id"), nullable=True) population = Column(Integer, nullable=True) # --- Country Model --- class Country(LocationBase): """ Represents a country location. """ __mapper_args__ = { 'polymorphic_identity': 'country', } continent_id = Column(Integer, ForeignKey("locations.id"), nullable=True) country_code = Column(String(3), nullable=True) # ISO country code # --- Continent Model --- class Continent(LocationBase): """ Represents a continent location. """ __mapper_args__ = { 'polymorphic_identity': 'continent', }