1- import json
21from abc import ABC , abstractmethod
32from datetime import datetime
4- from typing import cast
53from urllib .parse import parse_qs
64
75import web
86from pydantic import BaseModel
9- from typing_extensions import deprecated
107
11- from infogami .utils import delegate
128from infogami .utils .view import render_template
139from openlibrary .accounts import get_current_user
1410from openlibrary .core .fulltext import fulltext_search
2420from openlibrary .views .loanstats import get_trending_books
2521
2622
27- class PartialResolutionError (Exception ):
28- pass
29-
30-
3123class PartialDataHandler (ABC ):
3224 """Base class for partial data handlers.
3325
@@ -44,11 +36,8 @@ def generate(self) -> dict:
4436class ReadingGoalProgressPartial (PartialDataHandler ):
4537 """Handler for reading goal progress."""
4638
47- def __init__ (self , year : int | None = None ):
48- if year is None :
49- self .year = web .input (year = None ).year
50- else :
51- self .year = year
39+ def __init__ (self , year : int ):
40+ self .year = year
5241
5342 def generate (self ) -> dict :
5443 year = self .year or datetime .now ().year
@@ -93,43 +82,14 @@ class CarouselLoadMoreParams(BaseModel):
9382 layout : str | None = None
9483 published_in : str = ""
9584
96- @classmethod
97- def from_web_input (cls ) -> "CarouselLoadMoreParams" :
98- """Construct from web.py's web.input(), handling string-encoded booleans."""
99- # TODO: Delete this complicated code after we switch to FastAPI
100- i = web .input (
101- queryType = "" ,
102- q = "" ,
103- limit = 18 ,
104- page = 1 ,
105- sorts = "" ,
106- subject = "" ,
107- hasFulltextOnly = False ,
108- key = "" ,
109- layout = None ,
110- published_in = "" ,
111- )
112- return cls (
113- queryType = i .queryType ,
114- q = i .q ,
115- limit = int (i .limit ),
116- page = int (i .page ),
117- sorts = i .sorts ,
118- subject = i .subject ,
119- hasFulltextOnly = bool (i .hasFulltextOnly ),
120- key = i .key ,
121- layout = i .layout ,
122- published_in = i .published_in ,
123- )
124-
12585
12686class CarouselCardPartial (PartialDataHandler ):
12787 """Handler for carousel "load_more" requests"""
12888
12989 MAX_VISIBLE_CARDS = 5
13090
131- def __init__ (self , params : CarouselLoadMoreParams | None = None ):
132- self .params = params or CarouselLoadMoreParams . from_web_input ()
91+ def __init__ (self , params : CarouselLoadMoreParams ):
92+ self .params = params
13393
13494 def generate (self ) -> dict :
13595 p = self .params
@@ -233,18 +193,14 @@ def _do_subjects_query(self, params: CarouselLoadMoreParams) -> list:
233193class AffiliateLinksPartial (PartialDataHandler ):
234194 """Handler for affiliate links"""
235195
236- def __init__ (self , data : dict | None = None ):
237- if data is None :
238- i = web .input (data = None )
239- self .data = json .loads (i .data ) if i .data else {}
240- else :
241- self .data = data
196+ def __init__ (self , data : dict ):
197+ self .data = data
242198
243199 def generate (self ) -> dict :
244200 args = self .data .get ("args" , [])
245201
246202 if len (args ) < 2 :
247- raise PartialResolutionError ("Unexpected amount of arguments" )
203+ raise ValueError ("Unexpected amount of arguments" )
248204
249205 macro = web .template .Template .globals ['macros' ].AffiliateLinks (args [0 ], args [1 ])
250206 return {"partials" : str (macro )}
@@ -253,17 +209,13 @@ def generate(self) -> dict:
253209class SearchFacetsPartial (PartialDataHandler ):
254210 """Handler for search facets sidebar and "selected facets" affordances."""
255211
256- def __init__ (self , data : dict | None = None , sfw : bool = False ):
212+ def __init__ (self , data : dict , sfw : bool = False ):
257213 self .sfw = sfw
214+ self .data = data
258215 user = get_current_user ()
259216 self .show_merge_authors = user and (
260217 user .is_librarian () or user .is_super_librarian () or user .is_admin ()
261218 )
262- if data is None :
263- i = web .input (data = None )
264- self .data = json .loads (i .data ) if i .data else {}
265- else :
266- self .data = data
267219
268220 def generate (self ) -> dict :
269221 path = self .data .get ('path' )
@@ -311,25 +263,15 @@ def generate(self) -> dict:
311263class FullTextSuggestionsPartial (PartialDataHandler ):
312264 """Handler for rendering full-text search suggestions."""
313265
314- def __init__ (self , query : str | None = None ):
315- if query is None :
316- i = web .input (data = None )
317- self .query = i .get ("data" , "" )
318- self .webpy_mode = True
319- else :
320- self .query = query or ""
321- self .webpy_mode = False
266+ def __init__ (self , query : str ):
267+ self .query = query or ""
322268 self .has_error : bool = False
323269
324270 def generate (self ) -> dict :
325271 query = self .query
326272 data = fulltext_search (query )
327273 # Add caching headers only if there were no errors in the search results
328274 self .has_error = "error" in data
329- if not self .has_error and self .webpy_mode :
330- # Cache for 5 minutes (300 seconds)
331- # TODO: remove when we rip out the old partials endpoints
332- web .header ('Cache-Control' , 'public, max-age=300' )
333275 hits = data .get ('hits' , [])
334276 if not hits ['hits' ]:
335277 macro = '<div></div>'
@@ -343,15 +285,9 @@ def generate(self) -> dict:
343285class BookPageListsPartial (PartialDataHandler ):
344286 """Handler for rendering the book page "Lists" section"""
345287
346- def __init__ (self , workId : str = "" , editionId : str = "" ):
347- if not workId and not editionId :
348- # Only read from web.input if no params provided
349- i = web .input (workId = "" , editionId = "" )
350- self .workId = i .workId
351- self .editionId = i .editionId
352- else :
353- self .workId = workId
354- self .editionId = editionId
288+ def __init__ (self , workId : str , editionId : str ):
289+ self .workId = workId
290+ self .editionId = editionId
355291
356292 def generate (self ) -> dict :
357293 results : dict = {"partials" : []}
@@ -388,98 +324,30 @@ class LazyCarouselParams(BaseModel):
388324 layout : str = "carousel"
389325 fallback : str | None = None
390326
391- @classmethod
392- def from_web_input (cls ) -> "LazyCarouselParams" :
393- """Construct from web.py's web.input(), handling string-encoded booleans."""
394- i = web .input (
395- query = "" ,
396- title = None ,
397- sort = "new" ,
398- key = "" ,
399- limit = 20 ,
400- search = "false" ,
401- has_fulltext_only = True ,
402- url = None ,
403- layout = "carousel" ,
404- fallback = None ,
405- )
406- return cls (
407- query = i .query ,
408- title = i .title ,
409- sort = i .sort ,
410- key = i .key ,
411- limit = int (i .limit ),
412- search = i .search != "false" ,
413- has_fulltext_only = i .has_fulltext_only != "false" ,
414- url = i .url ,
415- layout = i .layout ,
416- fallback = i .fallback ,
417- )
418-
419327
420328class LazyCarouselPartial (PartialDataHandler ):
421329 """Handler for lazily-loaded query carousels."""
422330
423- def __init__ (self , params : LazyCarouselParams | None = None ):
424- self .i = params or LazyCarouselParams . from_web_input ()
331+ def __init__ (self , params : LazyCarouselParams ):
332+ self .params = params
425333
426334 def generate (self ) -> dict :
427335 macro = web .template .Template .globals ['macros' ].CacheableMacro (
428336 "RawQueryCarousel" ,
429- self .i .query ,
337+ self .params .query ,
430338 lazy = False ,
431- title = self .i .title ,
432- sort = self .i .sort ,
433- key = self .i .key ,
434- limit = self .i .limit ,
435- search = self .i .search ,
436- has_fulltext_only = self .i .has_fulltext_only ,
437- url = self .i .url ,
438- layout = self .i .layout ,
439- fallback = self .i .fallback ,
339+ title = self .params .title ,
340+ sort = self .params .sort ,
341+ key = self .params .key ,
342+ limit = self .params .limit ,
343+ search = self .params .search ,
344+ has_fulltext_only = self .params .has_fulltext_only ,
345+ url = self .params .url ,
346+ layout = self .params .layout ,
347+ fallback = self .params .fallback ,
440348 )
441349 return {"partials" : str (macro )}
442350
443351
444- class PartialRequestResolver :
445- # Maps `_component` values to PartialDataHandler subclasses
446- component_mapping = { # noqa: RUF012
447- "CarouselLoadMore" : CarouselCardPartial ,
448- "AffiliateLinks" : AffiliateLinksPartial ,
449- "SearchFacets" : SearchFacetsPartial ,
450- "FulltextSearchSuggestion" : FullTextSuggestionsPartial ,
451- "BPListsSection" : BookPageListsPartial ,
452- "LazyCarousel" : LazyCarouselPartial ,
453- "MyBooksDropperLists" : MyBooksDropperListsPartial ,
454- "ReadingGoalProgress" : ReadingGoalProgressPartial ,
455- }
456-
457- @staticmethod
458- def resolve (component : str ) -> dict :
459- """Gets an instantiated PartialDataHandler and returns its generated dict"""
460- handler = PartialRequestResolver .get_handler (component )
461- return handler .generate ()
462-
463- @classmethod
464- def get_handler (cls , component : str ) -> PartialDataHandler :
465- """Instantiates and returns the requested handler"""
466- if klass := cls .component_mapping .get (component ):
467- concrete_class = cast (type [PartialDataHandler ], klass )
468- return concrete_class ()
469- raise PartialResolutionError (f'No handler found for key "{ component } "' )
470-
471-
472- @deprecated ("migrated to fastapi" )
473- class Partials (delegate .page ):
474- path = r'/partials/([A-Za-z]+)'
475- encoding = 'json'
476-
477- def GET (self , component ):
478- return delegate .RawText (
479- json .dumps (PartialRequestResolver .resolve (component )),
480- content_type = 'application/json' ,
481- )
482-
483-
484352def setup ():
485353 pass
0 commit comments