prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Spider jimmy-johns is broken
  During the global build at 2021-09-29-14-42-48, spider **jimmy-johns** failed with **0 features** and **1544 errors**.
  
  Here's [the log](https://data.alltheplaces.xyz/runs/2021-09-29-14-42-48/logs/jimmy-johns.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-09-29-14-42-48/output/jimmy-johns.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-09-29-14-42-48/output/jimmy-johns.geojson))
  
  </issue>
  <code>
  [start of locations/spiders/jimmy_johns.py]
  1 # -*- coding: utf-8 -*-
  2 import scrapy
  3 import json
  4 
  5 from locations.items import GeojsonPointItem
  6 
  7 STATES = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "DE", "FL", "GA",
  8           "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
  9           "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
  10           "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
  11           "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]
  12 HEADERS = { 'Content-Type': 'application/json' }
  13 JJBASE = 'https://www.jimmyjohns.com/webservices/Location/LocationServiceHandler.asmx/{}'
  14 CITIES = JJBASE.format('GetCitiesByStateNameAbbreviation')
  15 STORES = JJBASE.format('GetStoreAddressesByCityAndState')
  16 
  17 class JimmyJohnsSpider(scrapy.Spider):
  18     name = "jimmy-johns"
  19     item_attributes = { 'brand': "Jimmy John's", 'brand_wikidata': "Q1689380" }
  20     allowed_domains = ["www.jimmyjohns.com"]
  21     download_delay = 0.2
  22 
  23     def start_requests(self):
  24         for state in STATES:
  25             current_state = json.dumps({ 'state': state })
  26             request = scrapy.Request(
  27                 CITIES,
  28                 method='POST',
  29                 body=current_state,
  30                 headers=HEADERS,
  31                 callback=self.parse_cities
  32             )
  33             request.meta['state'] = state
  34             yield request
  35 
  36     def parse_cities(self, response):
  37         cities = json.loads(response.body)
  38         for city in cities['d']:
  39             current_city = json.dumps({ 'state': response.meta['state'], 'city': city })
  40             request = scrapy.Request(
  41                 STORES,
  42                 method='POST',
  43                 body=current_city,
  44                 headers=HEADERS,
  45                 callback=self.parse
  46             )
  47             yield request
  48 
  49     def parse(self, response):
  50         stores = json.loads(response.body)
  51         for store in stores['d']:
  52             full = '{}, {}, {} {}'.format(store['address'], store['city'], store['state'], store['postalcode'])
  53             yield GeojsonPointItem(
  54                 name=store['storename'],
  55                 addr_full=full,
  56                 opening_hours=store['hours'],
  57                 phone=store['telephone'],
  58                 ref=store['storeid'],
  59                 lon=float(store['lng']),
  60                 lat=float(store['lat']),
  61             )
  62
  [end of locations/spiders/jimmy_johns.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/jimmy_johns.py b/locations/spiders/jimmy_johns.py
  --- a/locations/spiders/jimmy_johns.py
  +++ b/locations/spiders/jimmy_johns.py
  @@ -1,61 +1,36 @@
   # -*- coding: utf-8 -*-
  +from os import stat
   import scrapy
  +from urllib import parse
   import json
  -
   from locations.items import GeojsonPointItem
   
  -STATES = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "DE", "FL", "GA",
  -          "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
  -          "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
  -          "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
  -          "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]
  -HEADERS = { 'Content-Type': 'application/json' }
  -JJBASE = 'https://www.jimmyjohns.com/webservices/Location/LocationServiceHandler.asmx/{}'
  -CITIES = JJBASE.format('GetCitiesByStateNameAbbreviation')
  -STORES = JJBASE.format('GetStoreAddressesByCityAndState')
  +class TemplateSpider(scrapy.Spider):
  +    name = "jimmy_johns"
  +    allowed_domains = ["locations.jimmyjohns.com"]
  +    start_urls = (
  +        'https://locations.jimmyjohns.com/sitemap.xml',
  +    )
   
  -class JimmyJohnsSpider(scrapy.Spider):
  -    name = "jimmy-johns"
  -    item_attributes = { 'brand': "Jimmy John's", 'brand_wikidata': "Q1689380" }
  -    allowed_domains = ["www.jimmyjohns.com"]
  -    download_delay = 0.2
  +    def parse(self, response):
  +        stores = response.xpath('//url/loc[contains(text(),"sandwiches")]/text()').extract()
  +        for store in stores:
  +            yield scrapy.Request(response.urljoin(store), callback=self.parse_store)
   
  -    def start_requests(self):
  -        for state in STATES:
  -            current_state = json.dumps({ 'state': state })
  -            request = scrapy.Request(
  -                CITIES,
  -                method='POST',
  -                body=current_state,
  -                headers=HEADERS,
  -                callback=self.parse_cities
  -            )
  -            request.meta['state'] = state
  -            yield request
  +    def parse_store(self, response):
  +        data = json.loads(response.xpath('//script[@type="application/ld+json"]//text()').extract_first())
   
  -    def parse_cities(self, response):
  -        cities = json.loads(response.body)
  -        for city in cities['d']:
  -            current_city = json.dumps({ 'state': response.meta['state'], 'city': city })
  -            request = scrapy.Request(
  -                STORES,
  -                method='POST',
  -                body=current_city,
  -                headers=HEADERS,
  -                callback=self.parse
  -            )
  -            yield request
  +        properties = {
  +            'ref': data[0]['url'],
  +            'addr_full': data[0]['address']['streetAddress'],
  +            'city': data[0]['address']['addressLocality'],
  +            'state': data[0]['address']['addressRegion'],
  +            'postcode': data[0]['address']['postalCode'],
  +            'website': response.url,
  +            'lat': data[0]['geo']['latitude'],
  +            'lon': data[0]['geo']['longitude'],
  +        }
  +        if data[0]['address']['telephone']:
  +            properties['phone'] = data[0]['address']['telephone']
   
  -    def parse(self, response):
  -        stores = json.loads(response.body)
  -        for store in stores['d']:
  -            full = '{}, {}, {} {}'.format(store['address'], store['city'], store['state'], store['postalcode'])
  -            yield GeojsonPointItem(
  -                name=store['storename'],
  -                addr_full=full,
  -                opening_hours=store['hours'],
  -                phone=store['telephone'],
  -                ref=store['storeid'],
  -                lon=float(store['lng']),
  -                lat=float(store['lat']),
  -            )
  +        yield GeojsonPointItem(**properties)
  
verification_info: {"golden_diff": "diff --git a/locations/spiders/jimmy_johns.py b/locations/spiders/jimmy_johns.py\n--- a/locations/spiders/jimmy_johns.py\n+++ b/locations/spiders/jimmy_johns.py\n@@ -1,61 +1,36 @@\n # -*- coding: utf-8 -*-\n+from os import stat\n import scrapy\n+from urllib import parse\n import json\n-\n from locations.items import GeojsonPointItem\n \n-STATES = [\"AL\", \"AK\", \"AZ\", \"AR\", \"CA\", \"CO\", \"CT\", \"DC\", \"DE\", \"FL\", \"GA\",\n-          \"HI\", \"ID\", \"IL\", \"IN\", \"IA\", \"KS\", \"KY\", \"LA\", \"ME\", \"MD\",\n-          \"MA\", \"MI\", \"MN\", \"MS\", \"MO\", \"MT\", \"NE\", \"NV\", \"NH\", \"NJ\",\n-          \"NM\", \"NY\", \"NC\", \"ND\", \"OH\", \"OK\", \"OR\", \"PA\", \"RI\", \"SC\",\n-          \"SD\", \"TN\", \"TX\", \"UT\", \"VT\", \"VA\", \"WA\", \"WV\", \"WI\", \"WY\"]\n-HEADERS = { 'Content-Type': 'application/json' }\n-JJBASE = 'https://www.jimmyjohns.com/webservices/Location/LocationServiceHandler.asmx/{}'\n-CITIES = JJBASE.format('GetCitiesByStateNameAbbreviation')\n-STORES = JJBASE.format('GetStoreAddressesByCityAndState')\n+class TemplateSpider(scrapy.Spider):\n+    name = \"jimmy_johns\"\n+    allowed_domains = [\"locations.jimmyjohns.com\"]\n+    start_urls = (\n+        'https://locations.jimmyjohns.com/sitemap.xml',\n+    )\n \n-class JimmyJohnsSpider(scrapy.Spider):\n-    name = \"jimmy-johns\"\n-    item_attributes = { 'brand': \"Jimmy John's\", 'brand_wikidata': \"Q1689380\" }\n-    allowed_domains = [\"www.jimmyjohns.com\"]\n-    download_delay = 0.2\n+    def parse(self, response):\n+        stores = response.xpath('//url/loc[contains(text(),\"sandwiches\")]/text()').extract()\n+        for store in stores:\n+            yield scrapy.Request(response.urljoin(store), callback=self.parse_store)\n \n-    def start_requests(self):\n-        for state in STATES:\n-            current_state = json.dumps({ 'state': state })\n-            request = scrapy.Request(\n-                CITIES,\n-                method='POST',\n-                body=current_state,\n-                headers=HEADERS,\n-                callback=self.parse_cities\n-            )\n-            request.meta['state'] = state\n-            yield request\n+    def parse_store(self, response):\n+        data = json.loads(response.xpath('//script[@type=\"application/ld+json\"]//text()').extract_first())\n \n-    def parse_cities(self, response):\n-        cities = json.loads(response.body)\n-        for city in cities['d']:\n-            current_city = json.dumps({ 'state': response.meta['state'], 'city': city })\n-            request = scrapy.Request(\n-                STORES,\n-                method='POST',\n-                body=current_city,\n-                headers=HEADERS,\n-                callback=self.parse\n-            )\n-            yield request\n+        properties = {\n+            'ref': data[0]['url'],\n+            'addr_full': data[0]['address']['streetAddress'],\n+            'city': data[0]['address']['addressLocality'],\n+            'state': data[0]['address']['addressRegion'],\n+            'postcode': data[0]['address']['postalCode'],\n+            'website': response.url,\n+            'lat': data[0]['geo']['latitude'],\n+            'lon': data[0]['geo']['longitude'],\n+        }\n+        if data[0]['address']['telephone']:\n+            properties['phone'] = data[0]['address']['telephone']\n \n-    def parse(self, response):\n-        stores = json.loads(response.body)\n-        for store in stores['d']:\n-            full = '{}, {}, {} {}'.format(store['address'], store['city'], store['state'], store['postalcode'])\n-            yield GeojsonPointItem(\n-                name=store['storename'],\n-                addr_full=full,\n-                opening_hours=store['hours'],\n-                phone=store['telephone'],\n-                ref=store['storeid'],\n-                lon=float(store['lng']),\n-                lat=float(store['lat']),\n-            )\n+        yield GeojsonPointItem(**properties)\n", "issue": "Spider jimmy-johns is broken\nDuring the global build at 2021-09-29-14-42-48, spider **jimmy-johns** failed with **0 features** and **1544 errors**.\n\nHere's [the log](https://data.alltheplaces.xyz/runs/2021-09-29-14-42-48/logs/jimmy-johns.txt) and [the output](https://data.alltheplaces.xyz/runs/2021-09-29-14-42-48/output/jimmy-johns.geojson) ([on a map](https://data.alltheplaces.xyz/map.html?show=https://data.alltheplaces.xyz/runs/2021-09-29-14-42-48/output/jimmy-johns.geojson))\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nimport scrapy\nimport json\n\nfrom locations.items import GeojsonPointItem\n\nSTATES = [\"AL\", \"AK\", \"AZ\", \"AR\", \"CA\", \"CO\", \"CT\", \"DC\", \"DE\", \"FL\", \"GA\",\n          \"HI\", \"ID\", \"IL\", \"IN\", \"IA\", \"KS\", \"KY\", \"LA\", \"ME\", \"MD\",\n          \"MA\", \"MI\", \"MN\", \"MS\", \"MO\", \"MT\", \"NE\", \"NV\", \"NH\", \"NJ\",\n          \"NM\", \"NY\", \"NC\", \"ND\", \"OH\", \"OK\", \"OR\", \"PA\", \"RI\", \"SC\",\n          \"SD\", \"TN\", \"TX\", \"UT\", \"VT\", \"VA\", \"WA\", \"WV\", \"WI\", \"WY\"]\nHEADERS = { 'Content-Type': 'application/json' }\nJJBASE = 'https://www.jimmyjohns.com/webservices/Location/LocationServiceHandler.asmx/{}'\nCITIES = JJBASE.format('GetCitiesByStateNameAbbreviation')\nSTORES = JJBASE.format('GetStoreAddressesByCityAndState')\n\nclass JimmyJohnsSpider(scrapy.Spider):\n    name = \"jimmy-johns\"\n    item_attributes = { 'brand': \"Jimmy John's\", 'brand_wikidata': \"Q1689380\" }\n    allowed_domains = [\"www.jimmyjohns.com\"]\n    download_delay = 0.2\n\n    def start_requests(self):\n        for state in STATES:\n            current_state = json.dumps({ 'state': state })\n            request = scrapy.Request(\n                CITIES,\n                method='POST',\n                body=current_state,\n                headers=HEADERS,\n                callback=self.parse_cities\n            )\n            request.meta['state'] = state\n            yield request\n\n    def parse_cities(self, response):\n        cities = json.loads(response.body)\n        for city in cities['d']:\n            current_city = json.dumps({ 'state': response.meta['state'], 'city': city })\n            request = scrapy.Request(\n                STORES,\n                method='POST',\n                body=current_city,\n                headers=HEADERS,\n                callback=self.parse\n            )\n            yield request\n\n    def parse(self, response):\n        stores = json.loads(response.body)\n        for store in stores['d']:\n            full = '{}, {}, {} {}'.format(store['address'], store['city'], store['state'], store['postalcode'])\n            yield GeojsonPointItem(\n                name=store['storename'],\n                addr_full=full,\n                opening_hours=store['hours'],\n                phone=store['telephone'],\n                ref=store['storeid'],\n                lon=float(store['lng']),\n                lat=float(store['lat']),\n            )\n", "path": "locations/spiders/jimmy_johns.py"}]}