We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
NH failing since at least 2017-11-16
NH has been failing since 2017-11-16

Based on automated runs it appears that NH has not run successfully in 2 days (2017-11-16).


```
  23:17:39 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Yvonne Thomas"}
23:17:39 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "David Lundgren"}
23:17:39 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Neal Kurk"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Timothy Smith"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Thomas Buco"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Sandra Keans"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Jane Beaulieu"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "David Huot"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Franklin Tilton"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Kermit Williams"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Jacqueline Cali-Pitts"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Michael McCarthy"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Martin Jack"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Martha Hennessey"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Harold French"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Frank Sapareto"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Gary Daniels"}
23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{"name": "Suzanne Smith"}
  people: {}
nh (scrape, import)
  committees: {}
no pupa_settings on path, using defaults
  bills: {}
import jurisdictions...
import organizations...
import people...
import posts...
import memberships...
    subcommands[args.subcommand].handle(args, other)
  File "/opt/openstates/venv-pupa//bin/pupa", line 11, in <module>
    load_entry_point('pupa', 'console_scripts', 'pupa')()
  File "/opt/openstates/venv-pupa/src/pupa/pupa/cli/__main__.py", line 67, in main
Traceback (most recent call last):
  File "/opt/openstates/venv-pupa/src/pupa/pupa/cli/commands/update.py", line 260, in handle
    return self.do_handle(args, other, juris)
  File "/opt/openstates/venv-pupa/src/pupa/pupa/cli/commands/update.py", line 307, in do_handle
    report['import'] = self.do_import(juris, args)
  File "/opt/openstates/venv-pupa/src/pupa/pupa/cli/commands/update.py", line 211, in do_import
    report.update(membership_importer.import_directory(datadir))
  File "/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py", line 190, in import_directory
    return self.import_data(json_stream())
  File "/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py", line 227, in import_data
    obj_id, what = self.import_item(data)
  File "/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py", line 247, in import_item
    data = self.prepare_for_db(data)
  File "/opt/openstates/venv-pupa/src/pupa/pupa/importers/memberships.py", line 50, in prepare_for_db
    data['post_id'] = self.post_importer.resolve_json_id(data['post_id'])
  File "/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py", line 165, in resolve_json_id
    raise UnresolvedIdError(errmsg)
pupa.exceptions.UnresolvedIdError: cannot resolve pseudo id to Post: ~{"label": "13", "organization__classification": "lower"}
```

Visit http://bobsled.openstates.org for more info.


--- 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/nh/people.py`
Content:
```
1 import re
2 
3 from pupa.scrape import Person, Scraper
4 from openstates.utils import LXMLMixin
5 
6 
7 class NHPersonScraper(Scraper, LXMLMixin):
8     members_url = 'http://www.gencourt.state.nh.us/downloads/Members.txt'
9     lookup_url = 'http://www.gencourt.state.nh.us/house/members/memberlookup.aspx'
10     house_profile_url = 'http://www.gencourt.state.nh.us/house/members/member.aspx?member={}'
11     senate_profile_url = 'http://www.gencourt.state.nh.us/Senate/members/webpages/district{}.aspx'
12 
13     chamber_map = {'H': 'lower', 'S': 'upper'}
14     party_map = {
15         'D': 'Democratic',
16         'R': 'Republican',
17         'I': 'Independent',
18         'L': 'Libertarian',
19     }
20 
21     def _get_photo(self, url, chamber):
22         """Attempts to find a portrait in the given legislator profile."""
23         doc = self.lxmlize(url)
24 
25         if chamber == 'upper':
26             src = doc.xpath('//div[@id="page_content"]//img[contains(@src, '
27                             '"images/senators") or contains(@src, "Senator")]/@src')
28         elif chamber == 'lower':
29             src = doc.xpath('//img[contains(@src, "images/memberpics")]/@src')
30 
31         if src and 'nophoto' not in src[0]:
32             photo_url = src[0]
33         else:
34             photo_url = ''
35 
36         return photo_url
37 
38     def _parse_person(self, row, chamber, seat_map):
39         # Capture legislator vitals.
40         first_name = row['FirstName']
41         middle_name = row['MiddleName']
42         last_name = row['LastName']
43         full_name = '{} {} {}'.format(first_name, middle_name, last_name)
44         full_name = re.sub(r'[\s]{2,}', ' ', full_name)
45 
46         if chamber == 'lower':
47             district = '{} {}'.format(row['County'], int(row['District'])).strip()
48         else:
49             district = str(int(row['District'])).strip()
50 
51         party = self.party_map[row['party'].upper()]
52         email = row['WorkEmail']
53 
54         if district == '0':
55             self.warning('Skipping {}, district is set to 0'.format(full_name))
56             return
57 
58         # Temporary fix for Kari Lerner
59         if district == 'Rockingham 0' and last_name == 'Lerner':
60             district = 'Rockingham 4'
61 
62         person = Person(primary_org=chamber,
63                         district=district,
64                         name=full_name,
65                         party=party)
66 
67         extras = {
68             'first_name': first_name,
69             'middle_name': middle_name,
70             'last_name': last_name
71         }
72 
73         person.extras = extras
74         if email:
75             person.add_contact_detail(type='email', value=email, note='District Office')
76 
77         # Capture legislator office contact information.
78         district_address = '{}\n{}\n{}, {} {}'.format(row['Address'],
79                                                       row['address2'],
80                                                       row['city'], row['State'],
81                                                       row['Zipcode']).strip()
82 
83         phone = row['Phone'].strip()
84         if not phone:
85             phone = None
86 
87         if district_address:
88             person.add_contact_detail(type='address', value=district_address, note='Home Office')
89         if phone:
90             person.add_contact_detail(type='voice', value=phone, note='Home Office')
91 
92         # Retrieve legislator portrait.
93         profile_url = None
94         if chamber == 'upper':
95             profile_url = self.senate_profile_url.format(row['District'])
96         elif chamber == 'lower':
97             try:
98                 seat_number = seat_map[row['seatno']]
99                 profile_url = self.house_profile_url.format(seat_number)
100             except KeyError:
101                 pass
102 
103         if profile_url:
104             person.image = self._get_photo(profile_url, chamber)
105             person.add_source(profile_url)
106 
107         return person
108 
109     def _parse_members_txt(self):
110         lines = self.get(self.members_url).text.splitlines()
111 
112         header = lines[0].split('\t')
113 
114         for line in lines[1:]:
115             yield dict(zip(header, line.split('\t')))
116 
117     def _parse_seat_map(self):
118         """Get mapping between seat numbers and legislator identifiers."""
119         seat_map = {}
120         page = self.lxmlize(self.lookup_url)
121         options = page.xpath('//select[@id="member"]/option')
122         for option in options:
123             member_url = self.house_profile_url.format(option.attrib['value'])
124             member_page = self.lxmlize(member_url)
125             table = member_page.xpath('//table[@id="Table1"]')
126             if table:
127                 res = re.search(r'seat #:(\d+)', table[0].text_content(), re.IGNORECASE)
128                 if res:
129                     seat_map[res.groups()[0]] = option.attrib['value']
130         return seat_map
131 
132     def scrape(self, chamber=None):
133         chambers = [chamber] if chamber is not None else ['upper', 'lower']
134         seat_map = self._parse_seat_map()
135         for chamber in chambers:
136             for row in self._parse_members_txt():
137                 print(row['electedStatus'])
138                 if (self.chamber_map[row['LegislativeBody']] == chamber and
139                         row['electedStatus'] != 'Former'):
140                     person = self._parse_person(row, chamber, seat_map)
141 
142                     # allow for skipping
143                     if not person:
144                         continue
145 
146                     person.add_source(self.members_url)
147                     person.add_link(self.members_url)
148                     yield person
149
```
--- 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/nh/people.py b/openstates/nh/people.py
--- a/openstates/nh/people.py
+++ b/openstates/nh/people.py
@@ -59,6 +59,13 @@
         if district == 'Rockingham 0' and last_name == 'Lerner':
             district = 'Rockingham 4'
 
+        # Temporary fix for Casey Conley
+        if last_name == 'Conley':
+            if district == '13':
+                district = 'Strafford 13'
+            elif district == 'Strafford 13':
+                self.info('"Temporary fix for Casey Conley" can be removed')
+
         person = Person(primary_org=chamber,
                         district=district,
                         name=full_name,

{"golden_diff": "diff --git a/openstates/nh/people.py b/openstates/nh/people.py\n--- a/openstates/nh/people.py\n+++ b/openstates/nh/people.py\n@@ -59,6 +59,13 @@\n         if district == 'Rockingham 0' and last_name == 'Lerner':\n             district = 'Rockingham 4'\n \n+        # Temporary fix for Casey Conley\n+        if last_name == 'Conley':\n+            if district == '13':\n+                district = 'Strafford 13'\n+            elif district == 'Strafford 13':\n+                self.info('\"Temporary fix for Casey Conley\" can be removed')\n+\n         person = Person(primary_org=chamber,\n                         district=district,\n                         name=full_name,\n", "issue": "NH failing since at least 2017-11-16\nNH has been failing since 2017-11-16\n\nBased on automated runs it appears that NH has not run successfully in 2 days (2017-11-16).\n\n\n```\n  23:17:39 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Yvonne Thomas\"}\n23:17:39 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"David Lundgren\"}\n23:17:39 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Neal Kurk\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Timothy Smith\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Thomas Buco\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Sandra Keans\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Jane Beaulieu\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"David Huot\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Franklin Tilton\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Kermit Williams\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Jacqueline Cali-Pitts\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Michael McCarthy\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Martin Jack\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Martha Hennessey\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Harold French\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Frank Sapareto\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Gary Daniels\"}\n23:17:40 ERROR pupa: cannot resolve pseudo id to Person: ~{\"name\": \"Suzanne Smith\"}\n  people: {}\nnh (scrape, import)\n  committees: {}\nno pupa_settings on path, using defaults\n  bills: {}\nimport jurisdictions...\nimport organizations...\nimport people...\nimport posts...\nimport memberships...\n    subcommands[args.subcommand].handle(args, other)\n  File \"/opt/openstates/venv-pupa//bin/pupa\", line 11, in <module>\n    load_entry_point('pupa', 'console_scripts', 'pupa')()\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/cli/__main__.py\", line 67, in main\nTraceback (most recent call last):\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/cli/commands/update.py\", line 260, in handle\n    return self.do_handle(args, other, juris)\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/cli/commands/update.py\", line 307, in do_handle\n    report['import'] = self.do_import(juris, args)\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/cli/commands/update.py\", line 211, in do_import\n    report.update(membership_importer.import_directory(datadir))\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py\", line 190, in import_directory\n    return self.import_data(json_stream())\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py\", line 227, in import_data\n    obj_id, what = self.import_item(data)\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py\", line 247, in import_item\n    data = self.prepare_for_db(data)\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/importers/memberships.py\", line 50, in prepare_for_db\n    data['post_id'] = self.post_importer.resolve_json_id(data['post_id'])\n  File \"/opt/openstates/venv-pupa/src/pupa/pupa/importers/base.py\", line 165, in resolve_json_id\n    raise UnresolvedIdError(errmsg)\npupa.exceptions.UnresolvedIdError: cannot resolve pseudo id to Post: ~{\"label\": \"13\", \"organization__classification\": \"lower\"}\n```\n\nVisit http://bobsled.openstates.org for more info.\n\n", "before_files": [{"content": "import re\n\nfrom pupa.scrape import Person, Scraper\nfrom openstates.utils import LXMLMixin\n\n\nclass NHPersonScraper(Scraper, LXMLMixin):\n    members_url = 'http://www.gencourt.state.nh.us/downloads/Members.txt'\n    lookup_url = 'http://www.gencourt.state.nh.us/house/members/memberlookup.aspx'\n    house_profile_url = 'http://www.gencourt.state.nh.us/house/members/member.aspx?member={}'\n    senate_profile_url = 'http://www.gencourt.state.nh.us/Senate/members/webpages/district{}.aspx'\n\n    chamber_map = {'H': 'lower', 'S': 'upper'}\n    party_map = {\n        'D': 'Democratic',\n        'R': 'Republican',\n        'I': 'Independent',\n        'L': 'Libertarian',\n    }\n\n    def _get_photo(self, url, chamber):\n        \"\"\"Attempts to find a portrait in the given legislator profile.\"\"\"\n        doc = self.lxmlize(url)\n\n        if chamber == 'upper':\n            src = doc.xpath('//div[@id=\"page_content\"]//img[contains(@src, '\n                            '\"images/senators\") or contains(@src, \"Senator\")]/@src')\n        elif chamber == 'lower':\n            src = doc.xpath('//img[contains(@src, \"images/memberpics\")]/@src')\n\n        if src and 'nophoto' not in src[0]:\n            photo_url = src[0]\n        else:\n            photo_url = ''\n\n        return photo_url\n\n    def _parse_person(self, row, chamber, seat_map):\n        # Capture legislator vitals.\n        first_name = row['FirstName']\n        middle_name = row['MiddleName']\n        last_name = row['LastName']\n        full_name = '{} {} {}'.format(first_name, middle_name, last_name)\n        full_name = re.sub(r'[\\s]{2,}', ' ', full_name)\n\n        if chamber == 'lower':\n            district = '{} {}'.format(row['County'], int(row['District'])).strip()\n        else:\n            district = str(int(row['District'])).strip()\n\n        party = self.party_map[row['party'].upper()]\n        email = row['WorkEmail']\n\n        if district == '0':\n            self.warning('Skipping {}, district is set to 0'.format(full_name))\n            return\n\n        # Temporary fix for Kari Lerner\n        if district == 'Rockingham 0' and last_name == 'Lerner':\n            district = 'Rockingham 4'\n\n        person = Person(primary_org=chamber,\n                        district=district,\n                        name=full_name,\n                        party=party)\n\n        extras = {\n            'first_name': first_name,\n            'middle_name': middle_name,\n            'last_name': last_name\n        }\n\n        person.extras = extras\n        if email:\n            person.add_contact_detail(type='email', value=email, note='District Office')\n\n        # Capture legislator office contact information.\n        district_address = '{}\\n{}\\n{}, {} {}'.format(row['Address'],\n                                                      row['address2'],\n                                                      row['city'], row['State'],\n                                                      row['Zipcode']).strip()\n\n        phone = row['Phone'].strip()\n        if not phone:\n            phone = None\n\n        if district_address:\n            person.add_contact_detail(type='address', value=district_address, note='Home Office')\n        if phone:\n            person.add_contact_detail(type='voice', value=phone, note='Home Office')\n\n        # Retrieve legislator portrait.\n        profile_url = None\n        if chamber == 'upper':\n            profile_url = self.senate_profile_url.format(row['District'])\n        elif chamber == 'lower':\n            try:\n                seat_number = seat_map[row['seatno']]\n                profile_url = self.house_profile_url.format(seat_number)\n            except KeyError:\n                pass\n\n        if profile_url:\n            person.image = self._get_photo(profile_url, chamber)\n            person.add_source(profile_url)\n\n        return person\n\n    def _parse_members_txt(self):\n        lines = self.get(self.members_url).text.splitlines()\n\n        header = lines[0].split('\\t')\n\n        for line in lines[1:]:\n            yield dict(zip(header, line.split('\\t')))\n\n    def _parse_seat_map(self):\n        \"\"\"Get mapping between seat numbers and legislator identifiers.\"\"\"\n        seat_map = {}\n        page = self.lxmlize(self.lookup_url)\n        options = page.xpath('//select[@id=\"member\"]/option')\n        for option in options:\n            member_url = self.house_profile_url.format(option.attrib['value'])\n            member_page = self.lxmlize(member_url)\n            table = member_page.xpath('//table[@id=\"Table1\"]')\n            if table:\n                res = re.search(r'seat #:(\\d+)', table[0].text_content(), re.IGNORECASE)\n                if res:\n                    seat_map[res.groups()[0]] = option.attrib['value']\n        return seat_map\n\n    def scrape(self, chamber=None):\n        chambers = [chamber] if chamber is not None else ['upper', 'lower']\n        seat_map = self._parse_seat_map()\n        for chamber in chambers:\n            for row in self._parse_members_txt():\n                print(row['electedStatus'])\n                if (self.chamber_map[row['LegislativeBody']] == chamber and\n                        row['electedStatus'] != 'Former'):\n                    person = self._parse_person(row, chamber, seat_map)\n\n                    # allow for skipping\n                    if not person:\n                        continue\n\n                    person.add_source(self.members_url)\n                    person.add_link(self.members_url)\n                    yield person\n", "path": "openstates/nh/people.py"}], "after_files": [{"content": "import re\n\nfrom pupa.scrape import Person, Scraper\nfrom openstates.utils import LXMLMixin\n\n\nclass NHPersonScraper(Scraper, LXMLMixin):\n    members_url = 'http://www.gencourt.state.nh.us/downloads/Members.txt'\n    lookup_url = 'http://www.gencourt.state.nh.us/house/members/memberlookup.aspx'\n    house_profile_url = 'http://www.gencourt.state.nh.us/house/members/member.aspx?member={}'\n    senate_profile_url = 'http://www.gencourt.state.nh.us/Senate/members/webpages/district{}.aspx'\n\n    chamber_map = {'H': 'lower', 'S': 'upper'}\n    party_map = {\n        'D': 'Democratic',\n        'R': 'Republican',\n        'I': 'Independent',\n        'L': 'Libertarian',\n    }\n\n    def _get_photo(self, url, chamber):\n        \"\"\"Attempts to find a portrait in the given legislator profile.\"\"\"\n        doc = self.lxmlize(url)\n\n        if chamber == 'upper':\n            src = doc.xpath('//div[@id=\"page_content\"]//img[contains(@src, '\n                            '\"images/senators\") or contains(@src, \"Senator\")]/@src')\n        elif chamber == 'lower':\n            src = doc.xpath('//img[contains(@src, \"images/memberpics\")]/@src')\n\n        if src and 'nophoto' not in src[0]:\n            photo_url = src[0]\n        else:\n            photo_url = ''\n\n        return photo_url\n\n    def _parse_person(self, row, chamber, seat_map):\n        # Capture legislator vitals.\n        first_name = row['FirstName']\n        middle_name = row['MiddleName']\n        last_name = row['LastName']\n        full_name = '{} {} {}'.format(first_name, middle_name, last_name)\n        full_name = re.sub(r'[\\s]{2,}', ' ', full_name)\n\n        if chamber == 'lower':\n            district = '{} {}'.format(row['County'], int(row['District'])).strip()\n        else:\n            district = str(int(row['District'])).strip()\n\n        party = self.party_map[row['party'].upper()]\n        email = row['WorkEmail']\n\n        if district == '0':\n            self.warning('Skipping {}, district is set to 0'.format(full_name))\n            return\n\n        # Temporary fix for Kari Lerner\n        if district == 'Rockingham 0' and last_name == 'Lerner':\n            district = 'Rockingham 4'\n\n        # Temporary fix for Casey Conley\n        if last_name == 'Conley':\n            if district == '13':\n                district = 'Strafford 13'\n            elif district == 'Strafford 13':\n                self.info('\"Temporary fix for Casey Conley\" can be removed')\n\n        person = Person(primary_org=chamber,\n                        district=district,\n                        name=full_name,\n                        party=party)\n\n        extras = {\n            'first_name': first_name,\n            'middle_name': middle_name,\n            'last_name': last_name\n        }\n\n        person.extras = extras\n        if email:\n            person.add_contact_detail(type='email', value=email, note='District Office')\n\n        # Capture legislator office contact information.\n        district_address = '{}\\n{}\\n{}, {} {}'.format(row['Address'],\n                                                      row['address2'],\n                                                      row['city'], row['State'],\n                                                      row['Zipcode']).strip()\n\n        phone = row['Phone'].strip()\n        if not phone:\n            phone = None\n\n        if district_address:\n            person.add_contact_detail(type='address', value=district_address, note='Home Office')\n        if phone:\n            person.add_contact_detail(type='voice', value=phone, note='Home Office')\n\n        # Retrieve legislator portrait.\n        profile_url = None\n        if chamber == 'upper':\n            profile_url = self.senate_profile_url.format(row['District'])\n        elif chamber == 'lower':\n            try:\n                seat_number = seat_map[row['seatno']]\n                profile_url = self.house_profile_url.format(seat_number)\n            except KeyError:\n                pass\n\n        if profile_url:\n            person.image = self._get_photo(profile_url, chamber)\n            person.add_source(profile_url)\n\n        return person\n\n    def _parse_members_txt(self):\n        lines = self.get(self.members_url).text.splitlines()\n\n        header = lines[0].split('\\t')\n\n        for line in lines[1:]:\n            yield dict(zip(header, line.split('\\t')))\n\n    def _parse_seat_map(self):\n        \"\"\"Get mapping between seat numbers and legislator identifiers.\"\"\"\n        seat_map = {}\n        page = self.lxmlize(self.lookup_url)\n        options = page.xpath('//select[@id=\"member\"]/option')\n        for option in options:\n            member_url = self.house_profile_url.format(option.attrib['value'])\n            member_page = self.lxmlize(member_url)\n            table = member_page.xpath('//table[@id=\"Table1\"]')\n            if table:\n                res = re.search(r'seat #:(\\d+)', table[0].text_content(), re.IGNORECASE)\n                if res:\n                    seat_map[res.groups()[0]] = option.attrib['value']\n        return seat_map\n\n    def scrape(self, chamber=None):\n        chambers = [chamber] if chamber is not None else ['upper', 'lower']\n        seat_map = self._parse_seat_map()\n        for chamber in chambers:\n            for row in self._parse_members_txt():\n                print(row['electedStatus'])\n                if (self.chamber_map[row['LegislativeBody']] == chamber and\n                        row['electedStatus'] != 'Former'):\n                    person = self._parse_person(row, chamber, seat_map)\n\n                    # allow for skipping\n                    if not person:\n                        continue\n\n                    person.add_source(self.members_url)\n                    person.add_link(self.members_url)\n                    yield person\n", "path": "openstates/nh/people.py"}]}