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"<Article(title='{self.title[:30]}...', link='{self.link}')>"
# --- 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',
}