Coverage for src/local_deep_research/domain_classifier/models.py: 100%
18 statements
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-19 23:35 +0000
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-19 23:35 +0000
1"""Database models for domain classification."""
3from sqlalchemy import Column, String, Text, Float, Integer, Index
4from sqlalchemy_utc import UtcDateTime, utcnow
6# Import Base from the dependency-free database.base leaf, NOT the
7# ..database.models package. database.models eagerly imports every model —
8# including this one — so importing Base through it would re-enter a
9# partially-initialized database.models and raise "cannot import name
10# 'DomainClassification'". database.base holds the same Base object, so metadata
11# registration is unchanged. Regression: tests/test_domain_classifier_no_import_cycle.py
12from ..database.base import Base
15class DomainClassification(Base):
16 """Store domain classifications generated by LLM."""
18 __tablename__ = "domain_classifications"
20 id = Column(Integer, primary_key=True, autoincrement=True)
21 domain = Column(String(255), unique=True, nullable=False, index=True)
22 category = Column(String(100), nullable=False)
23 subcategory = Column(String(100))
24 confidence = Column(Float, default=0.0)
25 reasoning = Column(Text) # Store LLM's reasoning for the classification
26 sample_titles = Column(Text) # JSON array of sample titles from this domain
27 sample_count = Column(
28 Integer, default=0
29 ) # Number of resources used for classification
30 created_at = Column(UtcDateTime, default=utcnow())
31 updated_at = Column(UtcDateTime, default=utcnow(), onupdate=utcnow())
33 # Create index for faster lookups
34 __table_args__ = (Index("idx_domain_category", "domain", "category"),)
36 def to_dict(self):
37 """Convert to dictionary for JSON serialization."""
38 return {
39 "id": self.id,
40 "domain": self.domain,
41 "category": self.category,
42 "subcategory": self.subcategory,
43 "confidence": self.confidence,
44 "reasoning": self.reasoning,
45 "sample_titles": self.sample_titles,
46 "sample_count": self.sample_count,
47 "created_at": self.created_at.isoformat()
48 if self.created_at
49 else None,
50 "updated_at": self.updated_at.isoformat()
51 if self.updated_at
52 else None,
53 }