prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Spider lululemon is broken
  During the global build at 2021-06-02-14-42-40, spider **lululemon** failed with **0 features** and **0 errors**.
  
  Here's [the log](https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/logs/lululemon.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/output/lululemon.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/output/lululemon.geojson))
  
  </issue>
  <code>
  [start of locations/spiders/lululemon.py]
  1 # -*- coding: utf-8 -*-
  2 import json
  3 import scrapy
  4 
  5 from locations.items import GeojsonPointItem
  6 from locations.hours import OpeningHours
  7 
  8 WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
  9 
  10 
  11 class LuLuLemonSpider(scrapy.Spider):
  12     download_delay = 0.1
  13     name = "lululemon"
  14     item_attributes = {"brand": "LuLuLemon", "brand_wikidata": "Q6702957"}
  15     allowed_domains = ["shop.lululemon.com"]
  16     start_urls = ("https://shop.lululemon.com/stores/all-lululemon-stores",)
  17 
  18     def parse(self, response):
  19         urls = response.xpath('//a[@class="store-link basic"]/@href').extract()
  20         for path in urls:
  21             yield scrapy.Request(response.urljoin(path), callback=self.parse_store)
  22 
  23     def parse_store(self, response):
  24         address = {}
  25         geo = {}
  26         data = json.loads(
  27             response.xpath('//script[@type="application/ld+json"]/text()').extract_first()
  28         )
  29 
  30         ref = response.xpath('//h1[@class="lll-stack-lvl4"]/text()').extract_first()
  31 
  32         if data.get("address"):
  33             address["full"] = data["address"].get("streetAddress")
  34             address["zip"] = data["address"].get("postalCode")
  35             address["state"] = data["address"].get("addressRegion")
  36             address["city"] = data["address"].get("addressLocality")
  37             address["country"] = data["address"].get("addressCountry")
  38         if data.get("geo"):
  39             geo["lat"] = data["geo"].get("latitude")
  40             geo["lon"] = data["geo"].get("longitude")
  41         oh = self.parse_hours(data.get("openingHours"))
  42         if not oh:
  43             ref = "CLOSED - {}".format(ref)
  44 
  45         properties = {
  46             "addr_full": address.get("full"),
  47             "phone": data.get("telephone"),
  48             "city": address.get("city"),
  49             "state": address.get("state"),
  50             "postcode": address.get("zip"),
  51             "ref": ref,
  52             "website": response.url,
  53             "lat": geo.get("lat"),
  54             "lon": geo.get("lon"),
  55             "opening_hours": oh,
  56         }
  57         yield GeojsonPointItem(**properties)
  58 
  59     def parse_hours(self, hours):
  60         oh = OpeningHours()
  61 
  62         for h in hours:
  63             t = h.replace(" - ", "-")
  64             d, h = t.split()
  65             ot, ct = h.split("-")
  66             # Some stores are permanently closed, thus no time is defined
  67             if ot == "null" or ct == "null":
  68                 continue
  69             days = self.parse_days(d)
  70             for day in days:
  71                 oh.add_range(day=day, open_time=ot, close_time=ct, time_format="%H:%M")
  72 
  73         return oh.as_opening_hours()
  74 
  75     def parse_days(self, days):
  76         """Parse day ranges and returns a list of days it represent
  77         The following formats are considered:
  78           - Single day, e.g. "Mon", "Monday"
  79           - Range, e.g. "Mon-Fri", "Tue-Sund", "Sat-Sunday"
  80           - Two days, e.g. "Sat & Sun", "Friday & Su"
  81 
  82         Returns a list with the weekdays
  83         """
  84         # Produce a list of weekdays between two days e.g. su-sa, mo-th, etc.
  85         if "-" in days:
  86             d = days.split("-")
  87             r = [i.strip()[:2] for i in d]
  88             s = WEEKDAYS.index(r[0].title())
  89             e = WEEKDAYS.index(r[1].title())
  90             if s <= e:
  91                 return WEEKDAYS[s : e + 1]
  92             else:
  93                 return WEEKDAYS[s:] + WEEKDAYS[: e + 1]
  94         # Two days
  95         if "&" in days:
  96             d = days.split("&")
  97             return [i.strip()[:2].title() for i in d]
  98         # Single days
  99         else:
  100             return [days.strip()[:2].title()]
  101
  [end of locations/spiders/lululemon.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/locations/spiders/lululemon.py b/locations/spiders/lululemon.py
  --- a/locations/spiders/lululemon.py
  +++ b/locations/spiders/lululemon.py
  @@ -16,35 +16,39 @@
       start_urls = ("https://shop.lululemon.com/stores/all-lululemon-stores",)
   
       def parse(self, response):
  -        urls = response.xpath('//a[@class="store-link basic"]/@href').extract()
  +        urls = response.xpath('//a[@class="store-list_storeLink__3krLG"]/@href').extract()
           for path in urls:
               yield scrapy.Request(response.urljoin(path), callback=self.parse_store)
   
       def parse_store(self, response):
           address = {}
           geo = {}
  +        hours = {}
  +        status = "CLOSED"
           data = json.loads(
  -            response.xpath('//script[@type="application/ld+json"]/text()').extract_first()
  +            response.xpath('//script[@type="application/json"]/text()').extract_first()
           )
   
  -        ref = response.xpath('//h1[@class="lll-stack-lvl4"]/text()').extract_first()
  +        ref = data["props"]["pageProps"]["storeData"]["name"]
  +        address["full"] = data["props"]["pageProps"]["storeData"].get("fullAddress")
  +        address["zip"] = address["full"].split(",")[-1].strip()
  +        address["state"] = data["props"]["pageProps"]["storeData"].get("state")
  +        address["city"] = data["props"]["pageProps"]["storeData"].get("city")
  +        address["country"] = data["props"]["pageProps"]["storeData"].get("country")
  +        address["phone"] = data["props"]["pageProps"]["storeData"].get("phone")
  +        geo["lat"] = data["props"]["pageProps"]["storeData"].get("latitude")
  +        geo["lon"] = data["props"]["pageProps"]["storeData"].get("longitude")
  +        hours = data["props"]["pageProps"]["storeData"].get("hours")
  +        if data["props"]["pageProps"]["storeData"].get("status") == "active_soon":
  +            status = "Opening soon"
   
  -        if data.get("address"):
  -            address["full"] = data["address"].get("streetAddress")
  -            address["zip"] = data["address"].get("postalCode")
  -            address["state"] = data["address"].get("addressRegion")
  -            address["city"] = data["address"].get("addressLocality")
  -            address["country"] = data["address"].get("addressCountry")
  -        if data.get("geo"):
  -            geo["lat"] = data["geo"].get("latitude")
  -            geo["lon"] = data["geo"].get("longitude")
  -        oh = self.parse_hours(data.get("openingHours"))
  +        oh = self.parse_hours(hours)
           if not oh:
  -            ref = "CLOSED - {}".format(ref)
  +            ref = "{} - {}".format(status, ref)
   
           properties = {
               "addr_full": address.get("full"),
  -            "phone": data.get("telephone"),
  +            "phone": address.get("phone"),
               "city": address.get("city"),
               "state": address.get("state"),
               "postcode": address.get("zip"),
  @@ -60,11 +64,11 @@
           oh = OpeningHours()
   
           for h in hours:
  -            t = h.replace(" - ", "-")
  -            d, h = t.split()
  -            ot, ct = h.split("-")
  +            d = h.get("name")
  +            ot = h.get("openHour")
  +            ct = h.get("closeHour")
               # Some stores are permanently closed, thus no time is defined
  -            if ot == "null" or ct == "null":
  +            if not ot or not ct:
                   continue
               days = self.parse_days(d)
               for day in days:
  
verification_info: {"golden_diff": "diff --git a/locations/spiders/lululemon.py b/locations/spiders/lululemon.py\n--- a/locations/spiders/lululemon.py\n+++ b/locations/spiders/lululemon.py\n@@ -16,35 +16,39 @@\n     start_urls = (\"https://shop.lululemon.com/stores/all-lululemon-stores\",)\n \n     def parse(self, response):\n-        urls = response.xpath('//a[@class=\"store-link basic\"]/@href').extract()\n+        urls = response.xpath('//a[@class=\"store-list_storeLink__3krLG\"]/@href').extract()\n         for path in urls:\n             yield scrapy.Request(response.urljoin(path), callback=self.parse_store)\n \n     def parse_store(self, response):\n         address = {}\n         geo = {}\n+        hours = {}\n+        status = \"CLOSED\"\n         data = json.loads(\n-            response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first()\n+            response.xpath('//script[@type=\"application/json\"]/text()').extract_first()\n         )\n \n-        ref = response.xpath('//h1[@class=\"lll-stack-lvl4\"]/text()').extract_first()\n+        ref = data[\"props\"][\"pageProps\"][\"storeData\"][\"name\"]\n+        address[\"full\"] = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"fullAddress\")\n+        address[\"zip\"] = address[\"full\"].split(\",\")[-1].strip()\n+        address[\"state\"] = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"state\")\n+        address[\"city\"] = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"city\")\n+        address[\"country\"] = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"country\")\n+        address[\"phone\"] = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"phone\")\n+        geo[\"lat\"] = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"latitude\")\n+        geo[\"lon\"] = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"longitude\")\n+        hours = data[\"props\"][\"pageProps\"][\"storeData\"].get(\"hours\")\n+        if data[\"props\"][\"pageProps\"][\"storeData\"].get(\"status\") == \"active_soon\":\n+            status = \"Opening soon\"\n \n-        if data.get(\"address\"):\n-            address[\"full\"] = data[\"address\"].get(\"streetAddress\")\n-            address[\"zip\"] = data[\"address\"].get(\"postalCode\")\n-            address[\"state\"] = data[\"address\"].get(\"addressRegion\")\n-            address[\"city\"] = data[\"address\"].get(\"addressLocality\")\n-            address[\"country\"] = data[\"address\"].get(\"addressCountry\")\n-        if data.get(\"geo\"):\n-            geo[\"lat\"] = data[\"geo\"].get(\"latitude\")\n-            geo[\"lon\"] = data[\"geo\"].get(\"longitude\")\n-        oh = self.parse_hours(data.get(\"openingHours\"))\n+        oh = self.parse_hours(hours)\n         if not oh:\n-            ref = \"CLOSED - {}\".format(ref)\n+            ref = \"{} - {}\".format(status, ref)\n \n         properties = {\n             \"addr_full\": address.get(\"full\"),\n-            \"phone\": data.get(\"telephone\"),\n+            \"phone\": address.get(\"phone\"),\n             \"city\": address.get(\"city\"),\n             \"state\": address.get(\"state\"),\n             \"postcode\": address.get(\"zip\"),\n@@ -60,11 +64,11 @@\n         oh = OpeningHours()\n \n         for h in hours:\n-            t = h.replace(\" - \", \"-\")\n-            d, h = t.split()\n-            ot, ct = h.split(\"-\")\n+            d = h.get(\"name\")\n+            ot = h.get(\"openHour\")\n+            ct = h.get(\"closeHour\")\n             # Some stores are permanently closed, thus no time is defined\n-            if ot == \"null\" or ct == \"null\":\n+            if not ot or not ct:\n                 continue\n             days = self.parse_days(d)\n             for day in days:\n", "issue": "Spider lululemon is broken\nDuring the global build at 2021-06-02-14-42-40, spider **lululemon** failed with **0 features** and **0 errors**.\n\nHere's [the log](https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/logs/lululemon.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/output/lululemon.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-06-02-14-42-40/output/lululemon.geojson))\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nimport json\nimport scrapy\n\nfrom locations.items import GeojsonPointItem\nfrom locations.hours import OpeningHours\n\nWEEKDAYS = [\"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\", \"Su\"]\n\n\nclass LuLuLemonSpider(scrapy.Spider):\n    download_delay = 0.1\n    name = \"lululemon\"\n    item_attributes = {\"brand\": \"LuLuLemon\", \"brand_wikidata\": \"Q6702957\"}\n    allowed_domains = [\"shop.lululemon.com\"]\n    start_urls = (\"https://shop.lululemon.com/stores/all-lululemon-stores\",)\n\n    def parse(self, response):\n        urls = response.xpath('//a[@class=\"store-link basic\"]/@href').extract()\n        for path in urls:\n            yield scrapy.Request(response.urljoin(path), callback=self.parse_store)\n\n    def parse_store(self, response):\n        address = {}\n        geo = {}\n        data = json.loads(\n            response.xpath('//script[@type=\"application/ld+json\"]/text()').extract_first()\n        )\n\n        ref = response.xpath('//h1[@class=\"lll-stack-lvl4\"]/text()').extract_first()\n\n        if data.get(\"address\"):\n            address[\"full\"] = data[\"address\"].get(\"streetAddress\")\n            address[\"zip\"] = data[\"address\"].get(\"postalCode\")\n            address[\"state\"] = data[\"address\"].get(\"addressRegion\")\n            address[\"city\"] = data[\"address\"].get(\"addressLocality\")\n            address[\"country\"] = data[\"address\"].get(\"addressCountry\")\n        if data.get(\"geo\"):\n            geo[\"lat\"] = data[\"geo\"].get(\"latitude\")\n            geo[\"lon\"] = data[\"geo\"].get(\"longitude\")\n        oh = self.parse_hours(data.get(\"openingHours\"))\n        if not oh:\n            ref = \"CLOSED - {}\".format(ref)\n\n        properties = {\n            \"addr_full\": address.get(\"full\"),\n            \"phone\": data.get(\"telephone\"),\n            \"city\": address.get(\"city\"),\n            \"state\": address.get(\"state\"),\n            \"postcode\": address.get(\"zip\"),\n            \"ref\": ref,\n            \"website\": response.url,\n            \"lat\": geo.get(\"lat\"),\n            \"lon\": geo.get(\"lon\"),\n            \"opening_hours\": oh,\n        }\n        yield GeojsonPointItem(**properties)\n\n    def parse_hours(self, hours):\n        oh = OpeningHours()\n\n        for h in hours:\n            t = h.replace(\" - \", \"-\")\n            d, h = t.split()\n            ot, ct = h.split(\"-\")\n            # Some stores are permanently closed, thus no time is defined\n            if ot == \"null\" or ct == \"null\":\n                continue\n            days = self.parse_days(d)\n            for day in days:\n                oh.add_range(day=day, open_time=ot, close_time=ct, time_format=\"%H:%M\")\n\n        return oh.as_opening_hours()\n\n    def parse_days(self, days):\n        \"\"\"Parse day ranges and returns a list of days it represent\n        The following formats are considered:\n          - Single day, e.g. \"Mon\", \"Monday\"\n          - Range, e.g. \"Mon-Fri\", \"Tue-Sund\", \"Sat-Sunday\"\n          - Two days, e.g. \"Sat & Sun\", \"Friday & Su\"\n\n        Returns a list with the weekdays\n        \"\"\"\n        # Produce a list of weekdays between two days e.g. su-sa, mo-th, etc.\n        if \"-\" in days:\n            d = days.split(\"-\")\n            r = [i.strip()[:2] for i in d]\n            s = WEEKDAYS.index(r[0].title())\n            e = WEEKDAYS.index(r[1].title())\n            if s <= e:\n                return WEEKDAYS[s : e + 1]\n            else:\n                return WEEKDAYS[s:] + WEEKDAYS[: e + 1]\n        # Two days\n        if \"&\" in days:\n            d = days.split(\"&\")\n            return [i.strip()[:2].title() for i in d]\n        # Single days\n        else:\n            return [days.strip()[:2].title()]\n", "path": "locations/spiders/lululemon.py"}]}