0
\$\begingroup\$

This was a straightforward test for a junior level position. The README.md estimated completion should take 45 minutes. Provided were class and function stubs to complete and several unit tests which StackExchange won't let me link to due to my reputation (they are reproduced below).

I'd love to offer belabored rationalizations of why I did what I did, but I'm sure no one's interested. I stripped most comments for brevity, but they can be found in the above links. Please tear it apart:

import functools class Campaign: # Prefer not to refactor this because I think comparison logic belongs elsewhere def __init__(self, name, bid_price, target_keywords): self.name = name self.bid_price = bid_price self.target_keywords = target_keywords def __repr__(self): return "<Campaign> {}".format(self.name) @functools.total_ordering class Score: def __init__(self, user_search_terms, campaign): self.user_search_terms = user_search_terms self.campaign = campaign def __repr__(self): return "<Score> Campaign: {}, Relevance: {}".format(self.campaign.name, self.value) def __eq__(self, other): self._except_if_terms_ne(other) if self.campaign.bid_price == other.campaign.bid_price: return self.value == other.value return False def __lt__(self, other): self._except_if_terms_ne(other) if self.value < other.value: return True if self.value == other.value: return self.campaign.bid_price < other.campaign.bid_price return False def _except_if_terms_ne(self, other): if self.user_search_terms != other.user_search_terms: raise ValueError("Cannot compare unequal user search terms.") @property def value(self): ust_set = set(self.user_search_terms) ctk_set = set(self.campaign.target_keywords) return len(ust_set.intersection(ctk_set)) def choose_best_campaign_for_user(user_search_terms, campaigns): """ The "best" campaign to serve is the one with the most search term matches. If two or more campaigns have the same number of matches then the one with the highest bid_price should be served. If two or more campaigns have the same number of matches and the same bid_price, then either one may be returned. """ best_score = max([ Score(user_search_terms, camp) for camp in campaigns ]) return best_score.campaign if best_score.value > 0 else None 

And to the extent that they're relevant, the tests:

import unittest from bidder import Campaign, Score, choose_best_campaign_for_user # Sample campaigns for testing - the apple_mac and apple_ios campaigns have one keyword # in common 'apple' apple_mac_campaign = Campaign( name="Apple Mac Products", bid_price=1.00, target_keywords=["apple", "macbook", "macbook pro", "imac", "power mac", "mavericks"], ) apple_ios_campaign = Campaign( name="Apple IOS Products", bid_price=1.50, target_keywords=["apple", "ipad", "iphone", "ios", "appstore", "siri"], ) google_android_campaign = Campaign( name="Google Android Products", bid_price=1.50, target_keywords=["google", "android", "phone"] ) marvel_comics_campaign = Campaign( name="Marvel Comics", bid_price=0.50, target_keywords=["spiderman", "hulk", "wolverine", "ironman", "captain america"], ) all_campaigns = [ apple_mac_campaign, apple_ios_campaign, marvel_comics_campaign ] class ScoreTest(unittest.TestCase): def setUp(self): self.eq_val_dif_bids = ( Score([], apple_mac_campaign), Score([], apple_ios_campaign) ) self.eq_val_eq_bid = ( Score([], apple_ios_campaign), Score([], google_android_campaign) ) def test_eq_val_eq_bid_compares_eq(self): self.assertEqual(self.eq_val_eq_bid[0], self.eq_val_eq_bid[1]) def test_eq_val_dif_bids_compares_lt(self): self.assertTrue(self.eq_val_dif_bids[0] < self.eq_val_dif_bids[1]) def test_nonidentical_search_terms_raise_value_error(self): with self.assertRaises(ValueError): Score([], apple_ios_campaign) == Score(["test"], google_android_campaign) class BidderTest(unittest.TestCase): def test_it_picks_no_campaign_if_no_keywords_match(self): chosen_campaign = choose_best_campaign_for_user( user_search_terms=["batman"], campaigns=[marvel_comics_campaign], ) self.assertIsNone(chosen_campaign) def test_it_chooses_a_campaign_if_search_terms_match_keywords(self): chosen_campaign = choose_best_campaign_for_user( user_search_terms=["spiderman"], campaigns=[marvel_comics_campaign], ) self.assertEqual(marvel_comics_campaign, chosen_campaign) def test_it_chooses_campaign_with_most_overlapping_keywords(self): chosen_campaign = choose_best_campaign_for_user( ["apple", "macbook"], all_campaigns ) self.assertEqual(chosen_campaign, apple_mac_campaign) def test_it_chooses_campaign_with_higher_bid_price(self): chosen_campaign = choose_best_campaign_for_user( ["apple"], all_campaigns ) self.assertEqual(chosen_campaign, apple_ios_campaign) if __name__ == "__main__": unittest.main() 
\$\endgroup\$
5
  • \$\begingroup\$ Are you really allowed to share this? Every "take-home programming assignment" interview I've had required me not to disclose any details of the assignment. \$\endgroup\$ Commented Apr 13, 2017 at 15:30
  • \$\begingroup\$ @tilper I wasn't advised that I couldn't, and I redacted any mention of the company and platform in all code that I posted. That is, I assume it's OK, but if it breaks some rule, I can take it down. \$\endgroup\$ Commented Apr 13, 2017 at 15:33
  • \$\begingroup\$ @xxs It may break the 'not your code' as you derived from the companies code. But I'd be more worried of the theft of IP if they didn't allow you to take and distribute their IP. \$\endgroup\$ Commented Apr 13, 2017 at 15:53
  • \$\begingroup\$ Welcome to Code Review! Linking to off-site resources such as files on PasteBin is allowable, but you have to include enough information directly in the question itself so that it would still make sense if the links stop working. \$\endgroup\$ Commented Apr 13, 2017 at 17:59
  • \$\begingroup\$ I can't even see the pastebin stuff from work. (It's blocked by the web filters.) So, yeah. :P \$\endgroup\$ Commented Apr 13, 2017 at 21:36

1 Answer 1

3
\$\begingroup\$

Why does the scoring have to be so complicated? Tuple comparisons and set operations have the semantics you want. Just make sure that the keywords are stored as a set in the Campaign. Then:

class Campaign(): ... def score(self, keywords): return (len(self.target_keywords & keywords), self.bid_price) def valid_for(self, keywords): return not self.target_keywords.isdisjoint(keywords) #Assuming ``user_search_terms`` is a set. def choose_best_campaign_for_user(user_search_terms, campaigns): valid_campaigns = (c for c in campaigns if c.valid_for(user_search_terms)) compare_func = lambda campaign: campaign.score(user_search_terms) return max(valid_campaigns, key=compare_func, default=None) 
\$\endgroup\$
0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.