Repository: alltheplaces/alltheplaces

Issue:
Loblaws & Superstore & Thirfty Foods & Fairway's Market & Safeway & Save on Foods & Farmboy
- [x] [Loblaws](https://www.loblaws.ca/store-locator)
- [x] [Superstore](https://www.realcanadiansuperstore.ca/store-locator)
- [x] [Thrifty Foods](https://www.thriftyfoods.com/find-a-store)
- [x] [Fairways](http://www.fairwaymarkets.com/index.php/store-locations)
- [x] [Safeway](https://www.safeway.ca/find-a-store) #311
- [x] [Save on Foods](https://shop.saveonfoods.com/chain/8E4E398?hash=locator&_ga=1.168908327.789228674.1484454975)
- [x] [Farm Boy](https://www.farmboy.ca/about-us/stores/)




Discussion:
I can work on this.
Going to clear the assignee on this so others can work on it. If you're still working on it, please re-assign to yourself and push up what you've got so far.
These should all be broken out into their own tickets. A lot of them have already been done.
working on this.
@iandees , I think this issue should be opened for other stores.
Thanks.
@iandees , Safeway of this issue has a different domain with #311 .
https://www.safeway.ca/find-a-store
So I am going to create one for above URL.
Thanks.

Resolving patch:
diff --git a/locations/spiders/farmboy.py b/locations/spiders/farmboy.py
new file mode 100644
--- /dev/null
+++ b/locations/spiders/farmboy.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+import scrapy
+import re
+
+from locations.items import GeojsonPointItem
+
+
+
+class FarmBoySpider(scrapy.Spider):
+    name = "farmboy"
+    allowed_domains = ["www.farmboy.ca"]
+    start_urls = (
+        'https://www.farmboy.ca/about-us/stores/',
+    )
+
+    def parse_hours(self, trs):
+        opening_hours = []
+        for tr in trs:
+            day = tr.xpath('td/text()').extract_first()
+            time = tr.xpath('td[2]/text()').extract_first()
+            if day is not None and time is not None:
+                day = day.replace('Monday', 'Mo').replace('Tuesday', 'Tu').replace('Wednesday', 'We').replace('Thursday', 'Th').replace('Friday', 'Fr').replace('Saturday', 'Sa').replace('Sunday', 'Su')
+                time = time.replace('\xa0', ' ')
+                match = re.search(r'(\d{1,2}) (a|p)[.]m[.] . (\d{1,2}) (a|p)[.]m[.]', time)
+                if match:
+                    (f_hr, f_ampm, t_hr, t_ampm) = match.groups()
+                    f_hr = int(f_hr)
+                    if f_ampm == 'p':
+                        f_hr += 12
+                    elif f_ampm == 'a' and f_hr == 12:
+                        f_hr = 0
+                    t_hr = int(t_hr)
+                    if t_ampm == 'p':
+                        t_hr += 12
+                    elif t_ampm == 'a' and t_hr == 12:
+                        t_hr = 0
+                    
+                    hours = '{:02d}:{}-{:02d}:{}'.format(
+                        f_hr,
+                        '00',
+                        t_hr,
+                        '00',
+                    )
+                    opening_hours.append('{} {}'.format(day, hours))
+
+        return "; ".join(opening_hours)
+
+    def parse(self, response):
+
+        stores = response.xpath('//div[@id="portfolio"]/article')
+        for store in stores:
+            if store.xpath('@class').extract_first() != 'all portfolio-item toronto':
+                properties = {
+                    "ref": store.xpath('div/h3/text()').extract_first(),
+                    "name": store.xpath('div/h3/text()').extract_first(),
+                    "addr_full": store.xpath('div/div/p/text()').extract_first(),
+                    "postcode": self.postCode(store.xpath('div/div/p/text()[last()]').extract_first()),
+                    "state": self.state(store.xpath('div/div/p/text()[last() - 1]').extract_first()),
+                    "phone": self.phone(store.xpath('div/div/div[@id="cinfo"]/p/text()').extract_first()),
+                    "opening_hours": self.parse_hours(store.xpath('div/div/div[@id="sinfo"]/table[1]/tbody/tr'))
+                }
+                yield GeojsonPointItem(**properties)
+
+    def city(self, data):
+        str_list = data.split(',')
+        print(str_list[0])
+        return str_list[0].strip()
+
+    def state(self, data):
+        if data is None: return ''
+        m = re.search(r'(,)\s(.*)', data)
+        m_space = re.search(r'\s(.*)', data)
+        if m:
+            return m.group(2)
+        elif m_space: 
+            return m_space.group(1)
+        else: return data
+
+    def postCode(self, data):
+        if data is None: return ''
+        return data.strip().replace('\xa0', '')
+
+    def phone(self, data):
+        if data is None: return ''
+        if ':' not in data: return data.replace('\xa0', ' ')
+        return data.split(':')[1]
+


Test patch: