madmachinations / home-assistant-alexa-shopping-list

A custom component for Home Assistant which synchronises your Alexa Shopping List
GNU General Public License v3.0
30 stars 3 forks source link

Can't seem to get more than 13 items #22

Open alexiri opened 2 weeks ago

alexiri commented 2 weeks ago

It seems that if the Alexa list has more than 13 elements, the ones at the bottom aren't listed by get_alexa_list. Is this happening to anybody else?

madmachinations commented 2 weeks ago

Can I ask what region you have it set to? I have had lists longer than 13 I'm sure, but I will keep an eye out over the next week and look specifically as I've never counted

alexiri commented 2 weeks ago

I have it set to .es.

madmachinations commented 2 weeks ago

Have you consistently seen it not go beyond 13 items? Even if you replaced all items in the list with say 14 totally different ones?

alexiri commented 2 weeks ago

Yes, I just tried it now:

2024-10-12 18:27:57.920 INFO server - <module>: Alexa list: ['15', '14', '13', '12', '11', '10', '9', '8', '7', '6', '5', '4', '3']
2024-10-12 18:27:57.921 INFO server - <module>: 13 items in Alexa list

image

The HTML code also shows only 13 divs, I guess they're getting replaced when you scoll:

image

madmachinations commented 2 weeks ago

Interesting, thanks very much for confirming it.

I assume that perhaps the shopping list is being lazy loaded, and the markup maybe changes between blocks of 13? But that's a total guess until I actually look into it.

In the mean time I will mark this as verified and take a look at it in the coming days

alexiri commented 2 weeks ago

This kind of works:

@@ -237,15 +237,25 @@ class AlexaShoppingList:
     def get_alexa_list(self, refresh: bool = True):
         self._ensure_driver_is_on_alexa_list(refresh)
         time.sleep(5)

         list_container = self.driver.find_element(By.CLASS_NAME, 'virtual-list')
-        list_items = list_container.find_elements(By.CLASS_NAME, 'item-title')

         found = []
-        for item in list_items:
-            found.append(item.get_attribute('innerText'))
+        last = None
+        while True:
+            list_items = list_container.find_elements(By.CLASS_NAME, 'item-title')
+            # print(f"Found {len(list_items)} items: {list_items[0].get_attribute('innerText')} to {list_items[-1].get_attribute('innerText')}")
+            for item in list_items:
+                if item.get_attribute('innerText') not in found:
+                    found.append(item.get_attribute('innerText'))
+
+            if last == list_items[-1]:
+                break
+            last = list_items[-1]
+            # print(f"Scrolling to {last.get_attribute('innerText')}")
+            self.driver.execute_script("arguments[0].scrollIntoView();", last)

         return found
2024-10-12 19:06:30.443 INFO server - <module>: Logged in successfully
Found 13 items: 15 to 3
Scrolling to 3
Found 12 items: 12 to 1
Scrolling to 1
Found 12 items: 12 to 1
2024-10-12 19:06:36.346 INFO server - <module>: Alexa list: ['15', '14', '13', '12', '11', '10', '9', '8', '7', '6', '5', '4', '3', '2', '1']
2024-10-12 19:06:36.346 INFO server - <module>: 15 items in Alexa list

I'm not submitting it as a PR because I haven't tested it a lot (I already know it doesn't work for refresh=False) and I'm not super happy with it. Maybe there's a better way of doing this.