We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ID: scrape fails on substitute representative
Once #2053 is merged, the people scraper is blowing up reading https://legislature.idaho.gov/house/membership/ .  
Search down to "Scanlin", who you'll find is a "substitute" representative.  There's a parsing problem because the scraper expects the name to be bold and the party to not be; in this case "Substitute" is bold only.

For a description of what a "Substitute" is, see http://www.spokesman.com/blogs/boise/2014/mar/16/idahos-legislative-substitute-law-unique/ 

Just fixing this would leave two legislators in that district, where, as in Highlander, "there can be only one".  Representative Kloc, who Scanlin is substituting for, is directly above him.

Assuming that his roll call votes, committee memberships, etc. are registered to the substitute, I believe a fully correct fix for this would involve:
- lookahead for a "Substitute", skipping the current legislator if the next legislator is a substitute.
- rewriting the line parser to skip "Substitute" and just use the text (not bold format) for name/party parsing.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `openstates/id/people.py`
Content:
```
1 import re
2 import lxml.html
3 from pupa.scrape import Scraper, Person
4 
5 BASE_URL = 'https://legislature.idaho.gov/%s/membership/'
6 CHAMBERS = {'upper': 'senate', 'lower': 'house'}
7 PARTY = {
8     '(R)': 'Republican',
9     '(D)': 'Democratic',
10 }
11 
12 phone_patterns = {
13     'office': re.compile(r'Statehouse'),
14     'business': re.compile(r'Bus'),
15     'home': re.compile(r'Home'),
16 }
17 
18 parse_phone_pattern = re.compile(r'tel:(?:\+1)?(\d{10}$)')
19 fax_pattern = re.compile(r'fax\s+\((\d{3})\)\s+(\d{3})-(\d{4})', re.IGNORECASE)
20 address_pattern = re.compile(r', \d{5}')
21 address_replace_pattern = re.compile(r'(\d{5})')
22 
23 
24 def get_phones(el):
25     phones = {}
26     for link in el.xpath('p/a[@class = "mob-tel"]'):
27         prefix = link.getprevious().tail
28         for label, pattern in phone_patterns.items():
29             if pattern.search(prefix) is not None:
30                 phones[label] = parse_phone(link.get('href'))
31     return phones
32 
33 
34 def parse_phone(phone):
35     res = parse_phone_pattern.search(phone)
36     if res is not None:
37         return res.groups()[0]
38 
39 
40 def get_fax(el):
41     res = fax_pattern.search(el.text_content())
42     if res is not None:
43         return ''.join(res.groups())
44 
45 
46 def get_address(el):
47     for br in el.xpath('p/br'):
48         piece = (br.tail or '').strip()
49         res = address_pattern.search(piece)
50         if res is not None:
51             return address_replace_pattern.sub(r'ID \1', piece).strip()
52 
53 
54 class IDPersonScraper(Scraper):
55     """Legislator data seems to be available for the current term only."""
56     jurisdiction = 'id'
57 
58     def scrape(self, chamber=None):
59         if chamber:
60             yield from self.scrape_chamber(chamber)
61         else:
62             yield from self.scrape_chamber('upper')
63             yield from self.scrape_chamber('lower')
64 
65     def scrape_chamber(self, chamber):
66         """
67         Scrapes legislators for the current term only
68         """
69         # self.validate_term(term, latest_only=True)
70         url = BASE_URL % CHAMBERS[chamber].lower()
71         index = self.get(url, verify=False).text
72         html = lxml.html.fromstring(index)
73         html.make_links_absolute(url)
74 
75         rows = html.xpath('//div[contains(@class, "row-equal-height")]')
76 
77         for row in rows:
78             img_url = row.xpath('.//img/@src')[0]
79 
80             inner = row.xpath('.//div[@class="vc-column-innner-wrapper"]')[1]
81             if 'Resigned' in inner.text_content():
82                 continue
83 
84             name = inner.xpath('p/strong')[0].text.replace(u'\xa0', ' ').strip()
85             name = re.sub('\s+', ' ', name)
86             party = PARTY[inner.xpath('p/strong')[0].tail.strip()]
87             email = inner.xpath('p/strong/a')[0].text
88             district = inner.xpath('p/a')[0].text.replace('District ', '')
89 
90             person_url = inner.xpath('p/a/@href')[0]
91             # skip roles for now
92             role = ''
93             # for com in inner.xpath('p/a[contains(@href, "committees")]'):
94             #     role = com.tail.strip()
95 
96             person = Person(name=name, district=district,
97                             party=party, primary_org=chamber,
98                             image=img_url, role=role)
99             phones = get_phones(inner)
100             phone = phones.get('home') or phones.get('business')
101             office_phone = phones.get('office')
102             address = get_address(inner)
103             fax = get_fax(inner)
104             if address:
105                 person.add_contact_detail(type='address', value=address,
106                                           note='District Office')
107             if phone:
108                 person.add_contact_detail(type='voice', value=phone,
109                                           note='District Office')
110             if fax:
111                 person.add_contact_detail(type='fax', value=fax,
112                                           note='District Office')
113             if email:
114                 person.add_contact_detail(type='email', value=email,
115                                           note='District Office')
116             if office_phone:
117                 person.add_contact_detail(type='voice', value=office_phone,
118                                           note='Capitol Office')
119             person.add_source(url)
120             person.add_link(person_url)
121             yield person
122
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/openstates/id/people.py b/openstates/id/people.py
--- a/openstates/id/people.py
+++ b/openstates/id/people.py
@@ -78,7 +78,8 @@
             img_url = row.xpath('.//img/@src')[0]
 
             inner = row.xpath('.//div[@class="vc-column-innner-wrapper"]')[1]
-            if 'Resigned' in inner.text_content():
+            inner_text = inner.text_content()
+            if 'Resigned' in inner_text or 'Substitute' in inner_text:
                 continue
 
             name = inner.xpath('p/strong')[0].text.replace(u'\xa0', ' ').strip()

{"golden_diff": "diff --git a/openstates/id/people.py b/openstates/id/people.py\n--- a/openstates/id/people.py\n+++ b/openstates/id/people.py\n@@ -78,7 +78,8 @@\n             img_url = row.xpath('.//img/@src')[0]\n \n             inner = row.xpath('.//div[@class=\"vc-column-innner-wrapper\"]')[1]\n-            if 'Resigned' in inner.text_content():\n+            inner_text = inner.text_content()\n+            if 'Resigned' in inner_text or 'Substitute' in inner_text:\n                 continue\n \n             name = inner.xpath('p/strong')[0].text.replace(u'\\xa0', ' ').strip()\n", "issue": "ID: scrape fails on substitute representative\nOnce #2053 is merged, the people scraper is blowing up reading https://legislature.idaho.gov/house/membership/ .  \r\nSearch down to \"Scanlin\", who you'll find is a \"substitute\" representative.  There's a parsing problem because the scraper expects the name to be bold and the party to not be; in this case \"Substitute\" is bold only.\r\n\r\nFor a description of what a \"Substitute\" is, see http://www.spokesman.com/blogs/boise/2014/mar/16/idahos-legislative-substitute-law-unique/ \r\n\r\nJust fixing this would leave two legislators in that district, where, as in Highlander, \"there can be only one\".  Representative Kloc, who Scanlin is substituting for, is directly above him.\r\n\r\nAssuming that his roll call votes, committee memberships, etc. are registered to the substitute, I believe a fully correct fix for this would involve:\r\n- lookahead for a \"Substitute\", skipping the current legislator if the next legislator is a substitute.\r\n- rewriting the line parser to skip \"Substitute\" and just use the text (not bold format) for name/party parsing.\r\n\n", "before_files": [{"content": "import re\nimport lxml.html\nfrom pupa.scrape import Scraper, Person\n\nBASE_URL = 'https://legislature.idaho.gov/%s/membership/'\nCHAMBERS = {'upper': 'senate', 'lower': 'house'}\nPARTY = {\n    '(R)': 'Republican',\n    '(D)': 'Democratic',\n}\n\nphone_patterns = {\n    'office': re.compile(r'Statehouse'),\n    'business': re.compile(r'Bus'),\n    'home': re.compile(r'Home'),\n}\n\nparse_phone_pattern = re.compile(r'tel:(?:\\+1)?(\\d{10}$)')\nfax_pattern = re.compile(r'fax\\s+\\((\\d{3})\\)\\s+(\\d{3})-(\\d{4})', re.IGNORECASE)\naddress_pattern = re.compile(r', \\d{5}')\naddress_replace_pattern = re.compile(r'(\\d{5})')\n\n\ndef get_phones(el):\n    phones = {}\n    for link in el.xpath('p/a[@class = \"mob-tel\"]'):\n        prefix = link.getprevious().tail\n        for label, pattern in phone_patterns.items():\n            if pattern.search(prefix) is not None:\n                phones[label] = parse_phone(link.get('href'))\n    return phones\n\n\ndef parse_phone(phone):\n    res = parse_phone_pattern.search(phone)\n    if res is not None:\n        return res.groups()[0]\n\n\ndef get_fax(el):\n    res = fax_pattern.search(el.text_content())\n    if res is not None:\n        return ''.join(res.groups())\n\n\ndef get_address(el):\n    for br in el.xpath('p/br'):\n        piece = (br.tail or '').strip()\n        res = address_pattern.search(piece)\n        if res is not None:\n            return address_replace_pattern.sub(r'ID \\1', piece).strip()\n\n\nclass IDPersonScraper(Scraper):\n    \"\"\"Legislator data seems to be available for the current term only.\"\"\"\n    jurisdiction = 'id'\n\n    def scrape(self, chamber=None):\n        if chamber:\n            yield from self.scrape_chamber(chamber)\n        else:\n            yield from self.scrape_chamber('upper')\n            yield from self.scrape_chamber('lower')\n\n    def scrape_chamber(self, chamber):\n        \"\"\"\n        Scrapes legislators for the current term only\n        \"\"\"\n        # self.validate_term(term, latest_only=True)\n        url = BASE_URL % CHAMBERS[chamber].lower()\n        index = self.get(url, verify=False).text\n        html = lxml.html.fromstring(index)\n        html.make_links_absolute(url)\n\n        rows = html.xpath('//div[contains(@class, \"row-equal-height\")]')\n\n        for row in rows:\n            img_url = row.xpath('.//img/@src')[0]\n\n            inner = row.xpath('.//div[@class=\"vc-column-innner-wrapper\"]')[1]\n            if 'Resigned' in inner.text_content():\n                continue\n\n            name = inner.xpath('p/strong')[0].text.replace(u'\\xa0', ' ').strip()\n            name = re.sub('\\s+', ' ', name)\n            party = PARTY[inner.xpath('p/strong')[0].tail.strip()]\n            email = inner.xpath('p/strong/a')[0].text\n            district = inner.xpath('p/a')[0].text.replace('District ', '')\n\n            person_url = inner.xpath('p/a/@href')[0]\n            # skip roles for now\n            role = ''\n            # for com in inner.xpath('p/a[contains(@href, \"committees\")]'):\n            #     role = com.tail.strip()\n\n            person = Person(name=name, district=district,\n                            party=party, primary_org=chamber,\n                            image=img_url, role=role)\n            phones = get_phones(inner)\n            phone = phones.get('home') or phones.get('business')\n            office_phone = phones.get('office')\n            address = get_address(inner)\n            fax = get_fax(inner)\n            if address:\n                person.add_contact_detail(type='address', value=address,\n                                          note='District Office')\n            if phone:\n                person.add_contact_detail(type='voice', value=phone,\n                                          note='District Office')\n            if fax:\n                person.add_contact_detail(type='fax', value=fax,\n                                          note='District Office')\n            if email:\n                person.add_contact_detail(type='email', value=email,\n                                          note='District Office')\n            if office_phone:\n                person.add_contact_detail(type='voice', value=office_phone,\n                                          note='Capitol Office')\n            person.add_source(url)\n            person.add_link(person_url)\n            yield person\n", "path": "openstates/id/people.py"}], "after_files": [{"content": "import re\nimport lxml.html\nfrom pupa.scrape import Scraper, Person\n\nBASE_URL = 'https://legislature.idaho.gov/%s/membership/'\nCHAMBERS = {'upper': 'senate', 'lower': 'house'}\nPARTY = {\n    '(R)': 'Republican',\n    '(D)': 'Democratic',\n}\n\nphone_patterns = {\n    'office': re.compile(r'Statehouse'),\n    'business': re.compile(r'Bus'),\n    'home': re.compile(r'Home'),\n}\n\nparse_phone_pattern = re.compile(r'tel:(?:\\+1)?(\\d{10}$)')\nfax_pattern = re.compile(r'fax\\s+\\((\\d{3})\\)\\s+(\\d{3})-(\\d{4})', re.IGNORECASE)\naddress_pattern = re.compile(r', \\d{5}')\naddress_replace_pattern = re.compile(r'(\\d{5})')\n\n\ndef get_phones(el):\n    phones = {}\n    for link in el.xpath('p/a[@class = \"mob-tel\"]'):\n        prefix = link.getprevious().tail\n        for label, pattern in phone_patterns.items():\n            if pattern.search(prefix) is not None:\n                phones[label] = parse_phone(link.get('href'))\n    return phones\n\n\ndef parse_phone(phone):\n    res = parse_phone_pattern.search(phone)\n    if res is not None:\n        return res.groups()[0]\n\n\ndef get_fax(el):\n    res = fax_pattern.search(el.text_content())\n    if res is not None:\n        return ''.join(res.groups())\n\n\ndef get_address(el):\n    for br in el.xpath('p/br'):\n        piece = (br.tail or '').strip()\n        res = address_pattern.search(piece)\n        if res is not None:\n            return address_replace_pattern.sub(r'ID \\1', piece).strip()\n\n\nclass IDPersonScraper(Scraper):\n    \"\"\"Legislator data seems to be available for the current term only.\"\"\"\n    jurisdiction = 'id'\n\n    def scrape(self, chamber=None):\n        if chamber:\n            yield from self.scrape_chamber(chamber)\n        else:\n            yield from self.scrape_chamber('upper')\n            yield from self.scrape_chamber('lower')\n\n    def scrape_chamber(self, chamber):\n        \"\"\"\n        Scrapes legislators for the current term only\n        \"\"\"\n        # self.validate_term(term, latest_only=True)\n        url = BASE_URL % CHAMBERS[chamber].lower()\n        index = self.get(url, verify=False).text\n        html = lxml.html.fromstring(index)\n        html.make_links_absolute(url)\n\n        rows = html.xpath('//div[contains(@class, \"row-equal-height\")]')\n\n        for row in rows:\n            img_url = row.xpath('.//img/@src')[0]\n\n            inner = row.xpath('.//div[@class=\"vc-column-innner-wrapper\"]')[1]\n            inner_text = inner.text_content()\n            if 'Resigned' in inner_text or 'Substitute' in inner_text:\n                continue\n\n            name = inner.xpath('p/strong')[0].text.replace(u'\\xa0', ' ').strip()\n            name = re.sub('\\s+', ' ', name)\n            party = PARTY[inner.xpath('p/strong')[0].tail.strip()]\n            email = inner.xpath('p/strong/a')[0].text\n            district = inner.xpath('p/a')[0].text.replace('District ', '')\n\n            person_url = inner.xpath('p/a/@href')[0]\n            # skip roles for now\n            role = ''\n            # for com in inner.xpath('p/a[contains(@href, \"committees\")]'):\n            #     role = com.tail.strip()\n\n            person = Person(name=name, district=district,\n                            party=party, primary_org=chamber,\n                            image=img_url, role=role)\n            phones = get_phones(inner)\n            phone = phones.get('home') or phones.get('business')\n            office_phone = phones.get('office')\n            address = get_address(inner)\n            fax = get_fax(inner)\n            if address:\n                person.add_contact_detail(type='address', value=address,\n                                          note='District Office')\n            if phone:\n                person.add_contact_detail(type='voice', value=phone,\n                                          note='District Office')\n            if fax:\n                person.add_contact_detail(type='fax', value=fax,\n                                          note='District Office')\n            if email:\n                person.add_contact_detail(type='email', value=email,\n                                          note='District Office')\n            if office_phone:\n                person.add_contact_detail(type='voice', value=office_phone,\n                                          note='Capitol Office')\n            person.add_source(url)\n            person.add_link(person_url)\n            yield person\n", "path": "openstates/id/people.py"}]}