Files
crawler/cheapestoil/town_lookup.py
Edwin Eames 1592e6d685 refactor: replace fuel_scraper with newenglandoil + cheapestoil scrapers
- Add newenglandoil/ package as the primary scraper (replaces fuel_scraper)
- Add cheapestoil/ package as a secondary market price scraper
- Add app.py entry point for direct execution
- Update run.py: new scrape_cheapest(), migrate command, --state filter,
  --refresh-metadata flag for overwriting existing phone/URL data
- Update models.py with latest schema fields
- Update requirements.txt dependencies
- Update Dockerfile and docker-compose.yml for new structure
- Remove deprecated fuel_scraper module, test.py, and log file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 11:34:21 -05:00

1587 lines
50 KiB
Python

"""
Town-to-county mapping for all 6 New England states.
Used to resolve county from service_area text when CheapestOil returns
state-level data (NH, RI, VT) without county filtering.
For MA/CT/ME, the API has county-level pages so this is only needed as fallback.
"""
import re
import logging
# County names for direct mention matching (e.g. "Throughout Rockingham County")
_NE_COUNTIES = {
"CT": ["Fairfield", "Hartford", "Litchfield", "Middlesex", "New Haven",
"New London", "Tolland", "Windham"],
"MA": ["Barnstable", "Berkshire", "Bristol", "Dukes", "Essex", "Franklin",
"Hampden", "Hampshire", "Middlesex", "Nantucket", "Norfolk",
"Plymouth", "Suffolk", "Worcester"],
"ME": ["Androscoggin", "Aroostook", "Cumberland", "Franklin", "Hancock",
"Kennebec", "Knox", "Lincoln", "Oxford", "Penobscot", "Piscataquis",
"Sagadahoc", "Somerset", "Waldo", "Washington", "York"],
"NH": ["Belknap", "Carroll", "Cheshire", "Coos", "Grafton", "Hillsborough",
"Merrimack", "Rockingham", "Strafford", "Sullivan"],
"RI": ["Bristol", "Kent", "Newport", "Providence", "Washington"],
"VT": ["Addison", "Bennington", "Caledonia", "Chittenden", "Essex",
"Franklin", "Grand Isle", "Lamoille", "Orange", "Orleans",
"Rutland", "Washington", "Windham", "Windsor"],
}
# Town name (lowercase) -> County name, organized by state
TOWN_COUNTY_MAP = {
"CT": {
"andover": "Tolland",
"ansonia": "New Haven",
"ashford": "Windham",
"avon": "Hartford",
"barkhamsted": "Litchfield",
"beacon falls": "New Haven",
"berlin": "Hartford",
"bethany": "New Haven",
"bethel": "Fairfield",
"bethlehem": "Litchfield",
"bloomfield": "Hartford",
"bolton": "Tolland",
"bozrah": "New London",
"branford": "New Haven",
"bridgeport": "Fairfield",
"bridgewater": "Litchfield",
"bristol": "Hartford",
"brookfield": "Fairfield",
"brooklyn": "Windham",
"burlington": "Hartford",
"canaan": "Litchfield",
"canterbury": "Windham",
"canton": "Hartford",
"chaplin": "Windham",
"cheshire": "New Haven",
"chester": "Middlesex",
"clinton": "Middlesex",
"colchester": "New London",
"colebrook": "Litchfield",
"columbia": "Tolland",
"cornwall": "Litchfield",
"coventry": "Tolland",
"cromwell": "Middlesex",
"danbury": "Fairfield",
"darien": "Fairfield",
"deep river": "Middlesex",
"derby": "New Haven",
"durham": "Middlesex",
"east granby": "Hartford",
"east haddam": "Middlesex",
"east hampton": "Middlesex",
"east hartford": "Hartford",
"east haven": "New Haven",
"east lyme": "New London",
"east windsor": "Hartford",
"eastford": "Windham",
"easton": "Fairfield",
"ellington": "Tolland",
"enfield": "Hartford",
"essex": "Middlesex",
"fairfield": "Fairfield",
"farmington": "Hartford",
"franklin": "New London",
"glastonbury": "Hartford",
"goshen": "Litchfield",
"granby": "Hartford",
"greenwich": "Fairfield",
"griswold": "New London",
"groton": "New London",
"guilford": "New Haven",
"haddam": "Middlesex",
"hamden": "New Haven",
"hampton": "Windham",
"hartford": "Hartford",
"hartland": "Hartford",
"harwinton": "Litchfield",
"hebron": "Tolland",
"kent": "Litchfield",
"killingly": "Windham",
"killingworth": "Middlesex",
"lebanon": "New London",
"ledyard": "New London",
"lisbon": "New London",
"litchfield": "Litchfield",
"lyme": "New London",
"madison": "New Haven",
"manchester": "Hartford",
"mansfield": "Tolland",
"marlborough": "Hartford",
"meriden": "New Haven",
"middlebury": "New Haven",
"middlefield": "Middlesex",
"middletown": "Middlesex",
"milford": "New Haven",
"monroe": "Fairfield",
"montville": "New London",
"morris": "Litchfield",
"naugatuck": "New Haven",
"new britain": "Hartford",
"new canaan": "Fairfield",
"new fairfield": "Fairfield",
"new hartford": "Litchfield",
"new haven": "New Haven",
"new london": "New London",
"new milford": "Litchfield",
"newington": "Hartford",
"newtown": "Fairfield",
"norfolk": "Litchfield",
"north branford": "New Haven",
"north canaan": "Litchfield",
"north haven": "New Haven",
"north stonington": "New London",
"norwalk": "Fairfield",
"norwich": "New London",
"old lyme": "New London",
"old saybrook": "Middlesex",
"orange": "New Haven",
"oxford": "New Haven",
"plainfield": "Windham",
"plainville": "Hartford",
"plymouth": "Litchfield",
"pomfret": "Windham",
"portland": "Middlesex",
"preston": "New London",
"prospect": "New Haven",
"putnam": "Windham",
"redding": "Fairfield",
"ridgefield": "Fairfield",
"rocky hill": "Hartford",
"roxbury": "Litchfield",
"salem": "New London",
"salisbury": "Litchfield",
"scotland": "Windham",
"seymour": "New Haven",
"sharon": "Litchfield",
"shelton": "Fairfield",
"sherman": "Fairfield",
"simsbury": "Hartford",
"somers": "Tolland",
"south windsor": "Hartford",
"southbury": "New Haven",
"southington": "Hartford",
"sprague": "New London",
"stafford": "Tolland",
"stamford": "Fairfield",
"sterling": "Windham",
"stonington": "New London",
"stratford": "Fairfield",
"suffield": "Hartford",
"thomaston": "Litchfield",
"thompson": "Windham",
"tolland": "Tolland",
"torrington": "Litchfield",
"trumbull": "Fairfield",
"union": "Tolland",
"vernon": "Tolland",
"voluntown": "New London",
"wallingford": "New Haven",
"warren": "Litchfield",
"washington": "Litchfield",
"waterbury": "New Haven",
"waterford": "New London",
"watertown": "Litchfield",
"west hartford": "Hartford",
"west haven": "New Haven",
"westbrook": "Middlesex",
"weston": "Fairfield",
"westport": "Fairfield",
"wethersfield": "Hartford",
"willington": "Tolland",
"wilton": "Fairfield",
"winchester": "Litchfield",
"windham": "Windham",
"windsor": "Hartford",
"windsor locks": "Hartford",
"wolcott": "New Haven",
"woodbridge": "New Haven",
"woodbury": "Litchfield",
"woodstock": "Windham",
},
"MA": {
"abington": "Plymouth",
"acton": "Middlesex",
"acushnet": "Bristol",
"adams": "Berkshire",
"agawam": "Hampden",
"alford": "Berkshire",
"amesbury": "Essex",
"amherst": "Hampshire",
"andover": "Essex",
"arlington": "Middlesex",
"ashburnham": "Worcester",
"ashby": "Middlesex",
"ashfield": "Franklin",
"ashland": "Middlesex",
"athol": "Worcester",
"attleboro": "Bristol",
"auburn": "Worcester",
"avon": "Norfolk",
"ayer": "Middlesex",
"barnstable": "Barnstable",
"barre": "Worcester",
"becket": "Berkshire",
"bedford": "Middlesex",
"belchertown": "Hampshire",
"bellingham": "Norfolk",
"belmont": "Middlesex",
"berkley": "Bristol",
"berlin": "Worcester",
"bernardston": "Franklin",
"beverly": "Essex",
"billerica": "Middlesex",
"blackstone": "Worcester",
"blandford": "Hampden",
"bolton": "Worcester",
"boston": "Suffolk",
"bourne": "Barnstable",
"boxborough": "Middlesex",
"boxford": "Essex",
"boylston": "Worcester",
"braintree": "Norfolk",
"brewster": "Barnstable",
"bridgewater": "Plymouth",
"brimfield": "Hampden",
"brockton": "Plymouth",
"brookfield": "Worcester",
"brookline": "Norfolk",
"buckland": "Franklin",
"burlington": "Middlesex",
"cambridge": "Middlesex",
"canton": "Norfolk",
"carlisle": "Middlesex",
"carver": "Plymouth",
"charlemont": "Franklin",
"charlton": "Worcester",
"chatham": "Barnstable",
"chelmsford": "Middlesex",
"chelsea": "Suffolk",
"cheshire": "Berkshire",
"chester": "Hampden",
"chesterfield": "Hampshire",
"chicopee": "Hampden",
"chilmark": "Dukes",
"clarksburg": "Berkshire",
"clinton": "Worcester",
"cohasset": "Norfolk",
"colrain": "Franklin",
"concord": "Middlesex",
"conway": "Franklin",
"cummington": "Hampshire",
"dalton": "Berkshire",
"danvers": "Essex",
"dartmouth": "Bristol",
"dedham": "Norfolk",
"deerfield": "Franklin",
"dennis": "Barnstable",
"dighton": "Bristol",
"douglas": "Worcester",
"dover": "Norfolk",
"dracut": "Middlesex",
"dudley": "Worcester",
"dunstable": "Middlesex",
"duxbury": "Plymouth",
"east bridgewater": "Plymouth",
"east brookfield": "Worcester",
"east longmeadow": "Hampden",
"eastham": "Barnstable",
"easthampton": "Hampshire",
"easton": "Bristol",
"edgartown": "Dukes",
"egremont": "Berkshire",
"erving": "Franklin",
"essex": "Essex",
"everett": "Middlesex",
"fairhaven": "Bristol",
"fall river": "Bristol",
"falmouth": "Barnstable",
"fitchburg": "Worcester",
"florida": "Berkshire",
"foxborough": "Norfolk",
"framingham": "Middlesex",
"franklin": "Norfolk",
"freetown": "Bristol",
"gardner": "Worcester",
"georgetown": "Essex",
"gill": "Franklin",
"gloucester": "Essex",
"goshen": "Hampshire",
"gosnold": "Dukes",
"grafton": "Worcester",
"granby": "Hampshire",
"granville": "Hampden",
"great barrington": "Berkshire",
"greenfield": "Franklin",
"groton": "Middlesex",
"groveland": "Essex",
"hadley": "Hampshire",
"halifax": "Plymouth",
"hamilton": "Essex",
"hampden": "Hampden",
"hancock": "Berkshire",
"hanover": "Plymouth",
"hanson": "Plymouth",
"hardwick": "Worcester",
"harvard": "Worcester",
"harwich": "Barnstable",
"hatfield": "Hampshire",
"haverhill": "Essex",
"hawley": "Franklin",
"heath": "Franklin",
"hingham": "Plymouth",
"hinsdale": "Berkshire",
"holbrook": "Norfolk",
"holden": "Worcester",
"holland": "Hampden",
"holliston": "Middlesex",
"holyoke": "Hampden",
"hopedale": "Worcester",
"hopkinton": "Middlesex",
"hubbardston": "Worcester",
"hudson": "Middlesex",
"hull": "Plymouth",
"huntington": "Hampshire",
"ipswich": "Essex",
"kingston": "Plymouth",
"lakeville": "Plymouth",
"lancaster": "Worcester",
"lanesborough": "Berkshire",
"lawrence": "Essex",
"lee": "Berkshire",
"leicester": "Worcester",
"lenox": "Berkshire",
"leominster": "Worcester",
"leverett": "Franklin",
"lexington": "Middlesex",
"leyden": "Franklin",
"lincoln": "Middlesex",
"littleton": "Middlesex",
"longmeadow": "Hampden",
"lowell": "Middlesex",
"ludlow": "Hampden",
"lunenburg": "Worcester",
"lynn": "Essex",
"lynnfield": "Essex",
"malden": "Middlesex",
"manchester-by-the-sea": "Essex",
"manchester": "Essex",
"mansfield": "Bristol",
"marblehead": "Essex",
"marion": "Plymouth",
"marlborough": "Middlesex",
"marshfield": "Plymouth",
"mashpee": "Barnstable",
"mattapoisett": "Plymouth",
"maynard": "Middlesex",
"medfield": "Norfolk",
"medford": "Middlesex",
"medway": "Norfolk",
"melrose": "Middlesex",
"mendon": "Worcester",
"merrimac": "Essex",
"methuen": "Essex",
"middleborough": "Plymouth",
"middlefield": "Hampshire",
"middleton": "Essex",
"milford": "Worcester",
"millbury": "Worcester",
"millis": "Norfolk",
"millville": "Worcester",
"milton": "Norfolk",
"monroe": "Franklin",
"monson": "Hampden",
"montague": "Franklin",
"monterey": "Berkshire",
"montgomery": "Hampden",
"mount washington": "Berkshire",
"nahant": "Essex",
"nantucket": "Nantucket",
"natick": "Middlesex",
"needham": "Norfolk",
"new ashford": "Berkshire",
"new bedford": "Bristol",
"new braintree": "Worcester",
"new marlborough": "Berkshire",
"new salem": "Franklin",
"newbury": "Essex",
"newburyport": "Essex",
"newton": "Middlesex",
"norfolk": "Norfolk",
"north adams": "Berkshire",
"north andover": "Essex",
"north attleborough": "Bristol",
"north brookfield": "Worcester",
"north reading": "Middlesex",
"northampton": "Hampshire",
"northborough": "Worcester",
"northbridge": "Worcester",
"northfield": "Franklin",
"norton": "Bristol",
"norwell": "Plymouth",
"norwood": "Norfolk",
"oak bluffs": "Dukes",
"oakham": "Worcester",
"orange": "Franklin",
"orleans": "Barnstable",
"otis": "Berkshire",
"oxford": "Worcester",
"palmer": "Hampden",
"paxton": "Worcester",
"peabody": "Essex",
"pelham": "Hampshire",
"pembroke": "Plymouth",
"pepperell": "Middlesex",
"peru": "Berkshire",
"petersham": "Worcester",
"phillipston": "Worcester",
"pittsfield": "Berkshire",
"plainfield": "Hampshire",
"plainville": "Norfolk",
"plymouth": "Plymouth",
"plympton": "Plymouth",
"princeton": "Worcester",
"provincetown": "Barnstable",
"quincy": "Norfolk",
"randolph": "Norfolk",
"raynham": "Bristol",
"reading": "Middlesex",
"rehoboth": "Bristol",
"revere": "Suffolk",
"richmond": "Berkshire",
"rochester": "Plymouth",
"rockland": "Plymouth",
"rockport": "Essex",
"rowe": "Franklin",
"rowley": "Essex",
"royalston": "Worcester",
"russell": "Hampden",
"rutland": "Worcester",
"salem": "Essex",
"salisbury": "Essex",
"sandisfield": "Berkshire",
"sandwich": "Barnstable",
"saugus": "Essex",
"savoy": "Berkshire",
"scituate": "Plymouth",
"seekonk": "Bristol",
"sharon": "Norfolk",
"sheffield": "Berkshire",
"shelburne": "Franklin",
"sherborn": "Middlesex",
"shirley": "Middlesex",
"shrewsbury": "Worcester",
"shutesbury": "Franklin",
"somerset": "Bristol",
"somerville": "Middlesex",
"south hadley": "Hampshire",
"southampton": "Hampshire",
"southborough": "Worcester",
"southbridge": "Worcester",
"southwick": "Hampden",
"spencer": "Worcester",
"springfield": "Hampden",
"sterling": "Worcester",
"stockbridge": "Berkshire",
"stoneham": "Middlesex",
"stoughton": "Norfolk",
"stow": "Middlesex",
"sturbridge": "Worcester",
"sudbury": "Middlesex",
"sunderland": "Franklin",
"sutton": "Worcester",
"swampscott": "Essex",
"swansea": "Bristol",
"taunton": "Bristol",
"templeton": "Worcester",
"tewksbury": "Middlesex",
"tisbury": "Dukes",
"tolland": "Hampden",
"topsfield": "Essex",
"townsend": "Middlesex",
"truro": "Barnstable",
"tyngsborough": "Middlesex",
"tyringham": "Berkshire",
"upton": "Worcester",
"uxbridge": "Worcester",
"wakefield": "Middlesex",
"wales": "Hampden",
"walpole": "Norfolk",
"waltham": "Middlesex",
"ware": "Hampshire",
"wareham": "Plymouth",
"warren": "Worcester",
"warwick": "Franklin",
"washington": "Berkshire",
"watertown": "Middlesex",
"wayland": "Middlesex",
"webster": "Worcester",
"wellesley": "Norfolk",
"wellfleet": "Barnstable",
"wendell": "Franklin",
"wenham": "Essex",
"west boylston": "Worcester",
"west bridgewater": "Plymouth",
"west brookfield": "Worcester",
"west newbury": "Essex",
"west springfield": "Hampden",
"west stockbridge": "Berkshire",
"west tisbury": "Dukes",
"westborough": "Worcester",
"westfield": "Hampden",
"westford": "Middlesex",
"westhampton": "Hampshire",
"westminster": "Worcester",
"weston": "Middlesex",
"westport": "Bristol",
"westwood": "Norfolk",
"weymouth": "Norfolk",
"whately": "Franklin",
"whitman": "Plymouth",
"wilbraham": "Hampden",
"williamsburg": "Hampshire",
"williamstown": "Berkshire",
"wilmington": "Middlesex",
"winchendon": "Worcester",
"winchester": "Middlesex",
"windsor": "Berkshire",
"winthrop": "Suffolk",
"woburn": "Middlesex",
"worcester": "Worcester",
"worthington": "Hampshire",
"wrentham": "Norfolk",
"yarmouth": "Barnstable",
},
"ME": {
"auburn": "Androscoggin",
"durham": "Androscoggin",
"greene": "Androscoggin",
"leeds": "Androscoggin",
"lewiston": "Androscoggin",
"lisbon": "Androscoggin",
"livermore": "Androscoggin",
"livermore falls": "Androscoggin",
"mechanic falls": "Androscoggin",
"minot": "Androscoggin",
"poland": "Androscoggin",
"sabattus": "Androscoggin",
"turner": "Androscoggin",
"wales": "Androscoggin",
"allagash": "Aroostook",
"amity": "Aroostook",
"ashland": "Aroostook",
"bancroft": "Aroostook",
"blaine": "Aroostook",
"bridgewater": "Aroostook",
"caribou": "Aroostook",
"castle hill": "Aroostook",
"caswell": "Aroostook",
"chapman": "Aroostook",
"connor": "Aroostook",
"crystal": "Aroostook",
"dyer brook": "Aroostook",
"eagle lake": "Aroostook",
"easton": "Aroostook",
"fort fairfield": "Aroostook",
"fort kent": "Aroostook",
"frenchville": "Aroostook",
"grand isle": "Aroostook",
"hamlin": "Aroostook",
"haynesville": "Aroostook",
"hersey": "Aroostook",
"hodgdon": "Aroostook",
"houlton": "Aroostook",
"island falls": "Aroostook",
"limestone": "Aroostook",
"linneus": "Aroostook",
"littleton": "Aroostook",
"ludlow": "Aroostook",
"madawaska": "Aroostook",
"mapleton": "Aroostook",
"mars hill": "Aroostook",
"masardis": "Aroostook",
"merrill": "Aroostook",
"monticello": "Aroostook",
"new canada": "Aroostook",
"new limerick": "Aroostook",
"new sweden": "Aroostook",
"oakfield": "Aroostook",
"orient": "Aroostook",
"perham": "Aroostook",
"portage lake": "Aroostook",
"presque isle": "Aroostook",
"saint agatha": "Aroostook",
"saint francis": "Aroostook",
"sherman": "Aroostook",
"smyrna": "Aroostook",
"stockholm": "Aroostook",
"van buren": "Aroostook",
"wade": "Aroostook",
"wallagrass": "Aroostook",
"washburn": "Aroostook",
"westfield": "Aroostook",
"weston": "Aroostook",
"woodland": "Aroostook",
"baldwin": "Cumberland",
"bridgton": "Cumberland",
"brunswick": "Cumberland",
"cape elizabeth": "Cumberland",
"casco": "Cumberland",
"chebeague island": "Cumberland",
"cumberland": "Cumberland",
"falmouth": "Cumberland",
"freeport": "Cumberland",
"frye island": "Cumberland",
"gorham": "Cumberland",
"gray": "Cumberland",
"harpswell": "Cumberland",
"harrison": "Cumberland",
"long island": "Cumberland",
"naples": "Cumberland",
"new gloucester": "Cumberland",
"north yarmouth": "Cumberland",
"portland": "Cumberland",
"pownal": "Cumberland",
"raymond": "Cumberland",
"scarborough": "Cumberland",
"sebago": "Cumberland",
"south portland": "Cumberland",
"standish": "Cumberland",
"westbrook": "Cumberland",
"windham": "Cumberland",
"yarmouth": "Cumberland",
"avon": "Franklin",
"carrabassett valley": "Franklin",
"carthage": "Franklin",
"chesterville": "Franklin",
"eustis": "Franklin",
"farmington": "Franklin",
"industry": "Franklin",
"jay": "Franklin",
"kingfield": "Franklin",
"new sharon": "Franklin",
"new vineyard": "Franklin",
"phillips": "Franklin",
"rangeley": "Franklin",
"strong": "Franklin",
"temple": "Franklin",
"weld": "Franklin",
"wilton": "Franklin",
"amherst": "Hancock",
"aurora": "Hancock",
"bar harbor": "Hancock",
"blue hill": "Hancock",
"brooklin": "Hancock",
"brooksville": "Hancock",
"bucksport": "Hancock",
"castine": "Hancock",
"cranberry isles": "Hancock",
"dedham": "Hancock",
"deer isle": "Hancock",
"eastbrook": "Hancock",
"ellsworth": "Hancock",
"franklin": "Hancock",
"frenchboro": "Hancock",
"gouldsboro": "Hancock",
"hancock": "Hancock",
"lamoine": "Hancock",
"mariaville": "Hancock",
"mount desert": "Hancock",
"orland": "Hancock",
"otis": "Hancock",
"penobscot": "Hancock",
"sedgwick": "Hancock",
"sorrento": "Hancock",
"southwest harbor": "Hancock",
"stonington": "Hancock",
"sullivan": "Hancock",
"surry": "Hancock",
"swans island": "Hancock",
"tremont": "Hancock",
"trenton": "Hancock",
"verona island": "Hancock",
"waltham": "Hancock",
"winter harbor": "Hancock",
"albion": "Kennebec",
"augusta": "Kennebec",
"belgrade": "Kennebec",
"benton": "Kennebec",
"chelsea": "Kennebec",
"china": "Kennebec",
"clinton": "Kennebec",
"farmingdale": "Kennebec",
"fayette": "Kennebec",
"gardiner": "Kennebec",
"hallowell": "Kennebec",
"litchfield": "Kennebec",
"manchester": "Kennebec",
"monmouth": "Kennebec",
"mount vernon": "Kennebec",
"oakland": "Kennebec",
"pittston": "Kennebec",
"randolph": "Kennebec",
"readfield": "Kennebec",
"rome": "Kennebec",
"sidney": "Kennebec",
"vassalboro": "Kennebec",
"vienna": "Kennebec",
"waterville": "Kennebec",
"wayne": "Kennebec",
"west gardiner": "Kennebec",
"windsor": "Kennebec",
"winslow": "Kennebec",
"winthrop": "Kennebec",
"appleton": "Knox",
"camden": "Knox",
"cushing": "Knox",
"friendship": "Knox",
"hope": "Knox",
"isle au haut": "Knox",
"north haven": "Knox",
"owls head": "Knox",
"rockland": "Knox",
"rockport": "Knox",
"saint george": "Knox",
"south thomaston": "Knox",
"thomaston": "Knox",
"union": "Knox",
"vinalhaven": "Knox",
"warren": "Knox",
"washington": "Knox",
"alna": "Lincoln",
"boothbay": "Lincoln",
"boothbay harbor": "Lincoln",
"bremen": "Lincoln",
"bristol": "Lincoln",
"damariscotta": "Lincoln",
"dresden": "Lincoln",
"edgecomb": "Lincoln",
"jefferson": "Lincoln",
"newcastle": "Lincoln",
"nobleboro": "Lincoln",
"somerville": "Lincoln",
"south bristol": "Lincoln",
"southport": "Lincoln",
"waldoboro": "Lincoln",
"westport island": "Lincoln",
"whitefield": "Lincoln",
"wiscasset": "Lincoln",
"albany": "Oxford",
"andover": "Oxford",
"bethel": "Oxford",
"brownfield": "Oxford",
"buckfield": "Oxford",
"byron": "Oxford",
"canton": "Oxford",
"denmark": "Oxford",
"dixfield": "Oxford",
"fryeburg": "Oxford",
"gilead": "Oxford",
"greenwood": "Oxford",
"hanover": "Oxford",
"hartford": "Oxford",
"hebron": "Oxford",
"hiram": "Oxford",
"lovell": "Oxford",
"mexico": "Oxford",
"newry": "Oxford",
"norway": "Oxford",
"oxford": "Oxford",
"paris": "Oxford",
"peru": "Oxford",
"porter": "Oxford",
"roxbury": "Oxford",
"rumford": "Oxford",
"stoneham": "Oxford",
"stow": "Oxford",
"sumner": "Oxford",
"sweden": "Oxford",
"upton": "Oxford",
"waterford": "Oxford",
"west paris": "Oxford",
"woodstock": "Oxford",
"milton": "Oxford",
"alton": "Penobscot",
"bangor": "Penobscot",
"bradford": "Penobscot",
"bradley": "Penobscot",
"brewer": "Penobscot",
"burlington": "Penobscot",
"carmel": "Penobscot",
"charleston": "Penobscot",
"chester": "Penobscot",
"clifton": "Penobscot",
"corinna": "Penobscot",
"corinth": "Penobscot",
"dexter": "Penobscot",
"dixmont": "Penobscot",
"east millinocket": "Penobscot",
"eddington": "Penobscot",
"edinburg": "Penobscot",
"enfield": "Penobscot",
"etna": "Penobscot",
"exeter": "Penobscot",
"garland": "Penobscot",
"glenburn": "Penobscot",
"greenbush": "Penobscot",
"greenfield": "Penobscot",
"hampden": "Penobscot",
"holden": "Penobscot",
"howland": "Penobscot",
"hudson": "Penobscot",
"kenduskeag": "Penobscot",
"lagrange": "Penobscot",
"lee": "Penobscot",
"levant": "Penobscot",
"lincoln": "Penobscot",
"lowell": "Penobscot",
"mattawamkeag": "Penobscot",
"maxfield": "Penobscot",
"medway": "Penobscot",
"milford": "Penobscot",
"millinocket": "Penobscot",
"newburgh": "Penobscot",
"newport": "Penobscot",
"old town": "Penobscot",
"orono": "Penobscot",
"orrington": "Penobscot",
"passadumkeag": "Penobscot",
"patten": "Penobscot",
"plymouth": "Penobscot",
"prentiss": "Penobscot",
"stetson": "Penobscot",
"springfield": "Penobscot",
"stacyville": "Penobscot",
"veazie": "Penobscot",
"winn": "Penobscot",
"woodville": "Penobscot",
"mount chase": "Penobscot",
"abbot": "Piscataquis",
"atkinson": "Piscataquis",
"beaver cove": "Piscataquis",
"bowerbank": "Piscataquis",
"brownville": "Piscataquis",
"dover-foxcroft": "Piscataquis",
"greenville": "Piscataquis",
"guilford": "Piscataquis",
"medford": "Piscataquis",
"milo": "Piscataquis",
"monson": "Piscataquis",
"parkman": "Piscataquis",
"sangerville": "Piscataquis",
"sebec": "Piscataquis",
"shirley": "Piscataquis",
"wellington": "Piscataquis",
"willimantic": "Piscataquis",
"arrowsic": "Sagadahoc",
"bath": "Sagadahoc",
"bowdoin": "Sagadahoc",
"bowdoinham": "Sagadahoc",
"georgetown": "Sagadahoc",
"phippsburg": "Sagadahoc",
"richmond": "Sagadahoc",
"topsham": "Sagadahoc",
"west bath": "Sagadahoc",
"woolwich": "Sagadahoc",
"anson": "Somerset",
"athens": "Somerset",
"bingham": "Somerset",
"cambridge": "Somerset",
"canaan": "Somerset",
"caratunk": "Somerset",
"cornville": "Somerset",
"detroit": "Somerset",
"embden": "Somerset",
"fairfield": "Somerset",
"harmony": "Somerset",
"hartland": "Somerset",
"jackman": "Somerset",
"madison": "Somerset",
"mercer": "Somerset",
"moscow": "Somerset",
"new portland": "Somerset",
"norridgewock": "Somerset",
"palmyra": "Somerset",
"pittsfield": "Somerset",
"ripley": "Somerset",
"saint albans": "Somerset",
"skowhegan": "Somerset",
"smithfield": "Somerset",
"solon": "Somerset",
"starks": "Somerset",
"belfast": "Waldo",
"belmont": "Waldo",
"brooks": "Waldo",
"burnham": "Waldo",
"frankfort": "Waldo",
"freedom": "Waldo",
"islesboro": "Waldo",
"jackson": "Waldo",
"knox": "Waldo",
"liberty": "Waldo",
"lincolnville": "Waldo",
"monroe": "Waldo",
"montville": "Waldo",
"morrill": "Waldo",
"northport": "Waldo",
"palermo": "Waldo",
"prospect": "Waldo",
"searsmont": "Waldo",
"searsport": "Waldo",
"stockton springs": "Waldo",
"swanville": "Waldo",
"thorndike": "Waldo",
"troy": "Waldo",
"unity": "Waldo",
"waldo": "Waldo",
"winterport": "Waldo",
"addison": "Washington",
"alexander": "Washington",
"baileyville": "Washington",
"beals": "Washington",
"beddington": "Washington",
"calais": "Washington",
"centerville": "Washington",
"charlotte": "Washington",
"cherryfield": "Washington",
"columbia": "Washington",
"columbia falls": "Washington",
"cooper": "Washington",
"crawford": "Washington",
"cutler": "Washington",
"danforth": "Washington",
"deblois": "Washington",
"dennysville": "Washington",
"east machias": "Washington",
"eastport": "Washington",
"harrington": "Washington",
"jonesboro": "Washington",
"jonesport": "Washington",
"lubec": "Washington",
"machias": "Washington",
"machiasport": "Washington",
"marion": "Washington",
"marshfield": "Washington",
"meddybemps": "Washington",
"milbridge": "Washington",
"northfield": "Washington",
"pembroke": "Washington",
"perry": "Washington",
"princeton": "Washington",
"robbinston": "Washington",
"roque bluffs": "Washington",
"steuben": "Washington",
"talmadge": "Washington",
"topsfield": "Washington",
"vanceboro": "Washington",
"waite": "Washington",
"wesley": "Washington",
"whiting": "Washington",
"whitneyville": "Washington",
"acton": "York",
"alfred": "York",
"arundel": "York",
"berwick": "York",
"biddeford": "York",
"buxton": "York",
"cornish": "York",
"dayton": "York",
"eliot": "York",
"hollis": "York",
"kennebunk": "York",
"kennebunkport": "York",
"kittery": "York",
"lebanon": "York",
"limerick": "York",
"limington": "York",
"lyman": "York",
"newfield": "York",
"north berwick": "York",
"ogunquit": "York",
"old orchard beach": "York",
"parsonsfield": "York",
"saco": "York",
"sanford": "York",
"shapleigh": "York",
"south berwick": "York",
"waterboro": "York",
"wells": "York",
"york": "York",
},
"NH": {
"acworth": "Sullivan",
"albany": "Carroll",
"alexandria": "Grafton",
"allenstown": "Merrimack",
"alstead": "Cheshire",
"alton": "Belknap",
"amherst": "Hillsborough",
"andover": "Merrimack",
"antrim": "Hillsborough",
"ashland": "Grafton",
"atkinson": "Rockingham",
"auburn": "Rockingham",
"barnstead": "Belknap",
"barrington": "Strafford",
"bartlett": "Carroll",
"bath": "Grafton",
"bedford": "Hillsborough",
"belmont": "Belknap",
"bennington": "Hillsborough",
"benton": "Grafton",
"berlin": "Coos",
"bethlehem": "Grafton",
"boscawen": "Merrimack",
"bow": "Merrimack",
"bradford": "Merrimack",
"brentwood": "Rockingham",
"bridgewater": "Grafton",
"bristol": "Grafton",
"brookfield": "Carroll",
"brookline": "Hillsborough",
"campton": "Grafton",
"canaan": "Grafton",
"candia": "Rockingham",
"canterbury": "Merrimack",
"carroll": "Coos",
"center harbor": "Belknap",
"charlestown": "Sullivan",
"chatham": "Carroll",
"chester": "Rockingham",
"chesterfield": "Cheshire",
"chichester": "Merrimack",
"claremont": "Sullivan",
"clarksville": "Coos",
"colebrook": "Coos",
"columbia": "Coos",
"concord": "Merrimack",
"conway": "Carroll",
"cornish": "Sullivan",
"croydon": "Sullivan",
"dalton": "Coos",
"danbury": "Merrimack",
"danville": "Rockingham",
"deerfield": "Rockingham",
"deering": "Hillsborough",
"derry": "Rockingham",
"dorchester": "Grafton",
"dover": "Strafford",
"dublin": "Cheshire",
"dummer": "Coos",
"dunbarton": "Merrimack",
"durham": "Strafford",
"east kingston": "Rockingham",
"easton": "Grafton",
"eaton": "Carroll",
"effingham": "Carroll",
"ellsworth": "Grafton",
"enfield": "Grafton",
"epping": "Rockingham",
"epsom": "Merrimack",
"errol": "Coos",
"exeter": "Rockingham",
"farmington": "Strafford",
"fitzwilliam": "Cheshire",
"francestown": "Hillsborough",
"franconia": "Grafton",
"franklin": "Merrimack",
"freedom": "Carroll",
"fremont": "Rockingham",
"gilford": "Belknap",
"gilmanton": "Belknap",
"gilsum": "Cheshire",
"goffstown": "Hillsborough",
"gorham": "Coos",
"goshen": "Sullivan",
"grafton": "Grafton",
"grantham": "Sullivan",
"greenfield": "Hillsborough",
"greenland": "Rockingham",
"greenville": "Hillsborough",
"groton": "Grafton",
"hampstead": "Rockingham",
"hampton": "Rockingham",
"hampton falls": "Rockingham",
"hancock": "Hillsborough",
"hanover": "Grafton",
"harrisville": "Cheshire",
"hart's location": "Carroll",
"haverhill": "Grafton",
"hebron": "Grafton",
"henniker": "Merrimack",
"hill": "Merrimack",
"hillsborough": "Hillsborough",
"hinsdale": "Cheshire",
"holderness": "Grafton",
"hollis": "Hillsborough",
"hooksett": "Merrimack",
"hopkinton": "Merrimack",
"hudson": "Hillsborough",
"jackson": "Carroll",
"jaffrey": "Cheshire",
"jefferson": "Coos",
"keene": "Cheshire",
"kensington": "Rockingham",
"kingston": "Rockingham",
"laconia": "Belknap",
"lancaster": "Coos",
"landaff": "Grafton",
"langdon": "Sullivan",
"lee": "Strafford",
"lempster": "Sullivan",
"lincoln": "Grafton",
"lisbon": "Grafton",
"litchfield": "Hillsborough",
"littleton": "Grafton",
"londonderry": "Rockingham",
"loudon": "Merrimack",
"lyme": "Grafton",
"lyndeborough": "Hillsborough",
"madbury": "Strafford",
"madison": "Carroll",
"manchester": "Hillsborough",
"marlborough": "Cheshire",
"marlow": "Cheshire",
"mason": "Hillsborough",
"meredith": "Belknap",
"merrimack": "Hillsborough",
"middleton": "Strafford",
"milan": "Coos",
"milford": "Hillsborough",
"millsfield": "Coos",
"milton": "Strafford",
"monroe": "Grafton",
"mont vernon": "Hillsborough",
"moultonborough": "Carroll",
"nashua": "Hillsborough",
"nelson": "Cheshire",
"new boston": "Hillsborough",
"new castle": "Rockingham",
"new durham": "Strafford",
"new hampton": "Belknap",
"new ipswich": "Hillsborough",
"new london": "Merrimack",
"newbury": "Merrimack",
"newfields": "Rockingham",
"newington": "Rockingham",
"newmarket": "Rockingham",
"newport": "Sullivan",
"newton": "Rockingham",
"north hampton": "Rockingham",
"northfield": "Merrimack",
"northumberland": "Coos",
"northwood": "Rockingham",
"nottingham": "Rockingham",
"orange": "Grafton",
"orford": "Grafton",
"ossipee": "Carroll",
"pelham": "Hillsborough",
"pembroke": "Merrimack",
"peterborough": "Hillsborough",
"piermont": "Grafton",
"pittsburg": "Coos",
"pittsfield": "Merrimack",
"plainfield": "Sullivan",
"plaistow": "Rockingham",
"plymouth": "Grafton",
"portsmouth": "Rockingham",
"randolph": "Coos",
"raymond": "Rockingham",
"richmond": "Cheshire",
"rindge": "Cheshire",
"rochester": "Strafford",
"rollinsford": "Strafford",
"roxbury": "Cheshire",
"rumney": "Grafton",
"rye": "Rockingham",
"salem": "Rockingham",
"salisbury": "Merrimack",
"sanbornton": "Belknap",
"sandown": "Rockingham",
"sandwich": "Carroll",
"seabrook": "Rockingham",
"sharon": "Hillsborough",
"shelburne": "Coos",
"somersworth": "Strafford",
"south hampton": "Rockingham",
"springfield": "Sullivan",
"stark": "Coos",
"stewartstown": "Coos",
"stoddard": "Cheshire",
"strafford": "Strafford",
"stratford": "Coos",
"stratham": "Rockingham",
"sugar hill": "Grafton",
"sunapee": "Sullivan",
"surry": "Cheshire",
"sutton": "Merrimack",
"swanzey": "Cheshire",
"tamworth": "Carroll",
"temple": "Hillsborough",
"thornton": "Grafton",
"tilton": "Belknap",
"troy": "Cheshire",
"tuftonboro": "Carroll",
"unity": "Sullivan",
"wakefield": "Carroll",
"walpole": "Cheshire",
"warner": "Merrimack",
"warren": "Grafton",
"washington": "Sullivan",
"waterville valley": "Grafton",
"weare": "Hillsborough",
"webster": "Merrimack",
"wentworth": "Grafton",
"westmoreland": "Cheshire",
"whitefield": "Coos",
"wilmot": "Merrimack",
"wilton": "Hillsborough",
"winchester": "Cheshire",
"windham": "Rockingham",
"windsor": "Hillsborough",
"wolfeboro": "Carroll",
"woodstock": "Grafton",
},
"RI": {
"barrington": "Bristol",
"bristol": "Bristol",
"warren": "Bristol",
"coventry": "Kent",
"east greenwich": "Kent",
"warwick": "Kent",
"west greenwich": "Kent",
"west warwick": "Kent",
"jamestown": "Newport",
"little compton": "Newport",
"middletown": "Newport",
"newport": "Newport",
"portsmouth": "Newport",
"tiverton": "Newport",
"burrillville": "Providence",
"central falls": "Providence",
"cranston": "Providence",
"cumberland": "Providence",
"east providence": "Providence",
"foster": "Providence",
"glocester": "Providence",
"johnston": "Providence",
"lincoln": "Providence",
"north providence": "Providence",
"north smithfield": "Providence",
"pawtucket": "Providence",
"providence": "Providence",
"scituate": "Providence",
"smithfield": "Providence",
"woonsocket": "Providence",
"charlestown": "Washington",
"exeter": "Washington",
"hopkinton": "Washington",
"narragansett": "Washington",
"new shoreham": "Washington",
"north kingstown": "Washington",
"richmond": "Washington",
"south kingstown": "Washington",
"westerly": "Washington",
},
"VT": {
"addison": "Addison",
"bridport": "Addison",
"bristol": "Addison",
"cornwall": "Addison",
"ferrisburgh": "Addison",
"goshen": "Addison",
"granville": "Addison",
"hancock": "Addison",
"leicester": "Addison",
"lincoln": "Addison",
"middlebury": "Addison",
"monkton": "Addison",
"new haven": "Addison",
"orwell": "Addison",
"panton": "Addison",
"ripton": "Addison",
"salisbury": "Addison",
"shoreham": "Addison",
"starksboro": "Addison",
"vergennes": "Addison",
"waltham": "Addison",
"weybridge": "Addison",
"whiting": "Addison",
"arlington": "Bennington",
"bennington": "Bennington",
"dorset": "Bennington",
"glastenbury": "Bennington",
"landgrove": "Bennington",
"manchester": "Bennington",
"peru": "Bennington",
"pownal": "Bennington",
"readsboro": "Bennington",
"rupert": "Bennington",
"sandgate": "Bennington",
"searsburg": "Bennington",
"shaftsbury": "Bennington",
"stamford": "Bennington",
"sunderland": "Bennington",
"winhall": "Bennington",
"woodford": "Bennington",
"barnet": "Caledonia",
"burke": "Caledonia",
"danville": "Caledonia",
"groton": "Caledonia",
"hardwick": "Caledonia",
"kirby": "Caledonia",
"lyndon": "Caledonia",
"newark": "Caledonia",
"peacham": "Caledonia",
"ryegate": "Caledonia",
"sheffield": "Caledonia",
"st. johnsbury": "Caledonia",
"st johnsbury": "Caledonia",
"stannard": "Caledonia",
"sutton": "Caledonia",
"walden": "Caledonia",
"waterford": "Caledonia",
"wheelock": "Caledonia",
"bolton": "Chittenden",
"burlington": "Chittenden",
"charlotte": "Chittenden",
"colchester": "Chittenden",
"essex": "Chittenden",
"essex junction": "Chittenden",
"hinesburg": "Chittenden",
"huntington": "Chittenden",
"jericho": "Chittenden",
"milton": "Chittenden",
"richmond": "Chittenden",
"shelburne": "Chittenden",
"south burlington": "Chittenden",
"underhill": "Chittenden",
"westford": "Chittenden",
"williston": "Chittenden",
"winooski": "Chittenden",
"averill": "Essex",
"bloomfield": "Essex",
"brighton": "Essex",
"brunswick": "Essex",
"canaan": "Essex",
"concord": "Essex",
"east haven": "Essex",
"ferdinand": "Essex",
"granby": "Essex",
"guildhall": "Essex",
"lemington": "Essex",
"lunenburg": "Essex",
"maidstone": "Essex",
"norton": "Essex",
"victory": "Essex",
"bakersfield": "Franklin",
"berkshire": "Franklin",
"enosburg": "Franklin",
"enosburg falls": "Franklin",
"fairfax": "Franklin",
"fairfield": "Franklin",
"fletcher": "Franklin",
"franklin": "Franklin",
"georgia": "Franklin",
"highgate": "Franklin",
"montgomery": "Franklin",
"richford": "Franklin",
"sheldon": "Franklin",
"st. albans": "Franklin",
"st albans": "Franklin",
"swanton": "Franklin",
"alburgh": "Grand Isle",
"grand isle": "Grand Isle",
"isle la motte": "Grand Isle",
"north hero": "Grand Isle",
"south hero": "Grand Isle",
"belvidere": "Lamoille",
"cambridge": "Lamoille",
"eden": "Lamoille",
"elmore": "Lamoille",
"hyde park": "Lamoille",
"johnson": "Lamoille",
"morristown": "Lamoille",
"morrisville": "Lamoille",
"stowe": "Lamoille",
"waterville": "Lamoille",
"wolcott": "Lamoille",
"bradford": "Orange",
"braintree": "Orange",
"brookfield": "Orange",
"chelsea": "Orange",
"corinth": "Orange",
"fairlee": "Orange",
"newbury": "Orange",
"orange": "Orange",
"randolph": "Orange",
"strafford": "Orange",
"thetford": "Orange",
"topsham": "Orange",
"tunbridge": "Orange",
"vershire": "Orange",
"washington": "Orange",
"west fairlee": "Orange",
"williamstown": "Orange",
"albany": "Orleans",
"barton": "Orleans",
"brownington": "Orleans",
"charleston": "Orleans",
"coventry": "Orleans",
"craftsbury": "Orleans",
"derby": "Orleans",
"glover": "Orleans",
"greensboro": "Orleans",
"holland": "Orleans",
"irasburgh": "Orleans",
"jay": "Orleans",
"lowell": "Orleans",
"morgan": "Orleans",
"newport": "Orleans",
"troy": "Orleans",
"westfield": "Orleans",
"westmore": "Orleans",
"benson": "Rutland",
"brandon": "Rutland",
"castleton": "Rutland",
"chittenden": "Rutland",
"clarendon": "Rutland",
"danby": "Rutland",
"fair haven": "Rutland",
"hubbardton": "Rutland",
"ira": "Rutland",
"killington": "Rutland",
"mendon": "Rutland",
"middletown springs": "Rutland",
"mount holly": "Rutland",
"mount tabor": "Rutland",
"pawlet": "Rutland",
"pittsfield": "Rutland",
"pittsford": "Rutland",
"poultney": "Rutland",
"proctor": "Rutland",
"rutland": "Rutland",
"shrewsbury": "Rutland",
"sudbury": "Rutland",
"tinmouth": "Rutland",
"wallingford": "Rutland",
"west haven": "Rutland",
"west rutland": "Rutland",
"barre": "Washington",
"berlin": "Washington",
"cabot": "Washington",
"calais": "Washington",
"duxbury": "Washington",
"east montpelier": "Washington",
"fayston": "Washington",
"marshfield": "Washington",
"middlesex": "Washington",
"montpelier": "Washington",
"moretown": "Washington",
"northfield": "Washington",
"plainfield": "Washington",
"roxbury": "Washington",
"waitsfield": "Washington",
"warren": "Washington",
"waterbury": "Washington",
"woodbury": "Washington",
"worcester": "Washington",
"athens": "Windham",
"brattleboro": "Windham",
"brookline": "Windham",
"dover": "Windham",
"dummerston": "Windham",
"grafton": "Windham",
"guilford": "Windham",
"halifax": "Windham",
"jamaica": "Windham",
"londonderry": "Windham",
"marlboro": "Windham",
"newfane": "Windham",
"putney": "Windham",
"rockingham": "Windham",
"somerset": "Windham",
"stratton": "Windham",
"townshend": "Windham",
"vernon": "Windham",
"wardsboro": "Windham",
"westminster": "Windham",
"whitingham": "Windham",
"wilmington": "Windham",
"windham": "Windham",
"andover": "Windsor",
"baltimore": "Windsor",
"barnard": "Windsor",
"bethel": "Windsor",
"bridgewater": "Windsor",
"cavendish": "Windsor",
"chester": "Windsor",
"hartford": "Windsor",
"hartland": "Windsor",
"ludlow": "Windsor",
"norwich": "Windsor",
"plymouth": "Windsor",
"pomfret": "Windsor",
"reading": "Windsor",
"rochester": "Windsor",
"royalton": "Windsor",
"sharon": "Windsor",
"springfield": "Windsor",
"stockbridge": "Windsor",
"weathersfield": "Windsor",
"weston": "Windsor",
"west windsor": "Windsor",
"windsor": "Windsor",
"woodstock": "Windsor",
},
}
def resolve_county_from_service_area(service_area: str, state_abbr: str) -> str | None:
"""
Try to resolve a county name from a service area text string.
Strategy:
1. Check for direct county name mentions (e.g. "Throughout Rockingham County")
2. Tokenize and check each token against TOWN_COUNTY_MAP
Args:
service_area: Free-text service area from CheapestOil
state_abbr: Two-letter state code
Returns:
County name string or None if no match found.
"""
if not service_area or state_abbr not in TOWN_COUNTY_MAP:
return None
text = service_area.strip()
text_lower = text.lower()
# 1. Check for direct county name mentions
counties = _NE_COUNTIES.get(state_abbr, [])
for county in counties:
if county.lower() in text_lower:
return county
# 2. Tokenize and check against town map
town_map = TOWN_COUNTY_MAP[state_abbr]
# Split on common delimiters: commas, "and", semicolons, slashes
tokens = re.split(r'[,;/&]+|\band\b', text_lower)
for token in tokens:
token = token.strip().rstrip('.')
if not token:
continue
# Direct match
if token in town_map:
return town_map[token]
# Try without common prefixes/suffixes like "greater", "area", "surrounding"
for prefix in ("greater ", "the ", "town of ", "city of "):
if token.startswith(prefix):
cleaned = token[len(prefix):]
if cleaned in town_map:
return town_map[cleaned]
return None