Coverage for src/local_deep_research/news/core/storage.py: 71%
69 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"""
2Base storage interfaces for the news system.
3"""
5from abc import ABC, abstractmethod
6from typing import List, Optional, Dict, Any
7import uuid
10class BaseStorage(ABC):
11 """Abstract base class for all storage interfaces"""
13 @abstractmethod
14 def create(self, data: Dict[str, Any]) -> str:
15 """Create a new record and return its ID"""
16 pass
18 @abstractmethod
19 def get(self, id: str) -> Optional[Dict[str, Any]]:
20 """Get a record by ID"""
21 pass
23 @abstractmethod
24 def update(self, id: str, data: Dict[str, Any]) -> bool:
25 """Update a record, return True if successful"""
26 pass
28 @abstractmethod
29 def delete(self, id: str) -> bool:
30 """Delete a record, return True if successful"""
31 pass
33 @abstractmethod
34 def list(
35 self,
36 filters: Optional[Dict[str, Any]] = None,
37 limit: int = 100,
38 offset: int = 0,
39 ) -> List[Dict[str, Any]]:
40 """List records with optional filtering"""
41 pass
43 def generate_id(self) -> str:
44 """Generate a unique ID"""
45 return str(uuid.uuid4())
48class CardStorage(BaseStorage):
49 """Interface for news card storage"""
51 @abstractmethod
52 def get_by_user(
53 self, user_id: str, limit: int = 50, offset: int = 0
54 ) -> List[Dict[str, Any]]:
55 """Get cards for a specific user"""
56 pass
58 @abstractmethod
59 def get_latest_version(self, card_id: str) -> Optional[Dict[str, Any]]:
60 """Get the latest version of a card"""
61 pass
63 @abstractmethod
64 def add_version(self, card_id: str, version_data: Dict[str, Any]) -> str:
65 """Add a new version to a card"""
66 pass
68 @abstractmethod
69 def update_latest_info(
70 self, card_id: str, version_data: Dict[str, Any]
71 ) -> bool:
72 """Update the denormalized latest version info on the card"""
73 pass
75 @abstractmethod
76 def archive_card(self, card_id: str) -> bool:
77 """Archive a card"""
78 pass
80 @abstractmethod
81 def pin_card(self, card_id: str, pinned: bool = True) -> bool:
82 """Pin or unpin a card"""
83 pass
86class RatingStorage(BaseStorage):
87 """Interface for rating storage"""
89 @abstractmethod
90 def get_user_rating(
91 self, user_id: str, item_id: str, rating_type: str
92 ) -> Optional[Dict[str, Any]]:
93 """Get a user's rating for a specific item"""
94 pass
96 @abstractmethod
97 def upsert_rating(
98 self,
99 user_id: str,
100 item_id: str,
101 rating_type: str,
102 rating_value: str,
103 item_type: str = "card",
104 ) -> str:
105 """Create or update a rating"""
106 pass
108 @abstractmethod
109 def get_ratings_summary(
110 self, item_id: str, item_type: str = "card"
111 ) -> Dict[str, Any]:
112 """Get aggregated ratings for an item"""
113 pass
115 @abstractmethod
116 def get_user_ratings(
117 self, user_id: str, rating_type: Optional[str] = None, limit: int = 100
118 ) -> List[Dict[str, Any]]:
119 """Get all ratings by a user"""
120 pass
123class PreferenceStorage(BaseStorage):
124 """Interface for user preference storage"""
126 @abstractmethod
127 def get_user_preferences(self, user_id: str) -> Optional[Dict[str, Any]]:
128 """Get preferences for a user"""
129 pass
131 @abstractmethod
132 def upsert_preferences(
133 self, user_id: str, preferences: Dict[str, Any]
134 ) -> str:
135 """Create or update user preferences"""
136 pass
138 @abstractmethod
139 def add_liked_item(
140 self, user_id: str, item_id: str, item_type: str = "news"
141 ) -> bool:
142 """Add an item to liked list"""
143 pass
145 @abstractmethod
146 def add_disliked_item(
147 self, user_id: str, item_id: str, item_type: str = "news"
148 ) -> bool:
149 """Add an item to disliked list"""
150 pass
152 @abstractmethod
153 def update_preference_embedding(
154 self, user_id: str, embedding: List[float]
155 ) -> bool:
156 """Update the user's preference embedding"""
157 pass