HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

E-commerce product price tracker

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
trackerpricecommerceproduct

Problem

I building a very simple price tracker web app. I am using MongoDB with pymongo. The user will enter the URL of the product he wishes to track and the desired amount, when the price goes below this amount, he should get an alert. It doesn't matter how many products he is tracking, I will send the alert when price changes in one product. I will sending generalized alert like 'Some of the product/s available at cheaper price' or something like that.

There are three collections in the database, one to store product details, one for user details and one to map products to the users who are tracking it.

A typical document in products collection will be:

{
  _id: ObjectId("53a2bfcfa7603606c2765342"),
  name: "Nexus 7 from Google (7-Inch, 16 GB, Black)",
  url: "http://rads.stackoverflow.com/amzn/click/B00DVFLJDS",
  base_price: 222,
  current_price: 213.96,
  img_url: "http://i.imgur.com/something.jpg",
  history: [
    [
      ISODate("2014-06-19T12:08:13.354Z"),
      293
    ],
    [
      ISODate("2014-06-24T14:31:38.216Z"),
      424
    ],
    [
      ISODate("2014-06-24T14:32:06.992Z"),
      424
    ]
  ]
}


in users, the tracked_products contain the Object Id of the product document and the desired price:

{
  _id: ObjectId("539c4adea760360886d7ef02"),
  email_id: "johnappleseed@apple.com",
  channels: {
    'twitter': 'usertwitterid',
    'ios_push_id': '45462722672762576422'
    'android_push_id': '2572652754762474'
    'chrome_push_id': '456456454646464'
  },
  tracked_products: [
    [
        ObjectId("53a2bfcfa7603606c2765342"), 
        200
    ],
    [
        ObjectId("53a2d2ada7603606c2765344"),
        345
    ],
    [
        ObjectId("53a2d294a7603606c2765343"),
        120
    ]
  ]
}


and lastly trackers, here subscribers contain the Object Id of user document who are tracking this product:

```
{
_id: ObjectId("77a2bfcfa7603606c2765399"),
product_id: ObjectId("53a2bfcfa7603606c2765342"),
subscribers: [

Solution

You should create a function that returns all the subscribers for a product:

def get_subscribers(product, current_price):
    tracker_document = db.trackers.find_one({'product_id': product['_id']})

    subscribers = set()
    for user_id, desired_price in tracker_document['subscribers']:
        if current_price <= desired_price:
            subscribers.add(user_id)

    return subscribers


This could even be condensed even more using a list comprehension:

def get_subscribers(product, current_price):
    tracker_document = db.trackers.find_one({'product_id': product['_id']})

    return set([user_id for user_id, desired_price in tracker_document['subscribers']
                if current_price <= desired_price])


This change makes your code look like this:

subscribers = set()
for product in product.collection:
    current_price = get_current_price(product['url'])
    if current_price == product['current_price']:
        continue

    product['history'].append((datetime.datetime.utcnow(), current_price))
    product.collection.save(product)

    subscribers |= get_subscribers(product, current_price)


The next change I would suggest is: instead of using several lists for your queues, wrap them into a single defaultdict. This simplifies this code and would require only minimal changes in your alert_users code.

It may actually be better, depending on your alert_users function, to to alert a single user at a time instead of building queues of the users and calling alert_users with a batch of users.

Here is the batch example:

from collections import defaultdict

queues = defaultdict(list)
for user_id in subscribers:
    user_document = db.users.find_one({'_id': user_id})

    queues['email_id'].append(user_document['email_id'])
    for queue_type in ['twitter', 'ios_push_id', 'droid_push_id', 'chrome_push_id']:
        queues[queue_type].append(user_document['channels'][queue_type])

alert_users(queues)


Here is the user-at-a-time example:

for user_id in subscribers:
    user_document = db.users.find_one({'_id': user_id})

    alert_user(user_document['email_id'], *user_document['channels'])

Code Snippets

def get_subscribers(product, current_price):
    tracker_document = db.trackers.find_one({'product_id': product['_id']})

    subscribers = set()
    for user_id, desired_price in tracker_document['subscribers']:
        if current_price <= desired_price:
            subscribers.add(user_id)

    return subscribers
def get_subscribers(product, current_price):
    tracker_document = db.trackers.find_one({'product_id': product['_id']})

    return set([user_id for user_id, desired_price in tracker_document['subscribers']
                if current_price <= desired_price])
subscribers = set()
for product in product.collection:
    current_price = get_current_price(product['url'])
    if current_price == product['current_price']:
        continue

    product['history'].append((datetime.datetime.utcnow(), current_price))
    product.collection.save(product)

    subscribers |= get_subscribers(product, current_price)
from collections import defaultdict

queues = defaultdict(list)
for user_id in subscribers:
    user_document = db.users.find_one({'_id': user_id})

    queues['email_id'].append(user_document['email_id'])
    for queue_type in ['twitter', 'ios_push_id', 'droid_push_id', 'chrome_push_id']:
        queues[queue_type].append(user_document['channels'][queue_type])

alert_users(queues)
for user_id in subscribers:
    user_document = db.users.find_one({'_id': user_id})

    alert_user(user_document['email_id'], *user_document['channels'])

Context

StackExchange Code Review Q#55674, answer score: 2

Revisions (0)

No revisions yet.