AuthorBot Cursor commited on
Commit
9910ee4
·
1 Parent(s): 4e43a29

Auto-rescan format prices in background when verified listings have none

Browse files
app/admin/routers/books.py CHANGED
@@ -48,9 +48,12 @@ async def get_book_platform_presence(
48
  book_id: str,
49
  current_user=Depends(get_current_author_scoped),
50
  db: AsyncSession = Depends(get_db),
 
51
  ):
52
  """Return stored platform presence scan for a book."""
53
- result = await BookService(db).get_platform_presence(current_user.id, book_id)
 
 
54
  if result is None:
55
  raise HTTPException(404, "Book not found")
56
  return result
 
48
  book_id: str,
49
  current_user=Depends(get_current_author_scoped),
50
  db: AsyncSession = Depends(get_db),
51
+ redis=Depends(get_redis),
52
  ):
53
  """Return stored platform presence scan for a book."""
54
+ result = await BookService(db).get_platform_presence(
55
+ current_user.id, book_id, redis=redis,
56
+ )
57
  if result is None:
58
  raise HTTPException(404, "Book not found")
59
  return result
app/services/book_service.py CHANGED
@@ -181,8 +181,14 @@ class BookService:
181
  ],
182
  }
183
 
184
- async def get_platform_presence(self, author_id: str, book_id: str) -> dict | None:
185
- """Return stored platform presence scan. None if book not found."""
 
 
 
 
 
 
186
  from app.services.platform_catalog import platform_catalog_snapshot
187
 
188
  book = await self._books.get_by_id(book_id)
@@ -197,12 +203,49 @@ class BookService:
197
  "book": self._book_meta(book),
198
  }
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  return {
201
  "platform_presence": presence_dict_from_db_listings(listings, source_isbn=book.isbn),
202
  "listings": platform_catalog_snapshot(listings),
203
  "book": self._book_meta(book),
204
  }
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  async def upsert_platform_listing(
207
  self,
208
  author_id: str,
 
181
  ],
182
  }
183
 
184
+ async def get_platform_presence(
185
+ self, author_id: str, book_id: str, *, redis=None,
186
+ ) -> dict | None:
187
+ """Return stored platform presence scan for a book.
188
+
189
+ If all verified listings have empty format_prices, schedule a background
190
+ rescan so format data gets populated without blocking the response.
191
+ """
192
  from app.services.platform_catalog import platform_catalog_snapshot
193
 
194
  book = await self._books.get_by_id(book_id)
 
203
  "book": self._book_meta(book),
204
  }
205
 
206
+ verified = [
207
+ r for r in listings
208
+ if getattr(r, "status", None) == "verified"
209
+ ]
210
+ needs_format_scan = verified and all(
211
+ not (getattr(r, "format_prices", None) or {})
212
+ for r in verified
213
+ )
214
+ if needs_format_scan and redis is not None and book.title:
215
+ import asyncio
216
+ asyncio.create_task(self._background_format_rescan(
217
+ author_id, book_id, book, redis,
218
+ ))
219
+
220
  return {
221
  "platform_presence": presence_dict_from_db_listings(listings, source_isbn=book.isbn),
222
  "listings": platform_catalog_snapshot(listings),
223
  "book": self._book_meta(book),
224
  }
225
 
226
+ async def _background_format_rescan(
227
+ self, author_id: str, book_id: str, book, redis,
228
+ ) -> None:
229
+ """Run a fresh platform presence scan to populate format_prices."""
230
+ try:
231
+ scan = await scan_platform_presence(
232
+ title=book.title,
233
+ author=getattr(book, "book_author", None) or "",
234
+ isbn=getattr(book, "isbn", None) or None,
235
+ source_platform=getattr(book, "source_platform", None) or None,
236
+ source_url=getattr(book, "source_url", None) or None,
237
+ buy_url=getattr(book, "source_url", None) or None,
238
+ db=self._db,
239
+ redis=redis,
240
+ )
241
+ if scan is not None:
242
+ await self._listings.replace_from_scan(author_id, book_id, scan)
243
+ await self._db.commit()
244
+ if book.source_url:
245
+ await save_scan_to_redis(redis, book.source_url, scan)
246
+ except Exception:
247
+ logger.warning("background_format_rescan_failed", book_id=book_id)
248
+
249
  async def upsert_platform_listing(
250
  self,
251
  author_id: str,