Repository: internetarchive/openlibrary

Issue:
Make BWB + Amz prices lazy load on editions page
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

### Next Steps

* [ ] Decide on approach (e.g. partials + js to fetch prices?) @jimchamp 
* [ ] Needs breakdown on which files to edit
* [ ] Needs instructions for how to test -- for now if BWB doesn't return back data, have the partial endpoint return static/fake data. Ignore amz for now.

### Design

Patron should see the affiliate/vendors table but show static "tomb stone" grey placeholders until the amz + bwb data becomes available.


### Describe the problem that you'd like solved
<!-- A clear and concise description of what you want to happen. -->

**Justification:** Currently BWB + amazon are expensive network calls which result in slow books page load times in sentry

We want to load this data async to speed up the perf.

### Proposal & Constraints
<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->

<!-- Which suggestions or requirements should be considered for how feature needs to appear or be implemented? -->

### Additional context
<!-- Add any other context or screenshots about the feature request here. -->

### Stakeholders
<!-- @ tag stakeholders of this bug -->





Discussion:
can i work on this?

@mekarpeles Please assign me this issue.

@nick2432 I've assigned you to work on this (sorry for the delay).

Here's some more information about this:

The template for our affiliate links is [here](https://github.com/internetarchive/openlibrary/blob/master/openlibrary/macros/AffiliateLinks.html).  I don't think that this file will need to be modified at all, but it will need to be rendered server-side and returned to the client.

The template for our book page is [here](https://github.com/internetarchive/openlibrary/blob/e7404e7f15df39fdc11a0e45f86be3988540f102/openlibrary/templates/type/edition/view.html).  Currently, we store the links as [`affiliate_links`](https://github.com/internetarchive/openlibrary/blob/e7404e7f15df39fdc11a0e45f86be3988540f102/openlibrary/templates/type/edition/view.html#L195) and render this value twice on the book page.  This will need to be updated to instead store the HTML for the "tomb stone" placeholder.  You can define a new [template function](https://webpy.org/docs/0.3/templetor#def) that takes `isbn`, `asin`, `prices`, and `edition_key` as parameters and returns a placeholder element that includes data attributes for each parameter.  These values will be used to fetch the HTML for the `AffiliateLinks` macro.

You'll have to create a new endpoint which will be used to fetch the rendered `AffiliateLinks`.  This can be added to [this file](https://github.com/internetarchive/openlibrary/blob/e7404e7f15df39fdc11a0e45f86be3988540f102/openlibrary/plugins/openlibrary/api.py) for now.  You can model the `GET` handler on [this handler](https://github.com/internetarchive/openlibrary/blob/2cc5f78240380952242889e078cd491036d18235/openlibrary/plugins/upstream/checkins.py#L268-L278), which returns partials for another feature.  Since `AffiliateLinks.html` is macro, the `render_template` call will need to be replaced with something like `macro.macrostore['AffiliateLinks'](doc, opts)`, where doc is the edition, and `opts` is a `dict` containing `asin`, `isbn`, and `prices`.  You can get a reference to the edition by calling `web.ctx.site.get(edition_key)`.

The path for the new endpoint can be something like `/affiliate-links/partials`.

Finally, you'll need to replace the placeholder with the HTML for the affiliate links.  Make sure to only fetch the links once per book page, and replace both placeholders with the results.

Please assign me to this issue

@jimchamp Please assign me this issue on a first come basis I am the one whom you assign sir.

@tusharv01 happy to assign you, before starting work, can you please post a comment here describing how you will implement the solution and make sure you tag @jimchamp so he can review your approach. Thank you!
@mekarpeles @jimchamp 
Proposed Implementation Approach:

# 1. Update AffiliateLinks Template:
#   - Ensure the AffiliateLinks.html template includes necessary placeholders for BWB and Amazon prices.

# 2. Update Book Page Template:
#   - Replace the current rendering of 'affiliate_links' with a placeholder element.
#   - Create a new template function, render_affiliate_links_placeholder, to generate the placeholder.

# 3. Create a New Endpoint for AffiliateLinks:
#   - Add a new endpoint '/affiliate-links/partials' to fetch rendered AffiliateLinks.
#   - Model the GET handler on an existing handler, using the macro.macrostore['AffiliateLinks'](doc, opts) syntax.

# 4. Replace Placeholders with HTML:
#   - In the JavaScript part of book_page_template.html, fetch affiliate links using the new endpoint.
#   - Replace placeholders with the fetched HTML.
@tusharv01 are you still working on this issue?

> @tusharv01 are you still working on this issue?
> 

Yes
@tusharv01, this issue is rising in priority, and is something that we'd like finished relatively soon.  When do you expect to open a PR with your solution?

**Edit:** Just noticed that you don't have the repo forked, so I'm assuming that you are not working on this.  Assigning myself.

Resolving patch:
diff --git a/openlibrary/plugins/openlibrary/code.py b/openlibrary/plugins/openlibrary/code.py
--- a/openlibrary/plugins/openlibrary/code.py
+++ b/openlibrary/plugins/openlibrary/code.py
@@ -1020,11 +1020,23 @@ class Partials(delegate.page):
     encoding = 'json'
 
     def GET(self):
-        i = web.input(workid=None, _component=None)
+        # `data` is meant to be a dict with two keys: `args` and `kwargs`.
+        # `data['args']` is meant to be a list of a template's positional arguments, in order.
+        # `data['kwargs']` is meant to be a dict containing a template's keyword arguments.
+        i = web.input(workid=None, _component=None, data=None)
         component = i.pop("_component")
         partial = {}
         if component == "RelatedWorkCarousel":
             partial = _get_relatedcarousels_component(i.workid)
+        elif component == "AffiliateLinks":
+            data = json.loads(i.data)
+            args = data.get('args', [])
+            # XXX : Throw error if args length is less than 2
+            macro = web.template.Template.globals['macros'].AffiliateLinks(
+                args[0], args[1]
+            )
+            partial = {"partials": str(macro)}
+
         return delegate.RawText(json.dumps(partial))
 
 


Test patch: