internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: alltheplaces/alltheplaces

Issue:
Brookdale
https://www.brookdale.com/en/locations.html


Discussion:

Resolving patch:
diff --git a/locations/spiders/brookdale.py b/locations/spiders/brookdale.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/brookdale.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+import scrapy
+import json
+from locations.items import GeojsonPointItem
+
+URL = 'https://www.brookdale.com/bin/brookdale/community-search?care_type_category=resident&loc=&finrpt=&state='
+
+US_STATES = (
+       "AL", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA",
+       "ID", "IL", "IN", "IA", "KS", "KY", "LA", "MD",
+       "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
+       "NM", "NY", "NC", "OH", "OK", "OR", "PA", "RI", "SC",
+       "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY",
+)
+
+class TemplateSpider(scrapy.Spider):
+    name = "brookdale"
+    allowed_domains = ["www.brookdale.com"]
+
+    def start_requests(self):
+        for state in US_STATES:
+            url = ''.join([URL, state])
+            yield scrapy.Request(url, callback=self.parse_info)
+
+
+    def parse_info(self, response):
+
+        data = json.loads(response.body_as_unicode())
+
+        for row in data:
+
+            properties = {
+                "ref": row['community_id'],
+                "name": row['name'],
+                "lat": row['latitude'],
+                "lon": row['longitude'],
+                "addr_full": row['address1'],
+                "city": row['city'],
+                "state": row['state'],
+                "country": row['country_code'],
+                "postcode": row['zip_postal_code'],
+                "website": row['website'],
+                "phone": row['contact_center_phone'],
+            }
+
+            yield GeojsonPointItem(**properties)
\ No newline at end of file


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
BBVA Compass Bank
https://www.bbvacompass.com/USA


Discussion:

Resolving patch:
diff --git a/locations/spiders/bbvacompass.py b/locations/spiders/bbvacompass.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/bbvacompass.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+import html
+import json
+import re
+
+import scrapy
+
+from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
+
+
+class BbvaCompassSpider(scrapy.Spider):
+    name = "bbvacompass"
+    allowed_domains = ['bbvausa.com']
+    start_urls = [
+        'https://www.bbvausa.com/USA',
+    ]
+
+    def parse_hours(self, hours):
+        return ";".join(hours)
+
+    def parse_location(self, response):
+        data = json.loads(response.xpath('//script[@type="application/ld+json" and contains(text(), "streetAddress")]/text()').extract_first())
+
+        properties = {
+            'name': html.unescape(data["name"]),
+            'ref': "_".join(re.search(r".+/(.+?)/(.+?)/(.+?)/?(?:\.html|$)", response.url).groups()),
+            'addr_full': data["address"]["streetAddress"],
+            'city': data["address"]["addressLocality"],
+            'state': data["address"]["addressRegion"],
+            'postcode': data["address"]["postalCode"],
+            'country': data["address"].get("addressCountry"),
+            'phone': data.get("telephone"),
+            'website': data.get("url") or response.url,
+            'lat': float(data["geo"]["latitude"]),
+            'lon': float(data["geo"]["longitude"]),
+        }
+
+        hours = self.parse_hours(data.get("openingHours", []))
+        if hours:
+            properties["opening_hours"] = hours
+
+        yield GeojsonPointItem(**properties)
+
+    def parse(self, response):
+        urls = response.xpath('//div[contains(@class, "container-content")]//ul/li/a/@href').extract()
+
+        if urls:
+            for url in urls:
+                yield scrapy.Request(response.urljoin(url))
+
+        else:
+            urls = response.xpath('//div[@class="address-block"]//a/@href').extract()
+            for url in urls:
+                yield scrapy.Request(response.urljoin(url), callback=self.parse_location)


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
Boot Barn
https://www.bootbarn.com/stores-all


Discussion:

Resolving patch:
diff --git a/locations/spiders/bootbarn.py b/locations/spiders/bootbarn.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/bootbarn.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+import json
+import re
+
+import scrapy
+
+from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
+
+
+class BootbarnSpider(scrapy.Spider):
+    name = "bootbarn"
+    allowed_domains = ['bootbarn.com']
+    start_urls = [
+        'https://www.bootbarn.com/stores-all',
+    ]
+
+    def parse_hours(self, hours):
+        opening_hours = OpeningHours()
+        for i in range(0, len(hours), 2):
+            day = hours[i]
+            open_time, close_time = hours[i+1].split(' - ')
+            opening_hours.add_range(day=day[:2],
+                                    open_time=open_time,
+                                    close_time=close_time,
+                                    time_format='%H%p' if ':' not in open_time else '%H:%M%p')
+
+        return opening_hours.as_opening_hours()
+
+    def parse_location(self, response):
+        properties = {
+            'ref': re.search(r'.+/?StoreID=(.+)', response.url).group(1),
+            'name': response.xpath('normalize-space(//span[@class="store-name"]//text())').extract_first(),
+            'addr_full': response.xpath('normalize-space(//span[@class="store-address1"]//text())').extract_first(),
+            'city': response.xpath('normalize-space(//span[@class="store-address-city"]//text())').extract_first(),
+            'state': response.xpath('normalize-space(//span[@class="store-address-state"]//text())').extract_first(),
+            'postcode': response.xpath('normalize-space(//span[@class="store-address-postal-code"]//text())').extract_first(),
+            'phone': response.xpath('normalize-space(//span[@class="store-phone"]//text())').extract_first(),
+            'website': response.url,
+            'lat': response.xpath('//div[@id="store-detail-coords"]/@data-lat').extract_first(),
+            'lon': response.xpath('//div[@id="store-detail-coords"]/@data-lon').extract_first(),
+        }
+
+        properties['opening_hours'] = self.parse_hours(response.xpath('//div[@class="store-details-section"]//div[@class="store-hours-days"]/span/text()').extract())
+
+        yield GeojsonPointItem(**properties)
+
+
+    def parse(self, response):
+        urls = response.xpath('//div[@class="store"]/div[@class="city"]/a/@href').extract()
+
+        for url in urls:
+            yield scrapy.Request(response.urljoin(url), callback=self.parse_location)


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
The Home Depot (Canada)
https://stores.homedepot.ca/sitemap.xml


Discussion:

Resolving patch:
diff --git a/locations/spiders/homedepot_ca.py b/locations/spiders/homedepot_ca.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/homedepot_ca.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+import json
+import re
+import scrapy
+
+from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
+
+
+class HomeDepotCanadaSpider(scrapy.Spider):
+    name = "homedepot_ca"
+    allowed_domains = ["homedepot.ca"]
+    start_urls = [
+        'https://stores.homedepot.ca/sitemap.xml',
+    ]
+    download_delay = 0.2
+
+    def parse(self, response):
+        response.selector.remove_namespaces()
+        urls = response.xpath('//url/loc/text()').extract()
+        for url in urls:
+            if re.match(r"(.*.html)", url):
+                yield scrapy.Request(url, callback=self.parse_store)
+
+    def parse_store(self, response):
+        script = json.loads(response.xpath('//script[@type="application/ld+json" and contains(text(), "streetAddress")]/text()').extract_first())
+        data = script[0]
+        ref = re.search(r'.+/.+?([0-9]+).html', response.url).group(1)
+
+        properties = {
+            'name': data["name"],
+            'ref': ref,
+            'addr_full': data["address"]["streetAddress"].strip(),
+            'city': data["address"]["addressLocality"],
+            'state': data["address"]["addressRegion"],
+            'postcode': data["address"]["postalCode"],
+            'phone': data.get("address").get("telephone"),
+            'website': data.get("url") or response.url,
+            'lat': float(data["geo"]["latitude"]),
+            'lon': float(data["geo"]["longitude"]),
+        }
+
+        hours = self.parse_hours(data.get("openingHours"))
+        if hours:
+            properties["opening_hours"] = hours
+
+        yield GeojsonPointItem(**properties)
+
+    def parse_hours(self, open_hours):
+        opening_hours = OpeningHours()
+        location_hours = re.findall(r'([a-zA-Z]*)\s(.*?)\s-\s(.*?)\s', open_hours)
+
+        for weekday in location_hours:
+            opening_hours.add_range(day=weekday[0],
+                                    open_time=weekday[1],
+                                    close_time=weekday[2])
+
+        return opening_hours.as_opening_hours()


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
Thomas Jefferson University and Hospitals
https://hospitals.jefferson.edu/find-a-location.html


Discussion:
I'm working on this one...

Resolving patch:
diff --git a/locations/spiders/jefferson_univ_hosp.py b/locations/spiders/jefferson_univ_hosp.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/jefferson_univ_hosp.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+import json
+import re
+
+import scrapy
+
+from locations.items import GeojsonPointItem
+
+
+class jefferson_univ_hosp(scrapy.Spider):
+    name = "jefferson_univ_hosp"
+    allowed_domains = ['https://hospitals.jefferson.edu']
+    start_urls = [
+        'https://hospitals.jefferson.edu/find-a-location.html',
+    ]
+
+
+    def parse(self, response):
+        data = " ".join(response.xpath('//script[contains(text(), "itemsArray.push")]/text()').extract())
+        locations = re.findall(r"itemsArray.push\((.+?)\);",data)
+
+        for loc in locations:
+            if len(loc.split(";")) == 6:
+
+                loctype, locname, html, url, lat, lon = loc.strip("'").split(";")
+
+                phone = re.search(r"Phone:.*?([\d\-]+?)</p>", html)
+                if phone:
+                    phone = phone.groups()[0]
+
+                postcode = re.search(r"<br>.+?,.+?(\d{5})</p>", html)
+                if postcode:
+                    postcode = postcode.groups()[0]
+
+                addr_full = re.search(r"</h3><p>(.+?)<br>", html)
+                if addr_full:
+                    addr_full = addr_full.groups()[0]
+
+                properties = {
+                    'name': locname,
+                    'ref': loctype + "_" + locname,
+                    'addr_full': addr_full if addr_full else re.search(r"</h3> <p>(.+?)<br>", html).groups()[0],
+                    'city': re.search(r"<br>(.+?),", html).groups()[0],
+                    'state': re.search(r",(\s\D{2})", html).groups()[0].strip(),
+                    'postcode': postcode if postcode else None,
+                    'phone': phone if phone else None,
+                    'website': url,
+                    'lat': float(lat),
+                    'lon': float(lon),
+                }
+
+            else:
+                loctype, html = loc.strip("'").split(";")
+                locname, addr_full = html.split("(")
+
+                properties = {
+                    'name': locname,
+                    'ref': loc,
+                    'addr_full': addr_full
+                }
+
+            yield GeojsonPointItem(**properties)


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
Complete Cash
https://locations.completecash.net/


Discussion:

Resolving patch:
diff --git a/locations/spiders/completecash.py b/locations/spiders/completecash.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/completecash.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+import scrapy
+import json
+
+from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
+
+
+class CompleteCashSpider(scrapy.Spider):
+    name = "completecash"
+    allowed_domains = ["locations.completecash.net"]
+    cc_url = 'https://locations.completecash.net/api/5c1bc42410e9b07c77f0fab5/locations-details'
+    base_url = 'https://locations.completecash.net/'
+
+    start_urls = (cc_url, )
+    
+    def get_opening_hours(self, days):
+        o = OpeningHours()
+        for day, hours in days.items():
+            short_day = day[:2]
+            if hours:
+                hours = hours[0]
+                o.add_range(short_day, hours[0], hours[1])
+        return o.as_opening_hours()
+
+    def parse_location(self, location):
+        properties = location['properties']
+        opening_hours = self.get_opening_hours(properties['hoursOfOperation'])
+        coordinates = location['geometry']['coordinates']
+
+        props = {
+            'addr_full': properties['addressLine1'] + '\n' + properties['addressLine2'],
+            'lat': coordinates[1],
+            'lon': coordinates[0],
+            'city': properties['city'],
+            'postcode': properties['postalCode'],
+            'state': properties['province'],
+            'phone': properties['phoneNumber'],
+            'ref': self.base_url + properties['slug'],
+            'website': self.base_url + properties['slug'],
+            'opening_hours': opening_hours
+        }
+        return GeojsonPointItem(**props)
+
+    def parse(self, response):
+        locations = json.loads(response.text)['features']
+        for location in locations:
+            yield self.parse_location(location)


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
Victoria's Secret
https://www.victoriassecret.com/store-locator#/storelist/usa


Discussion:
Store directory: https://www.victoriassecret.com/store-locator#storeList/US
Working on this! :ghost: 

Resolving patch:
diff --git a/locations/spiders/victoriassecret.py b/locations/spiders/victoriassecret.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/victoriassecret.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+import json
+import scrapy
+from locations.items import GeojsonPointItem
+from locations.hours import OpeningHours
+
+DAY_MAPPING = {
+    2: 'Mo',
+    3: 'Tu',
+    4: 'We',
+    5: 'Th',
+    6: 'Fr',
+    7: 'Sa',
+    1: 'Su'
+}
+
+class VictoriassecretSpider(scrapy.Spider):
+    name = "victoriassecret"
+    allowed_domains = ["victoriassecret.com"]
+    start_urls = [
+        'https://www.victoriassecret.com/store-locator#storeList/US',
+    ]
+
+    def start_requests(self):
+        template = 'https://api.victoriassecret.com/stores/v1/search?countryCode=US'
+
+        headers = {
+            'Accept': 'application/json',
+        }
+
+        yield scrapy.http.FormRequest(
+            url=template,
+            method='GET',
+            headers=headers,
+            callback=self.parse
+        )
+
+    def parse(self, response):
+        jsonresponse = json.loads(response.body_as_unicode())
+        for stores in jsonresponse:
+            store = json.dumps(stores)
+            store_data = json.loads(store)
+
+            if store_data["latitudeDegrees"] == "":
+                properties['lat'] = float(0)
+                properties['lon'] = float(0)
+
+            else:
+                properties = {
+                    'name': store_data["name"],
+                    'ref': store_data["storeId"],
+                    'addr_full': store_data["address"]["streetAddress1"],
+                    'city': store_data["address"]["city"],
+                    'state': store_data["address"]["region"],
+                    'postcode': store_data["address"]["postalCode"],
+                    'country': "US",
+                    'phone': store_data["address"]["phone"],
+                    'lat': float(store_data["latitudeDegrees"]),
+                    'lon': float(store_data["longitudeDegrees"])
+                }
+
+            hours = store_data["hours"]
+
+            if hours:
+                properties['opening_hours'] = self.process_hours(hours)
+
+            yield GeojsonPointItem(**properties)
+
+    def process_hours(self, hours):
+        opening_hours = OpeningHours()
+
+        for hour in hours:
+            hr = json.dumps(hour)
+            hrs = json.loads(hr)
+            day = hrs["day"]
+            open_time = hrs["open"]
+            close_time = hrs["close"]
+
+            opening_hours.add_range(day=DAY_MAPPING[day], open_time=open_time, close_time=close_time, time_format='%H:%M %p')
+        return opening_hours.as_opening_hours()


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
Heart of Texas Goodwill Industries, Inc.
http://hotgoodwill.org/shop/territory-and-locations/


Discussion:

Resolving patch:
diff --git a/locations/spiders/goodwill.py b/locations/spiders/goodwill.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/goodwill.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+import scrapy
+import json
+from locations.items import GeojsonPointItem
+
+CATEGORY_MAPPING = {
+    '1': 'Donation Site',
+    '2': 'Outlet',
+    '3': 'Retail Store',
+    '4': 'Job & Career Support',
+    '5': 'Headquarters'
+}
+
+
+class GoodwillSpider(scrapy.Spider):
+    name = "goodwill"
+    allowed_domains = ['www.goodwill.org']
+    download_delay = 0.2
+
+    def start_requests(self):
+        url = 'https://www.goodwill.org/getLocations.php'
+        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+
+        with open('./locations/searchable_points/us_centroids_25mile_radius.csv') as points:
+
+            next(points)  # Ignore the header
+            for point in points:
+                _, lat, lon = point.strip().split(',')
+
+                # Unable to find a way to specify a search radius
+                # Appears to use a set search radius somewhere > 25mi, using 25mi to be safe
+                form_data = {
+                    'lat': '{}'.format(lat),
+                    'lng': '{}'.format(lon),
+                    'cats': '3,1,2,4,5'  # Includes donation sites
+                }
+
+                yield scrapy.http.FormRequest(url=url, method='POST', formdata=form_data, headers=headers,
+                                              callback=self.parse)
+
+    def parse(self, response):
+        data = json.loads(response.body_as_unicode())
+
+        for store in data:
+            service_codes = store.get("services")
+
+            store_categories = []
+            for code in service_codes:
+                store_categories.append(CATEGORY_MAPPING[code])
+
+            properties = {
+                'name': store["name"],
+                'ref': store["id"],
+                'addr_full': store["address1"],
+                'city': store["city"],
+                'state': store["state"],
+                'postcode': store["postal_code"],
+                'country': store["country"],
+                'phone': store.get("phone"),
+                'website': store.get("website") or response.url,
+                'lat': store.get("lat"),
+                'lon': store.get("lng"),
+                'extras': {
+                    'service_codes': service_codes,
+                    'store_categories': store_categories
+                }
+            }
+
+            yield GeojsonPointItem(**properties)


Test patch:
Repository: alltheplaces/alltheplaces

Issue:
Marks and Spencer
Marks and Spencer is a chain of department stores (with some "simply food" grocery-only stores) in the UK.

https://www.marksandspencer.com/s/store-listing


Discussion:
I can't find out the geo of each store 
@iandees Marks and Spencer leaves the store hours in military time. Prefer to convert it?
The OSM opening hours use 24-hour time, so no need to convert it.

Resolving patch:
diff --git a/locations/spiders/marks_and_spencer.py b/locations/spiders/marks_and_spencer.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/marks_and_spencer.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+import json
+import scrapy
+from locations.hours import OpeningHours
+from locations.items import GeojsonPointItem
+
+
+class MarksAndSpencerSpider(scrapy.Spider):
+    name = "marks_and_spencer"
+    start_urls = (
+        "https://www.marksandspencer.com/webapp/wcs/stores/servlet/MSResStoreFinderConfigCmd?storeId=10151&langId=-24",
+    )
+
+    def parse(self, response):
+        config = json.loads(
+            response.body_as_unicode().replace("STORE_FINDER_CONFIG=", "")
+        )
+        stores_api_url = (
+            f"{config['storeFinderAPIBaseURL']}?apikey={config['apiConsumerKey']}"
+        )
+        yield response.follow(stores_api_url, self.parse_stores)
+
+    def parse_stores(self, response):
+        stores = json.loads(response.body_as_unicode())
+        for store in stores["results"]:
+            properties = {
+                "ref": store["id"],
+                "name": store["name"],
+                "addr_full": f"{store['address']['addressLine1']}, {store['address']['addressLine2']}",
+                "city": store["address"]["city"],
+                "country": store["address"]["country"],
+                "postcode": store["address"]["postalCode"],
+                "lat": store["coordinates"]["latitude"],
+                "lon": store["coordinates"]["longitude"],
+                "phone": store.get("phone", ""),
+                "opening_hours": self.get_opening_hours(store),
+            }
+            yield GeojsonPointItem(**properties)
+
+    def get_opening_hours(self, store):
+        o = OpeningHours()
+        for day in store["coreOpeningHours"]:
+            o.add_range(
+                day["day"][:2], day["open"], day["close"].replace("24:00", "23:59")
+            )
+        return o.as_opening_hours()


Test patch: