prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Spider lenscrafters is broken
  During the global build at 2021-08-25-14-42-15, spider **lenscrafters** failed with **0 features** and **1 errors**.
  
  Here's [the log](https://data.alltheplaces.xyz/runs/2021-08-25-14-42-15/logs/lenscrafters.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-08-25-14-42-15/output/lenscrafters.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-08-25-14-42-15/output/lenscrafters.geojson))
  
  </issue>
  <code>
  [start of locations/spiders/lenscrafters.py]
  1 # -*- coding: utf-8 -*-
  2 import json
  3 import re
  4 
  5 import scrapy
  6 
  7 from locations.items import GeojsonPointItem
  8 from locations.hours import OpeningHours
  9 
  10 
  11 class LensCraftersSpider(scrapy.Spider):
  12     name = "lenscrafters"
  13     item_attributes = { 'brand': "Lenscrafters" }
  14     allowed_domains = ['local.lenscrafters.com']
  15     start_urls = [
  16         'https://local.lenscrafters.com/'
  17     ]
  18 
  19     def parse_hours(self, hours):
  20         opening_hours = OpeningHours()
  21         for group in hours:
  22             if "Closed" in group:
  23                 pass
  24             else:
  25                 days, open_time, close_time = re.search(r'([a-zA-Z,]+)\s([\d:]+)-([\d:]+)', group).groups()
  26                 days = days.split(',')
  27                 for day in days:
  28                     opening_hours.add_range(day=day, open_time=open_time, close_time=close_time, time_format='%H:%M')
  29 
  30         return opening_hours.as_opening_hours()
  31 
  32     def parse(self, response):
  33         urls = response.xpath(
  34             '//a[@class="c-directory-list-content-item-link" or @class="c-location-grid-item-link"]/@href').extract()
  35         # If cannot find 'c-directory-list-content-item-link' or 'c-location-grid-item-link' then this is a store page
  36         if len(urls) == 0:
  37             properties = {
  38                 'name': response.xpath('//*[@class="location-name h1-normal"]/text()').extract_first(),
  39                 'addr_full': response.xpath('//*[@class="c-address-street-1"]/text()').extract_first(),
  40                 'city': response.xpath('//*[@class="c-address-city"]/text()').extract_first(),
  41                 'state': response.xpath('//*[@class="c-address-state"]/text()').extract_first(),
  42                 'postcode': response.xpath('//*[@class="c-address-postal-code"]/text()').extract_first(),
  43                 'phone': response.xpath('//*[@id="phone-main"]/text()').extract_first(),
  44                 'ref': "_".join(re.search(r".+/(.+?)/(.+?)/(.+?)/?(?:\.html|$)", response.url).groups()),
  45                 'website': response.url,
  46                 'lat': response.xpath('//*[@itemprop="latitude"]/@content').extract_first(),
  47                 'lon': response.xpath('//*[@itemprop="longitude"]/@content').extract_first(),
  48             }
  49 
  50             hours = self.parse_hours(response.xpath('//*[@itemprop="openingHours"]/@content').extract())
  51             if hours:
  52                 properties["opening_hours"] = hours
  53 
  54             yield GeojsonPointItem(**properties)
  55         else:
  56             for path in urls:
  57                 yield scrapy.Request(url=response.urljoin(path), callback=self.parse)
  58
  [end of locations/spiders/lenscrafters.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/lenscrafters.py b/locations/spiders/lenscrafters.py
  --- a/locations/spiders/lenscrafters.py
  +++ b/locations/spiders/lenscrafters.py
  @@ -10,7 +10,7 @@
   
   class LensCraftersSpider(scrapy.Spider):
       name = "lenscrafters"
  -    item_attributes = { 'brand': "Lenscrafters" }
  +    item_attributes = {'brand': "Lenscrafters"}
       allowed_domains = ['local.lenscrafters.com']
       start_urls = [
           'https://local.lenscrafters.com/'
  @@ -30,21 +30,21 @@
           return opening_hours.as_opening_hours()
   
       def parse(self, response):
  -        urls = response.xpath(
  -            '//a[@class="c-directory-list-content-item-link" or @class="c-location-grid-item-link"]/@href').extract()
  -        # If cannot find 'c-directory-list-content-item-link' or 'c-location-grid-item-link' then this is a store page
  +        urls = response.xpath('//a[@class="Directory-listLink Link--directory"]/@href').extract()
  +
  +        # If cannot find 'Directory-listLink Link--directory' then this is a store page
           if len(urls) == 0:
               properties = {
  -                'name': response.xpath('//*[@class="location-name h1-normal"]/text()').extract_first(),
  -                'addr_full': response.xpath('//*[@class="c-address-street-1"]/text()').extract_first(),
  -                'city': response.xpath('//*[@class="c-address-city"]/text()').extract_first(),
  -                'state': response.xpath('//*[@class="c-address-state"]/text()').extract_first(),
  -                'postcode': response.xpath('//*[@class="c-address-postal-code"]/text()').extract_first(),
  -                'phone': response.xpath('//*[@id="phone-main"]/text()').extract_first(),
  -                'ref': "_".join(re.search(r".+/(.+?)/(.+?)/(.+?)/?(?:\.html|$)", response.url).groups()),
  -                'website': response.url,
  -                'lat': response.xpath('//*[@itemprop="latitude"]/@content').extract_first(),
  -                'lon': response.xpath('//*[@itemprop="longitude"]/@content').extract_first(),
  +                'name': response.xpath('//h1[@id="location-name"]/text()').extract_first(),
  +                'addr_full': response.xpath('//span[@class="c-address-street-1"]/text()').extract_first(),
  +                'city': response.xpath('//span[@class="c-address-city"]/text()').extract_first(),
  +                'state': response.xpath('//abbr[@class="c-address-state"]/text()').extract_first(),
  +                'postcode': response.xpath('//span[@class="c-address-postal-code"]/text()').extract_first(),
  +                'phone': response.xpath('//div[@id="phone-main"]/text()').extract_first(),
  +                'ref': response.xpath('//link[@rel="canonical"]/@href').extract_first(),
  +                'website': response.xpath('//link[@rel="canonical"]/@href').extract_first(),
  +                'lat': response.xpath('//meta[@itemprop="latitude"]/@content').extract_first(),
  +                'lon': response.xpath('//meta[@itemprop="longitude"]/@content').extract_first(),
               }
   
               hours = self.parse_hours(response.xpath('//*[@itemprop="openingHours"]/@content').extract())
  
verification_info: {"golden_diff": "diff --git a/locations/spiders/lenscrafters.py b/locations/spiders/lenscrafters.py\n--- a/locations/spiders/lenscrafters.py\n+++ b/locations/spiders/lenscrafters.py\n@@ -10,7 +10,7 @@\n \n class LensCraftersSpider(scrapy.Spider):\n     name = \"lenscrafters\"\n-    item_attributes = { 'brand': \"Lenscrafters\" }\n+    item_attributes = {'brand': \"Lenscrafters\"}\n     allowed_domains = ['local.lenscrafters.com']\n     start_urls = [\n         'https://local.lenscrafters.com/'\n@@ -30,21 +30,21 @@\n         return opening_hours.as_opening_hours()\n \n     def parse(self, response):\n-        urls = response.xpath(\n-            '//a[@class=\"c-directory-list-content-item-link\" or @class=\"c-location-grid-item-link\"]/@href').extract()\n-        # If cannot find 'c-directory-list-content-item-link' or 'c-location-grid-item-link' then this is a store page\n+        urls = response.xpath('//a[@class=\"Directory-listLink Link--directory\"]/@href').extract()\n+\n+        # If cannot find 'Directory-listLink Link--directory' then this is a store page\n         if len(urls) == 0:\n             properties = {\n-                'name': response.xpath('//*[@class=\"location-name h1-normal\"]/text()').extract_first(),\n-                'addr_full': response.xpath('//*[@class=\"c-address-street-1\"]/text()').extract_first(),\n-                'city': response.xpath('//*[@class=\"c-address-city\"]/text()').extract_first(),\n-                'state': response.xpath('//*[@class=\"c-address-state\"]/text()').extract_first(),\n-                'postcode': response.xpath('//*[@class=\"c-address-postal-code\"]/text()').extract_first(),\n-                'phone': response.xpath('//*[@id=\"phone-main\"]/text()').extract_first(),\n-                'ref': \"_\".join(re.search(r\".+/(.+?)/(.+?)/(.+?)/?(?:\\.html|$)\", response.url).groups()),\n-                'website': response.url,\n-                'lat': response.xpath('//*[@itemprop=\"latitude\"]/@content').extract_first(),\n-                'lon': response.xpath('//*[@itemprop=\"longitude\"]/@content').extract_first(),\n+                'name': response.xpath('//h1[@id=\"location-name\"]/text()').extract_first(),\n+                'addr_full': response.xpath('//span[@class=\"c-address-street-1\"]/text()').extract_first(),\n+                'city': response.xpath('//span[@class=\"c-address-city\"]/text()').extract_first(),\n+                'state': response.xpath('//abbr[@class=\"c-address-state\"]/text()').extract_first(),\n+                'postcode': response.xpath('//span[@class=\"c-address-postal-code\"]/text()').extract_first(),\n+                'phone': response.xpath('//div[@id=\"phone-main\"]/text()').extract_first(),\n+                'ref': response.xpath('//link[@rel=\"canonical\"]/@href').extract_first(),\n+                'website': response.xpath('//link[@rel=\"canonical\"]/@href').extract_first(),\n+                'lat': response.xpath('//meta[@itemprop=\"latitude\"]/@content').extract_first(),\n+                'lon': response.xpath('//meta[@itemprop=\"longitude\"]/@content').extract_first(),\n             }\n \n             hours = self.parse_hours(response.xpath('//*[@itemprop=\"openingHours\"]/@content').extract())\n", "issue": "Spider lenscrafters is broken\nDuring the global build at 2021-08-25-14-42-15, spider **lenscrafters** failed with **0 features** and **1 errors**.\n\nHere's [the log](https://data.alltheplaces.xyz/runs/2021-08-25-14-42-15/logs/lenscrafters.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-08-25-14-42-15/output/lenscrafters.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-08-25-14-42-15/output/lenscrafters.geojson))\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nimport json\nimport re\n\nimport scrapy\n\nfrom locations.items import GeojsonPointItem\nfrom locations.hours import OpeningHours\n\n\nclass LensCraftersSpider(scrapy.Spider):\n    name = \"lenscrafters\"\n    item_attributes = { 'brand': \"Lenscrafters\" }\n    allowed_domains = ['local.lenscrafters.com']\n    start_urls = [\n        'https://local.lenscrafters.com/'\n    ]\n\n    def parse_hours(self, hours):\n        opening_hours = OpeningHours()\n        for group in hours:\n            if \"Closed\" in group:\n                pass\n            else:\n                days, open_time, close_time = re.search(r'([a-zA-Z,]+)\\s([\\d:]+)-([\\d:]+)', group).groups()\n                days = days.split(',')\n                for day in days:\n                    opening_hours.add_range(day=day, open_time=open_time, close_time=close_time, time_format='%H:%M')\n\n        return opening_hours.as_opening_hours()\n\n    def parse(self, response):\n        urls = response.xpath(\n            '//a[@class=\"c-directory-list-content-item-link\" or @class=\"c-location-grid-item-link\"]/@href').extract()\n        # If cannot find 'c-directory-list-content-item-link' or 'c-location-grid-item-link' then this is a store page\n        if len(urls) == 0:\n            properties = {\n                'name': response.xpath('//*[@class=\"location-name h1-normal\"]/text()').extract_first(),\n                'addr_full': response.xpath('//*[@class=\"c-address-street-1\"]/text()').extract_first(),\n                'city': response.xpath('//*[@class=\"c-address-city\"]/text()').extract_first(),\n                'state': response.xpath('//*[@class=\"c-address-state\"]/text()').extract_first(),\n                'postcode': response.xpath('//*[@class=\"c-address-postal-code\"]/text()').extract_first(),\n                'phone': response.xpath('//*[@id=\"phone-main\"]/text()').extract_first(),\n                'ref': \"_\".join(re.search(r\".+/(.+?)/(.+?)/(.+?)/?(?:\\.html|$)\", response.url).groups()),\n                'website': response.url,\n                'lat': response.xpath('//*[@itemprop=\"latitude\"]/@content').extract_first(),\n                'lon': response.xpath('//*[@itemprop=\"longitude\"]/@content').extract_first(),\n            }\n\n            hours = self.parse_hours(response.xpath('//*[@itemprop=\"openingHours\"]/@content').extract())\n            if hours:\n                properties[\"opening_hours\"] = hours\n\n            yield GeojsonPointItem(**properties)\n        else:\n            for path in urls:\n                yield scrapy.Request(url=response.urljoin(path), callback=self.parse)\n", "path": "locations/spiders/lenscrafters.py"}]}