patternpythonMinor
E-commerce product price tracker
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
in
and lastly
```
{
_id: ObjectId("77a2bfcfa7603606c2765399"),
product_id: ObjectId("53a2bfcfa7603606c2765342"),
subscribers: [
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:
This could even be condensed even more using a list comprehension:
This change makes your code look like this:
The next change I would suggest is: instead of using several lists for your queues, wrap them into a single
It may actually be better, depending on your
Here is the batch example:
Here is the user-at-a-time example:
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 subscribersThis 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 subscribersdef 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.