Guest User

Stubhub Senior Software Engineer Guide

a guest
Jun 8th, 2024
1,309
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.39 KB | Software | 0 0
  1.  
  2. """
  3. The solution you are writing is part of a marketing engine for our StubHub website.
  4. You will be implementing the setup and business logic for different marketing campaigns that find “relevant” events for a user and notifies them accordingly.
  5. We are first looking for the correctness and performance of your solution, but also the extensibility and long-term maintainability.
  6.  
  7. Q1
  8. This code is missing some key elements. Define the MarketingEngine class and implement a send_customer_notifications method to notify the customer of all the events happening in the same city as the customer.
  9.  
  10. While in the real world we would notify the customer of these events via email or text, for the purposes of this exercise, we want you to output the text content we intend to send to the customer and print it to the console.
  11.  
  12. Please note that this method will be invoked multiple times for multiple different customers.
  13.  
  14.  
  15. Notes:
  16.  
  17. - Make sure to ask if the timezone matters, e.g. do we need to send a batch of notifications
  18. at 12 PM Eastern Time and another batch at 12 PM Pacific Time?
  19. - Ask clarifying questions, like what if there is a tie where two events happen at the same time.
  20. What should you do in that scenario? Just take the first event.
  21.  
  22.  
  23. Q2
  24. Extend the solution to add a new campaign which sends a notification to the customer with the event closest to their next or upcoming birthday.
  25.  
  26. Please note that this campaign will be run multiple times for multiple different customers.
  27. # Just pick the first event
  28. # 100_000 customers
  29.  
  30. Q3
  31. We now need to implement a new campaign that notifies the customer of the 5 closest events to the customer based on the distance between the customer and the event.
  32.  
  33. You should assume that we have about 10 million events and we need to call this per user.
  34.  
  35. Q4
  36. In the stubhub backend we have a simple restful api that you can call that will return prices for events:
  37.  
  38. For all event prices: https://sh-mockapi.azurewebsites.net/api/ticketprice
  39.  
  40. For a price per event: https://sh-mockapi.azurewebsites.net/api/ticketprice?eventId={EventId}
  41.  
  42. i.e https://sh-mockapi.azurewebsites.net/api/ticketprice?eventId=1
  43.  
  44. Can you call this service from your solution to send a notification of the 5 cheapest tickets within a Y mile radius of the customer? We can test with various radiuses.
  45.  
  46. """
  47.  
  48. from bisect import bisect_left
  49. from collections import defaultdict
  50. from dataclasses import dataclass
  51. from datetime import datetime, timedelta
  52. from dateutil import parser
  53. from math import sqrt
  54. import requests
  55. from typing import List
  56.  
  57. from requests.models import Response
  58.  
  59. class City:
  60. name: str
  61. x_cor: int
  62. y_cor: int
  63. '''
  64. /*-------------------------------------
  65. Coordinates are roughly to scale with miles in the USA
  66.  
  67. 2000 +----------------------+
  68. | |
  69. | |
  70. Y | |
  71. | |
  72. | |
  73. | |
  74. | |
  75. 0 +----------------------+
  76. 0 X 4000
  77.  
  78. ---------------------------------------*/
  79. '''
  80.  
  81. # Assume a static number of cities
  82. CITY_MAP : dict[str, City]= {
  83. 'New York': City('New York', 3572, 1455),
  84. 'Los Angeles': City('Los Angeles', 462, 975),
  85. 'Boston': City('Boston', 3778, 1566),
  86. 'Chicago': City('Chicago', 2608, 1525),
  87. 'San Francisco': City('San Francisco', 183, 1233),
  88. 'Washington': City('Washington', 3358, 1320)
  89. }
  90.  
  91. def distance(city1: City, city2: City) -> float:
  92. return sqrt(
  93. (city1.x_cor - city2.x_cor) * (city1.x_cor - city2.x_cor) +
  94. (city1.y_cor - city2.y_cor) * (city1.y_cor - city2.y_cor)
  95. )
  96.  
  97. class Event:
  98. id: int
  99. name: str
  100. city: str
  101. event_date: datetime
  102.  
  103. class Customer:
  104. id: int
  105. name: str
  106. city: str
  107. birth_date: datetime
  108.  
  109. class MarketingEngine:
  110. def __init__(self, events: List[Event]):
  111. now: datetime = datetime.now()
  112. self.events: List[Event] = [
  113. event for event in events if event.event_date >= now
  114. ]
  115. self.city_to_events: dict[str, List[Event]] = defaultdict(list)
  116. for event in self.events:
  117. self.city_to_events[event.city].append(event)
  118. event_date_to_event: dict[datetime, Event] = {event.event_date: event for event in self.events}
  119. self.events_sorted_by_date = sorted(
  120. event_date_to_event.values(), key=lambda event: event.event_date
  121. )
  122. self.closest_threshold: int = 5
  123.  
  124. self.prices_ttl: timedelta = timedelta(days=1)
  125. self.last_price_cache: datetime = datetime.now()
  126. price_data = requests.get("https://sh-mockapi.azurewebsites.net/api/ticketprice").json()
  127. self.id_to_event = {int(event["Id"]): event for event in price_data}
  128.  
  129.  
  130. def send_customer_notifications(self, customer: Customer) -> None:
  131. # "Sends" notifications via the print method
  132. print(f"Calling send_customer_notifications for customer {customer.name}")
  133. result = self.city_to_events.get(customer.city, [])
  134. print(result)
  135.  
  136. def send_customer_notifications_by_birthday(self, customer: Customer) -> None:
  137. # log(|EVENTS|)
  138. if not self.events_sorted_by_date:
  139. print(f"No events for {customer}")
  140. base_birthdate = customer.birth_date
  141. now: datetime = datetime.now()
  142. next_birthday = datetime(now.year, base_birthdate.month, base_birthdate.day)
  143. if now > next_birthday:
  144. next_birthday = datetime(now.year + 1, next_birthday.month, next_birthday.day)
  145. print(f"{next_birthday=}")
  146.  
  147. idx = bisect_left(self.events_sorted_by_date, next_birthday, key=lambda event: event.event_date)
  148. # If you're sandwiched between two events, which is closer?
  149. if idx > 0:
  150. time_diff_right = abs(self.events_sorted_by_date[idx].event_date - next_birthday)
  151. time_diff_left = abs(self.events_sorted_by_date[idx - 1].event_date - next_birthday)
  152. if time_diff_left <= time_diff_right:
  153. print(self.events_sorted_by_date[idx - 1])
  154. return
  155. print(self.events_sorted_by_date[idx])
  156.  
  157. def send_customer_notifications_by_proximity(self, customer: Customer) -> None:
  158. customer_city = CITY_MAP.get(customer.city)
  159. if not customer_city:
  160. print(f"No city found for {customer}")
  161. result = []
  162. nearest_cities = sorted(CITY_MAP.values(), key=lambda city: distance(customer_city, city))
  163. for city in nearest_cities:
  164. events = self.city_to_events[city.name]
  165. for event in events:
  166. result.append(event)
  167. if len(result) == self.closest_threshold:
  168. break
  169. if len(result) == self.closest_threshold:
  170. break
  171. print("Closest events: ")
  172. print(result)
  173.  
  174. def part_4(self, customer: Customer, radius: int) -> None:
  175. customer_city = CITY_MAP.get(customer.city)
  176. if not customer_city:
  177. print(f"No city found for {customer}")
  178. return
  179. now = datetime.now()
  180. if now - self.last_price_cache < self.prices_ttl:
  181. price_data = requests.get("https://sh-mockapi.azurewebsites.net/api/ticketprice").json()
  182. # print(price_data)
  183. self.id_to_event = {int(event["Id"]): event for event in price_data}
  184. self.last_price_cache = now
  185. # 5 cheapest tickets in a y-mile radius
  186. cities_within_radius = [
  187. city for city in CITY_MAP.values() if distance(city, customer_city) <= radius
  188. ]
  189. event_ids_in_the_radius: List[int] = []
  190. for city in cities_within_radius:
  191. event_ids_in_the_radius.extend(event.id for event in self.city_to_events.get(city.name, []))
  192. print(f"{event_ids_in_the_radius=}")
  193. # List[(price, event)]
  194. prices_and_events = []
  195. for event_id in event_ids_in_the_radius:
  196. price = self.id_to_event[event_id]["Price"]
  197. prices_and_events.append((float(price), self.id_to_event[event_id]))
  198. prices_and_events.sort()
  199. if len(prices_and_events) < 5:
  200. prices_and_events = prices_and_events[:5]
  201. print([event for _, event in prices_and_events])
  202.  
  203.  
  204. def main():
  205. events: list[Event] = [
  206. Event(1, "Phantom of the Opera", "New York", parser.parse("2023-12-23")), # X
  207. Event(2, "Metallica", "Los Angeles", parser.parse("2024-12-02")), # OK
  208. Event(3, "Metallica", "New York", parser.parse("2024-12-06")), # OK
  209. Event(4, "Metallica", "Boston", parser.parse("2024-10-23")), # OK
  210. Event(5, "LadyGaGa", "New York", parser.parse("2023-09-20")),
  211. Event(6, "LadyGaGa", "Boston", parser.parse("2024-08-01")), # OK
  212. Event(7, "LadyGaGa", "Chicago", parser.parse("2024-07-04")), # OK
  213. Event(8, "LadyGaGa", "San Francisco", parser.parse("2024-07-07")), #OK
  214. Event(9, "LadyGaGa", "Washington", parser.parse("2023-05-22")),
  215. Event(10, "Metallica", "Chicago", parser.parse("2024-01-01")),
  216. Event(11, "Phantom of the Opera", "San Francisco", parser.parse("2024-07-04")),
  217. Event(12, "Phantom of the Opera", "Chicago", parser.parse("2025-05-15")),
  218. ]
  219.  
  220. customer: Customer = Customer(1, "Avos", "New York", parser.parse("1995-05-11"))
  221.  
  222. engine: MarketingEngine = MarketingEngine(events)
  223. # engine.send_customer_notifications(customer)
  224.  
  225. # engine.send_customer_notifications_by_birthday(customer)
  226. # print()
  227. # print("By proximity: ")
  228. # engine.send_customer_notifications_by_proximity(customer)
  229.  
  230. engine.part_4(customer, 2000)
  231. ```
  232.  
  233.  
  234. ==============
  235.  
  236.  
  237. Design a system that will send 1,000,000 recommendation emails to users in 5 minutes.
  238.  
  239. These emails must be sent from 12:00 PM to 12:05 PM.
  240.  
  241.  
  242. Answer:
  243.  
  244. - You cannot create the recommendations within 5 minutes. You must create the recommendations in the
  245. hour or two beforehand.
  246.  
  247. - Ask your interviewer whether the email service is a black box, or whether you own the service.
  248. When it comes to email, some users will ultimately complain that they aren't getting their
  249. emails even if you send them, so you might need SMTP logs to prove that you're sending the emails.
  250. This is usually easier to do if you own the email service.
  251.  
  252. - Discuss other problems that accompany email systems, like failure, retries, at-least-once delivery, and dead lettering.
  253.  
  254. - I gave calculations for the number of worker nodes that we need for the email service. I don't know
  255. whether this is necessary.
  256.  
  257. - You will be asked about business statistics etc. that you should collect. Open rate, click through rate,
  258. conversion rate, A/B testing success metrics etc. are the ones you should talk about.
  259.  
  260. - You will be asked about system metrics to collect. You should collect metrics on email failure, retries, complaints.
  261. You should also figure out how long it takes for the recommendation system to prepare 1,000,000 recommendations.
  262.  
  263. - You should talk about what to do if each component fails. If the recommendation system fails, you should have default recommendations.
  264.  
  265. Feel free to ask questions. See my diagram.
  266.  
  267.  
  268. My diagram:
  269. https://imgur.com/RyaMzg5
  270.  
  271.  
  272. ============
  273.  
  274. Director of Engineering Interview
  275.  
  276. Talk about a project you're particularly proud of. The project can be one you’re particularly proud of, or one you’ve learned a lot from. Be sure to share the
  277. accomplishments from this project; again, noting both technical accomplishments, and data points at how this project impacted the business as whole, are great ways to get your impact across to the team.
  278.  
  279. Talk about getting blocked by another team.
  280. Talk about a disgreement in priorities with another team.
  281. Talk about a technical challenge that you faced.
  282.  
  283. Then do your creative pitch. Don't overthink it.
  284.  
  285. Hiring Manager Interview:
  286.  
  287. Introductions. He talks about what his teams do. What his roadmap is. He gave me a ton of time to ask questions. The only question he had for me was a time where I had a disagreement with someone else. I asked whether he wanted a technical or interpersonal disagreement. He said either. I went with a technical disagreement.
  288.  
  289.  
  290.  
  291.  
Advertisement
Add Comment
Please, Sign In to add comment