Skip to content
A data science exercise to programmatically find buildings that used to be Pizza Hut restaurants... | Project for /r/FormerPizzaHuts
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
PizzaHuts
src
.Rhistory
.gitignore
README.md
captcha.py
formerLocations.txt
formerPizzaHutPosts.json
locations.csv
main.py

README.md

A data science exercise to programmatically find buildings that *used* to be Pizza Hut restaurants...

My main inspiration for this project: https://www.reddit.com/r/FormerPizzaHuts/

Finding Store Status

Pizza Hut has an API that returns store information by Store Number. Fortunately, this API has no real form of rate limitation, so I iterated through all possible store combinations.

To return store information you simply send a POST request with form data that contains a storeNum parameter to the following API endpoint:

https://www.pizzahut.com/api.php/site/api_ajax/stores/getStoreAjax

You will get a response that looks similar to this:

{
    "success": 1,
    "response": {
        "storeNum": "027543",
        "address1": "8024 Augusta Rd",
        "address2": "Piedmont, SC 29673",
        "zip": "29673",
        "phone": "(864) 277-1222",
        "landmark": "",
        "hrs_available": 1,
        "hrs_concepts": [{
            "service": "carryout"
        }, {
            "service": "delivery"
        }],
        "hrs_times": [{
            "day": "Sun",
            "col0": "11:00am - 11:00pm",
            "col1": "11:00am - 11:00pm"
        }, {
            "day": "Mon",
            "col0": "11:00am - 11:00pm",
            "col1": "11:00am - 11:00pm"
        }, {
            "day": "Tues",
            "col0": "11:00am - 11:00pm",
            "col1": "11:00am - 11:00pm"
        }, {
            "day": "Weds",
            "col0": "11:00am - 11:00pm",
            "col1": "11:00am - 11:00pm"
        }, {
            "day": "Thurs",
            "col0": "11:00am - 11:00pm",
            "col1": "11:00am - 11:00pm"
        }, {
            "day": "Fri",
            "col0": "11:00am - 12:00am",
            "col1": "11:00am - 12:00am"
        }, {
            "day": "Sat",
            "col0": "11:00am - 12:00am",
            "col1": "11:00am - 12:00am"
        }],
        "dinein_available": 0,
        "services": "Carryout, Delivery",
        "wing_street": 1,
        "latitude": 34.696159,
        "longitude": -82.398854,
        "closure_reason": "",
        "carryout_future_order": true,
        "delivery_future_order": true,
        "city": "Piedmont",
        "offline": false,
        "status": "open",
        "occasion_status": "DC",
        "store_status": "DC",
        "delivery_available": true,
        "offline_status_msg": "",
        "first_delivery": "11:00 AM",
        "first_carryout": "11:00 AM",
        "open_later_today_delivery": "",
        "open_later_today_carryout": "",
        "loyalty": {
            "address": "8024 Augusta Rd",
            "city": "Piedmont",
            "state": "SC",
            "state_name": "South Carolina",
            "zip": 29673,
            "phone": "(864) 277-1222",
            "status": "DC",
            "carryout_open": "11:00AM",
            "carryout_close": "11:00PM",
            "carryout_future_order": true,
            "delivery_open": "11:00AM",
            "delivery_close": "11:00PM",
            "delivery_future_order": true,
            "open_for_delivery": true,
            "closure_reason": "",
            "onlineStatus": "online",
            "firstTimeDiscount": false,
            "currentlyOpen": true,
            "deliveryAvailable": true,
            "carryoutAvailable": true,
            "acceptFutureOrders": true,
            "landmark": null,
            "futuredelivery": true,
            "futurecarryout": true,
            "pizzaEvoTestStore": true,
            "offlineStatusMsg": null,
            "loyalty": "Y",
            "tmp_offline_msg": "",
            "store_lastUpdate": null,
            "showDeliveryTime": "Y",
            "promiseTimeDel": 50,
            "promiseTimeLastUpdateDel": {
                "date": "2018-06-24 16:27:00.000000",
                "timezone_type": 3,
                "timezone": "US\/Central"
            },
            "showCarryoutTime": "Y",
            "promiseTimeCo": 15,
            "promiseTimeLastUpdateCo": {
                "date": "2018-06-24 16:25:00.000000",
                "timezone_type": 3,
                "timezone": "US\/Central"
            },
            "promiseTime": 50
        }
    }
}

Here is the Python function used to determine if the store was Open, Closed, or Non-Existant (Note that this simply prints out the results, however the actual code used in main.py will save store information to a local file.)

def checkStore(storeNums):
	data = '{"storeNum":"NUM"}'.replace("NUM", str(storeNum))
	response = requests.post('https://www.pizzahut.com/api.php/site/api_ajax/stores/getStoreAjax', data=data)
	if response.json()['response']['phone'] == None:
		print("Not a Real Store")
	elif response.json()['response']['longitude'] == 0:
		print("Closed Store")
	else:
		print("Open Store")

Grabbing Images

After finding restaraunt addresses that have recently closed, the program uses Google Maps to extract photos from the location. It does this by making a Google search for the address to extract the Panoid value from the first Google Maps URL in the search results. The Panoid is then used to directly pull images using the following API endpoint:

http://geo1.ggpht.com/cbk?panoid={panoid}&output=thumbnail&cb_client=search.LOCAL_UNIVERSAL.gps&thumb=2&w=2000&h=2000&yaw={cameraTilt}&pitch=0&thumbfov=100

After pulling the panoid it will extract 17 images from each address using camera orientation values ranging from 0 to 340. Additionally, camera orientation values are "randomized" by adding a random float between 0 and 1 to the orientaiton. This does not significantly affect the camera orientation, but it prevents bot detection when accessing this API endpoint frequently.

def grabImagesFromAddress(address):
	imageList = []
	# Contains a list of image URLs
	addressURL = address.replace(" ", "+")
	# Converts address to a valid URL address
	url = "https://www.google.com/maps/place/" + addressURL
	# Gens URL for this address
	res = grabSite(url)
	# Pulls the url that was previously defined
	page = bs4.BeautifulSoup(res.text, 'lxml')
	# Converts it into a BS4 Object
	panoidVal = grabPanoidFromHTML(page)
	# This grabs the Panoid val from that address
	if panoidVal != None:
		# This means the address did exist
		for cameraOrientationVal in genListOfCameraOrientations():
			# Iterates through all possible camera orientations
			imageList.append(GOOGLE_STREETVIEW_API.format(panoidVal, cameraOrientationVal))
			# Appends them to the list of images
		return imageList

Ensuring Accuracy

The most time intensive part of this project was identifying the best camera orientation for each set of location images. The program pulls 17 camera orientations for each address, and the pizza hut is usually in only 1 or 2 of these images.

Method #1

The most cost effective way of finding the ideal camera orientation was using an unrelated service called DeathByCapthca. DBC uses real people to solve Captcha images, but the service is usually used by spammers and bot creators. I theorized that if I turned my Pizza Hut images into Captchas, DBC would send back the image coordinates of the Pizza Hut locations.

Here is an example of the generated Captcha image:

Captcha images were then sent to DeathByCaptcha, and the quantity of solved coordinates were used to find the best image of each restaraunt.

Here is an example using psuedocode:

# This is pseudocode, but the captcha.py file contains the actual python code
listOfImages = GrabImages("123 Test Street, Palo Alto CA")
# Grabs the Pizza Hut images at 123 Test Street, Palo Alto CA...
listOfImageInfo = []
for image in listOfImages:
    # Iterates through all 17 camera orientations
    fakeCaptcha = generateCaptcha(image)
    # Generates a "Fake" captcha image
    coordinates = SolveCaptcha(fakeCaptcha)
    # DBC returns coordinates of the squares that contain restaraunts
    listOfImageInfo({"image_file": image, "number_of_restaraunts": len(coordinates)})
    # Creates a dictionary with image file and the number of squares containing restaraunts
mostRestaraunts = 0
for imageInfo in listOfImageInfo:
    if imageInfo['number_of_restaraunts'] > mostRestaraunts:
        bestImage = imageInfo['image_file']
return bestImage

Method #2

The captcha method worked for most images, but there was still a handful of photos that either didn't contain a Pizza Hut or that contained photos of other buildings in the image. To resolve this issue, I created a python script that iterates through all remaining addresses, and opens up a window that allows you to move through images quickly to pick the image that best displays the former Pizza Hut location. This also allows you to eliminate Pizza Hut locations that had been torn down and replaced with a building that didn't keep the iconic Pizza Hut building style...

cameraTilt = list(range(0, 340, 20))
# This contains all camera tilt orientations in the Google Car
currentOrientation = cameraTilt[0]
while True:
	fileName = genFileName(address, currentOrientation)
	# Filename for this specific camera orientation
	keyChoice = viewImage(fileName)
	# Keychoice is the key a user presses in the image window
	if keyChoice == ord('n'):
		# The user inputted the letter N
		newTilt = cameraTilt.index(currentOrientation) + 1
		if newTilt == len(cameraTilt):
			# This is going to refresh the orientation from 320+ to 0
			# ^ This allows for a smoothless transition between images
			newTilt = 0
		currentOrientation = cameraTilt[newTilt]
		# Sets new orientation
	if keyChoice == ord("p"):
		# The user inputted the letter P
		currentOrientation = cameraTilt[cameraTilt.index(currentOrientation) - 1]
		# Sets new orientation
	if keyChoice == ord("q"):
		# The user inputted the letter Q
		# This indicates that this image did not have a Pizza hut
		break
		# Quits the while loop
	if keyChoice == ord("s"):
		# The user inputted the letter S
		# This indicates the user wants to save on the current image
		saveImage(fileName)
		# Saves the image
		break
		# Quits the while loop
	print("{} IN LOOP {}".format(currentOrientation, fileName))
	# Prints out information about the image

Examples

Verizon Wireless - 8514 Holcomb Bridge Rd, Alpharetta GA 20022

Moe's Cafe - 4705 River Oaks Blvd, Fort Worth TX 76114

Don Beto's Tacos - 720 Ricks Rd, Selma NC

Carlos Mexican Grill #3 - 282 W Virginia St. Crystal Lake IL 60014

Sakura Japanese Restaraunt - Carlos Mexican Grill #3 - 201 Inman Rd, Lyman SC 29365

Post Virality

I wanted to get as much project feedback as possible from Reddit, so I used the Reddit Post Bigquery Dataset to find the most ideal time to post the project on /r/FormerPizzaHuts.

I used the following SQL query to pull upvote count by UTC-Timestamp:

#standardSQL
SELECT
  created_utc, score
FROM `fh-bigquery.reddit_posts.201*`
WHERE subreddit = "FormerPizzaHuts"

After analyzing the dataset I found that posting this project on Reddit on a Monday at 9:00PM PST would result in the highest upvote count.

Upvotes by Day

Upvotes by Time

Locations Found

1940 Main St, Green Bay,WI
2309 Lafayette Rd, Fort Oglethorpe,GA
3278 Lancaster Dr Ne, Salem,OR
6049 Memorial Dr, Stone Mountain,GA
220 Cumberland St, Bogalusa,LA
440 East St, Pittsboro,NC
1123 Highway 301 N, Dillon,SC
124 Water St, Newton,NJ
1215 S Gordon St, Alvin,TX
7114 Mechanicsville Tpke, Mechanicsville,VA
147 Mansfield Ave, Shelby,OH
6304 Leesburg Pike, Falls Church,VA
3322 W Loomis Rd, Milwaukee,WI
3586 Homestead Rd, Santa Clara,CA
6710 S Broadway Ave, Tyler,TX
1001 E Loop 304, Crockett,TX
2684 Shaffer Rd, Atwater,CA
3810 Oakwood Blvd, Melvindale,MI
145 Clarice Dr, Holly Springs,MS
65 Lakeview Dr, Clinton,MS
2749 S Main St, High Point,NC
1367 Progressive Ave, Seward,NE
5214 Dixie Hwy, Waterford Twp,MI
2423 N Herritage St, Kinston,NC
11975 Bernardo Plaza Dr, San Diego,CA
6102 Falls Of Neuse Rd, Raleigh,NC
5900 S Western Ave, Chicago,IL
208 Mcguinness Blvd, Brooklyn,NY
1908 W Main St, Kalamazoo,MI
14563 60th St N, Stillwater,MN
839 E 2nd St, Chase City,VA
6285 W Park Ave, Houma,LA
2917 Fort Campbell Blvd, Hopkinsville,KY
2661 Main St West Sw, Snellville,GA
3854 Central Ave Ne, Minneapolis,MN
11420 E Washington St, Indianapolis,IN
1076 Harkrider St, Conway,AR
7000 W Charleston Blvd, Las Vegas,NV
3779 S Cooper St, Arlington,TX
475 E Us 69, Kansas City,MO
346 N University Ave, Provo,UT
907 S High St, West Chester,PA
7685 E Colonial Dr, Orlando,FL
257 Plainfield Rd, West Lebanon,NH
6223 Crain Hwy, La Plata,MD
2202 N Galloway Ave, Mesquite,TX
4610 Capital Blvd, Raleigh,NC
2101 Plum Grove Rd, Rolling Meadows,IL
1026 N Duquesne Blvd, Duquesne,PA
211 Nw Barry Rd, Kansas City,MO
3694 Mercer University Dr, Macon,GA
3505 Gravois Ave, St Louis,MO
226 9th Ave Se, Mayville,ND
1739 Pearl Rd, Brunswick,OH
611 N Oceanshore Blvd, Flagler Beach,FL
1325 Town Centre Dr, St Paul,MN
46 Crystal Ave, Derry,NH
1428 W Galena Ave, Freeport,IL
15380 George O Neal Rd, Baton Rouge,LA
3884 Hixson Pike, Chattanooga,TN
1227 N 24th St, Quincy,IL
3800 Williams Blvd, Kenner,LA
4236 Peach St, Erie,PA
1927 N Highway 65, Lake Village,AR
1772 Fremont Blvd, Seaside,CA
1811 Cleveland Rd, Sandusky,OH
7897 Normandy Blvd, Jacksonville,FL
601 1st Ave Se, Moultrie,GA
29 Warren St, Randolph,MA
1244 S Range Ave, Denham Springs,LA
720 N State Road 25, Rochester,IN
10595 Greenbelt Rd, Lanham,MD
2633 Williams Blvd, Kenner,LA
500 Sw 10th Ave, Topeka,KS
6303 N Meridian Ave, Oklahoma City,OK
831 W 6th Ave, Emporia,KS
1300 S Noland Rd, Independence,MO
218 Andover St, Peabody,MA
13886 Old Columbia Pike, Silver Spring,MD
308 N Boulder Hwy, Henderson,NV
8 Traders Way, Salem,MA
3455 Ross Clark Cir, Dothan,AL
6946 Fm 1960 Rd E, Humble,TX
1023 W Main St, Princeton,KY
908 S Kniss Ave, Luverne,MN
1924 S Minnesota Ave, Sioux Falls,SD
1501 N Palm Canyon Dr, Palm Springs,CA
1600 Independence Pky, Plano,TX
5025 Lapalco Blvd, Marrero,LA
445 Center St, Auburn,ME
791 Lisbon St, Lewiston,ME
3150 Jefferson St, Napa,CA
921 E Danforth Rd, Edmond,OK
745 W Cypress St, Kennett Square,PA
2200 National Rd W, Richmond,IN
1620 Mall Dr, Benton Harbor,MI
5119 Indian Head Hwy, Oxon Hill,MD
2413 N Salisbury Blvd, Salisbury,MD
2310 Nw Topeka Blvd, Topeka,KS
14699 Sw 104th St, Miami,FL
2415 Market St, Pascagoula,MS
1801 T P White Dr, Jacksonville,AR
4453 William Penn Hwy, Murrysville,PA
7403 103rd St, Jacksonville,FL
4711 N Chambliss St, Alexandria,VA
1308 Eastern Ave, Gallipolis,OH
5770 Harrison Ave, Cincinnati,OH
3605 Austin Bluffs Pky, Colorado Springs,CO
7802 Fairview Rd, Charlotte,NC
1601 S University Dr, Fargo,ND
3727 Spenard Rd, Anchorage,AK
3108 Western Branch Blvd, Chesapeake,VA
3048 N Service Dr, Red Wing,MN
6391 Royalton Rd, North Royalton,OH
100 Portsmouth Ave, Exeter,NH
720 W Wishkah St, Aberdeen,WA
670 E 3rd Ave, San Mateo,CA
5945 Buttermilk Hollow Rd, Pittsburgh,PA
161 S Benton Ave, Marshall,MO
696 Liberty Blvd, Dubois,PA
26010 Euclid Ave, Euclid,OH
611 12th Ave Rd, Nampa,ID
576 12th St, Menasha,WI
6441 Troost Ave, Kansas City,MO
983 Carlisle St, Hanover,PA
8800 Hampton Mall Dr N, Capitol Heights,MD
1025 Christiana Rd, Newark,DE
2312 Grand Ave, Waukegan,IL
8514 Holcomb Bridge Rd, Alpharetta,GA
4705 River Oaks Blvd, Fort Worth,TX
3245 Sierra Rd, San Jose,CA
1823 W Hill Ave, Valdosta,GA
127 Twin City Mall, Crystal City,MO
3318 N Main St, Anderson,SC
1135 N 9th St, Stroudsburg,PA
6066 S Hulen St, Fort Worth,TX
835 Central St, Franklin,NH
6871 W Overland Rd, Boise,ID
557 Wilson St, Brewer,ME
4551 Yadkin Rd, Fayetteville,NC
814 S Broadway St, Georgetown,KY
1235 E Tallmadge Ave, Akron,OH
326 Sr 60 E, Lake Wales,FL
1739 S Lumpkin St, Athens,GA
405 N 1st St, Saratoga,WY
6610 Camden Blvd, Fountain,CO
2935 W Market St, Fairlawn,OH
9160 Oh 14, Streetsboro,OH
31200 Ten Mile, Farmington,MI
541 W Main St, Henderson,TN
301 J St, La Porte,IN
1477 Freedom Blvd, Watsonville,CA
2430 Bailey Ave, Jackson,MS
720 Ricks Rd, Selma,NC
655 E 9400 S, Sandy,UT
107 Mathis Dr, Dickson,TN
1155 W Main St, Valley City,ND
900 Church St, Laurens,SC
282 W Virginia St, Crystal Lake,IL
15 Belmont Ave, Belfast,ME
2021 Springdale Rd, Waukesha,WI
680 W Main St, Lebanon,KY
656 S Main St, Cottonwood,AZ
1203 W 2nd St, Roswell,NM
101 Plaistow Rd, Haverhill,MA
5202 Wesley St, Greenville,TX
2851 Perkiomen Ave, Reading,PA
2500 North Point Ct, Alpharetta,GA
290 E Main St, Collegeville,PA
140 Caldwell Blvd, Nampa,ID
209 High St, Ellsworth,ME
5516 South Blvd, Charlotte,NC
9581 Braddock Rd, Fairfax,VA
540 W South St, Frederick,MD
10239 Lincoln Trl, Fairview Heights,IL
4131 W Ina Rd, Tucson,AZ
11003 Lower Azusa Rd, El Monte,CA
191 Main St, Gorham,NH
314 Cabot St, Beverly,MA
101 Crown Hill Rd, Excelsior Springs,MO
4815 4th St Nw, Albuquerque,NM
10115 Dorchester Rd, Summerville,SC
709 W Main St, Jamestown,NC
201 Inman Rd, Lyman,SC
2813 N 6th St, Vincennes,IN
1000 W Vine St, Kissimmee,FL
5801 Conroy Rd, Orlando,FL
2287 N Highland Ave, Jackson,TN
710 E Battle St, Talladega,AL
14112 Greenwell Springs Rd, Greenwell Springs,LA
5107 W El Segundo Blvd, Hawthorne,CA

You can’t perform that action at this time.