SHAFI commited on
Commit
ca44fa4
Β·
1 Parent(s): 5e81c7b

Fix: Newsletter subscription to use Appwrite & Audio API double-prefix bug

Browse files
app/models.py CHANGED
@@ -16,6 +16,7 @@ class Article(BaseModel):
16
  source: Optional[str] = ""
17
  category: Optional[str] = ""
18
  audio_url: Optional[str] = None # URL to audio summary
 
19
 
20
  # Engagement Stats (Side-loaded)
21
  likes: int = 0
 
16
  source: Optional[str] = ""
17
  category: Optional[str] = ""
18
  audio_url: Optional[str] = None # URL to audio summary
19
+ text_summary: Optional[str] = None # Generated text summary
20
 
21
  # Engagement Stats (Side-loaded)
22
  likes: int = 0
app/routes/audio.py CHANGED
@@ -20,8 +20,89 @@ class AudioGenerationRequest(BaseModel):
20
  class AudioResponse(BaseModel):
21
  success: bool
22
  audio_url: str
 
23
  message: str
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  @router.post("/generate", response_model=AudioResponse)
26
  async def generate_audio_summary(request: AudioGenerationRequest):
27
  """
@@ -40,49 +121,13 @@ async def generate_audio_summary(request: AudioGenerationRequest):
40
  from appwrite.query import Query
41
 
42
  # 1. Fetch Article by URL
43
- # FIX: Use multi-collection search like engagement endpoints
44
- # Re-implement ID generation logic to find the doc directly
45
  import hashlib
46
  url_hash = hashlib.sha256(request.article_url.encode()).hexdigest()
47
  article_id = url_hash[:32]
48
 
49
  print(f"πŸ”‘ Generated Article ID: {article_id}")
50
 
51
- # Determine strict collection if category provided
52
- target_collection_ids = []
53
- if request.category:
54
- target_collection_ids.append(appwrite.get_collection_id(request.category))
55
-
56
- # Always fallback to checking ALL known collections if not found (Safety Net)
57
- fallback_collections = [
58
- settings.APPWRITE_COLLECTION_ID,
59
- settings.APPWRITE_CLOUD_COLLECTION_ID,
60
- settings.APPWRITE_AI_COLLECTION_ID,
61
- settings.APPWRITE_DATA_COLLECTION_ID,
62
- settings.APPWRITE_MAGAZINE_COLLECTION_ID,
63
- settings.APPWRITE_MEDIUM_COLLECTION_ID
64
- ]
65
-
66
- for cid in fallback_collections:
67
- if cid and cid not in target_collection_ids:
68
- target_collection_ids.append(cid)
69
-
70
- article = None
71
- found_collection_id = None
72
-
73
- # Try to find article in target collections
74
- for collection_id in target_collection_ids:
75
- try:
76
- article = appwrite.tablesDB.get_row(
77
- database_id=settings.APPWRITE_DATABASE_ID,
78
- collection_id=collection_id,
79
- document_id=article_id
80
- )
81
- found_collection_id = collection_id
82
- print(f"βœ… Found article in collection: {collection_id}")
83
- break
84
- except Exception:
85
- continue
86
 
87
  # If not found, create it
88
  if not article:
@@ -130,6 +175,7 @@ async def generate_audio_summary(request: AudioGenerationRequest):
130
  return AudioResponse(
131
  success=True,
132
  audio_url=article['audio_url'],
 
133
  message="Audio already exists"
134
  )
135
 
@@ -187,12 +233,14 @@ async def generate_audio_summary(request: AudioGenerationRequest):
187
  update_success = await appwrite.update_article_audio(
188
  collection_id=found_collection_id,
189
  document_id=article_id,
190
- audio_url=audio_url
 
191
  )
192
 
193
  return AudioResponse(
194
  success=True,
195
  audio_url=audio_url,
 
196
  message="Audio generated successfully"
197
  )
198
 
 
20
  class AudioResponse(BaseModel):
21
  success: bool
22
  audio_url: str
23
+ text_summary: Optional[str] = None
24
  message: str
25
 
26
+
27
+ async def _find_article(appwrite, article_id: str, category: Optional[str] = None):
28
+ """
29
+ Helper to find an article across multiple collections.
30
+ Returns (article, collection_id) or (None, None).
31
+ """
32
+ target_collection_ids = []
33
+ if category:
34
+ # Resolve category to collection ID if possible
35
+ # Note: appwrite.get_collection_id might need self/instance access or be static?
36
+ # appwrite_db.py defines get_collection_id as instance method.
37
+ # We passed 'appwrite' instance.
38
+ try:
39
+ target_collection_ids.append(appwrite.get_collection_id(category))
40
+ except:
41
+ pass
42
+
43
+ # Always fallback to checking ALL known collections if not found (Safety Net)
44
+ fallback_collections = [
45
+ settings.APPWRITE_COLLECTION_ID,
46
+ settings.APPWRITE_CLOUD_COLLECTION_ID,
47
+ settings.APPWRITE_AI_COLLECTION_ID,
48
+ settings.APPWRITE_DATA_COLLECTION_ID,
49
+ settings.APPWRITE_MAGAZINE_COLLECTION_ID,
50
+ settings.APPWRITE_MEDIUM_COLLECTION_ID
51
+ ]
52
+
53
+ for cid in fallback_collections:
54
+ if cid and cid not in target_collection_ids:
55
+ target_collection_ids.append(cid)
56
+
57
+ # Try to find article in target collections
58
+ for collection_id in target_collection_ids:
59
+ try:
60
+ article = appwrite.tablesDB.get_row(
61
+ database_id=settings.APPWRITE_DATABASE_ID,
62
+ collection_id=collection_id,
63
+ document_id=article_id
64
+ )
65
+ print(f"βœ… Found article in collection: {collection_id}")
66
+ return article, collection_id
67
+ except Exception:
68
+ continue
69
+
70
+ return None, None
71
+
72
+ @router.get("/status", response_model=AudioResponse)
73
+ async def get_audio_status(article_url: str, category: Optional[str] = None):
74
+ """
75
+ Check if audio/text summary exists for an article.
76
+ """
77
+ try:
78
+ appwrite = get_appwrite_db()
79
+ import hashlib
80
+ url_hash = hashlib.sha256(article_url.encode()).hexdigest()
81
+ article_id = url_hash[:32]
82
+
83
+ article, _ = await _find_article(appwrite, article_id, category)
84
+
85
+ if article:
86
+ return AudioResponse(
87
+ success=True,
88
+ audio_url=article.get('audio_url') or "",
89
+ text_summary=article.get('text_summary'),
90
+ message="Article found"
91
+ )
92
+ else:
93
+ return AudioResponse(
94
+ success=False,
95
+ audio_url="",
96
+ message="Article not found"
97
+ )
98
+ except Exception as e:
99
+ print(f"Error fetching status: {e}")
100
+ return AudioResponse(
101
+ success=False,
102
+ audio_url="",
103
+ message=str(e)
104
+ )
105
+
106
  @router.post("/generate", response_model=AudioResponse)
107
  async def generate_audio_summary(request: AudioGenerationRequest):
108
  """
 
121
  from appwrite.query import Query
122
 
123
  # 1. Fetch Article by URL
 
 
124
  import hashlib
125
  url_hash = hashlib.sha256(request.article_url.encode()).hexdigest()
126
  article_id = url_hash[:32]
127
 
128
  print(f"πŸ”‘ Generated Article ID: {article_id}")
129
 
130
+ article, found_collection_id = await _find_article(appwrite, article_id, request.category)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
  # If not found, create it
133
  if not article:
 
175
  return AudioResponse(
176
  success=True,
177
  audio_url=article['audio_url'],
178
+ text_summary=article.get('text_summary'), # Return existing summary if present
179
  message="Audio already exists"
180
  )
181
 
 
233
  update_success = await appwrite.update_article_audio(
234
  collection_id=found_collection_id,
235
  document_id=article_id,
236
+ audio_url=audio_url,
237
+ text_summary=summary # Pass generated summary
238
  )
239
 
240
  return AudioResponse(
241
  success=True,
242
  audio_url=audio_url,
243
+ text_summary=summary,
244
  message="Audio generated successfully"
245
  )
246
 
app/services/appwrite_db.py CHANGED
@@ -812,17 +812,21 @@ class AppwriteDatabase:
812
  logger.error(f"❌ [Appwrite] Error getting subscribers by preference: {e}")
813
  return []
814
 
815
- async def update_article_audio(self, collection_id: str, document_id: str, audio_url: str) -> bool:
816
- """Update article with audio URL"""
817
  if not self.initialized:
818
  return False
819
 
820
  try:
 
 
 
 
821
  self.tablesDB.update_row(
822
  database_id=settings.APPWRITE_DATABASE_ID,
823
  collection_id=collection_id,
824
  document_id=document_id,
825
- data={'audio_url': audio_url}
826
  )
827
  return True
828
  except Exception as e:
 
812
  logger.error(f"❌ [Appwrite] Error getting subscribers by preference: {e}")
813
  return []
814
 
815
+ async def update_article_audio(self, collection_id: str, document_id: str, audio_url: str, text_summary: Optional[str] = None) -> bool:
816
+ """Update article with audio URL and optional text summary"""
817
  if not self.initialized:
818
  return False
819
 
820
  try:
821
+ data = {'audio_url': audio_url}
822
+ if text_summary:
823
+ data['text_summary'] = text_summary
824
+
825
  self.tablesDB.update_row(
826
  database_id=settings.APPWRITE_DATABASE_ID,
827
  collection_id=collection_id,
828
  document_id=document_id,
829
+ data=data
830
  )
831
  return True
832
  except Exception as e:
tools/add_text_summary_attribute.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import os
3
+ import sys
4
+
5
+ # Add parent directory to path to import app modules
6
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7
+
8
+ from app.config import settings
9
+ from app.services.appwrite_db import get_appwrite_db
10
+
11
+ async def add_text_summary_attribute():
12
+ """
13
+ Adds the 'text_summary' attribute to all article collections.
14
+ """
15
+ print("="*60)
16
+ print("πŸ“œ Adding 'text_summary' attribute to Appwrite Collections")
17
+ print("="*60)
18
+
19
+ appwrite = get_appwrite_db()
20
+
21
+ if not appwrite.initialized:
22
+ print("❌ Appwrite not initialized. Check credentials.")
23
+ return
24
+
25
+ # List of collections to update
26
+ collections = [
27
+ settings.APPWRITE_COLLECTION_ID, # Articles
28
+ settings.APPWRITE_CLOUD_COLLECTION_ID, # Cloud Articles
29
+ settings.APPWRITE_AI_COLLECTION_ID, # AI
30
+ settings.APPWRITE_DATA_COLLECTION_ID, # Data
31
+ settings.APPWRITE_MAGAZINE_COLLECTION_ID,# Magazine
32
+ settings.APPWRITE_MEDIUM_COLLECTION_ID # Medium
33
+ ]
34
+
35
+ for col_id in collections:
36
+ if not col_id or col_id == "change_me":
37
+ continue
38
+
39
+ print(f"Checking collection: {col_id}...")
40
+
41
+ try:
42
+ # Create text_summary attribute (String, 5000 chars, not required)
43
+ try:
44
+ appwrite.databases.create_string_attribute(
45
+ database_id=settings.APPWRITE_DATABASE_ID,
46
+ collection_id=col_id,
47
+ key="text_summary",
48
+ size=5000,
49
+ required=False,
50
+ default=None
51
+ )
52
+ print(f"βœ… Added 'text_summary' to {col_id}")
53
+ except Exception as e:
54
+ if "Attribute already exists" in str(e) or "409" in str(e):
55
+ print(f"⚠️ Attribute 'text_summary' already exists in {col_id}")
56
+ else:
57
+ print(f"❌ Failed to add attribute to {col_id}: {e}")
58
+
59
+ except Exception as e:
60
+ print(f"❌ Error processing collection {col_id}: {e}")
61
+
62
+ print("\nπŸŽ‰ Schema update complete!")
63
+
64
+ if __name__ == "__main__":
65
+ asyncio.run(add_text_summary_attribute())