Subversion Repositories SmartDukaan

Rev

Rev 14081 | Rev 14213 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
13569 amit.gupta 1
'''
2
Created on Jan 15, 2015
3
 
4
@author: amit
5
'''
13724 amit.gupta 6
from datetime import datetime
13869 amit.gupta 7
import time
13868 amit.gupta 8
from pprint import pprint
13569 amit.gupta 9
from pymongo.mongo_client import MongoClient
10
import importlib
13662 amit.gupta 11
import json
12
import math
13569 amit.gupta 13
import mechanize
13631 amit.gupta 14
import traceback
13662 amit.gupta 15
import urllib
16
import urllib2
14145 amit.gupta 17
from dtr.config import PythonPropertyReader
13582 amit.gupta 18
sourceMap = {1:"amazon", 2:"flipkart", 3:"snapdeal", 4:"spice", 5:"homeshop18"}
13662 amit.gupta 19
headers = { 
20
           'User-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
21
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',      
22
            'Accept-Language' : 'en-US,en;q=0.8',                     
23
            'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'
24
        }
14145 amit.gupta 25
CASHBACK_URL = PythonPropertyReader.getConfig('CASHBACK_URL')
26
USER_LOOKUP_URL = PythonPropertyReader.getConfig('USER_LOOKUP_URL')
27
WALLET_CREDIT_URL = PythonPropertyReader.getConfig('WALLET_CREDIT_URL')
13569 amit.gupta 28
 
29
def getStore(source_id):
30
    #module = sourceMap[source_id]
31
    store = Store(source_id)
13631 amit.gupta 32
    try:
33
        module = importlib.import_module("dtr.sources." + sourceMap[source_id])
34
        store = getattr(module, "Store")(source_id)
35
        return store
36
    except:
13781 amit.gupta 37
        traceback.print_exc()
13631 amit.gupta 38
        return None
13569 amit.gupta 39
 
40
class ScrapeException(Exception):
41
    """Exception raised for errors in the input.
42
 
43
    Attributes:
44
        expr -- input expression in which the error occurred
45
        msg  -- explanation of the error
46
    """
47
 
48
    def __init__(self, expr, msg):
49
        self.expr = expr
50
        self.msg = msg
51
 
52
class ParseException(Exception):
53
    """Exception raised for errors in the input.
54
 
55
    Attributes:
56
        expr -- input expression in which the error occurred
57
        msg  -- explanation of the error
58
    """
59
 
60
    def __init__(self, expr, msg):
61
        self.expr = expr
62
        self.msg = msg
63
 
13677 amit.gupta 64
client = MongoClient('mongodb://localhost:27017/') 
65
 
13569 amit.gupta 66
class Store(object):
67
 
68
    ORDER_PLACED = 'Order Placed'
69
    ORDER_DELIVERED = 'Delivered'
70
    ORDER_SHIPPED = 'Shipped' #Lets see if we can make use of it
71
    ORDER_CANCELLED = 'Cancelled'
72
 
13662 amit.gupta 73
    CB_INIT = 'Waiting Confirmation'
13610 amit.gupta 74
    CB_PENDING = 'Pending'
13955 amit.gupta 75
    CB_CREDIT_IN_PROCESS = 'Credit in process'
13610 amit.gupta 76
    CB_CREDITED = 'Credited to wallet'
77
    CB_NA = 'Not Applicable'
78
    CB_APPROVED = 'Approved'
79
    CB_CANCELLED = 'Cancelled'
13569 amit.gupta 80
 
13662 amit.gupta 81
    CONF_CB_SELLING_PRICE = 0
82
    CONF_CB_DISCOUNTED_PRICE = 1
13610 amit.gupta 83
 
13569 amit.gupta 84
    def __init__(self, store_id):
13662 amit.gupta 85
        self.db = client.Dtr
13569 amit.gupta 86
        self.store_id = store_id
13576 amit.gupta 87
        self.store_name = sourceMap[store_id]
13569 amit.gupta 88
 
13662 amit.gupta 89
    '''
90
    To Settle payback for respective stores.
91
    Also ensures that settlement happens only for approved orders
92
    '''
13569 amit.gupta 93
 
94
    def getName(self):
95
        raise NotImplementedError
96
 
97
    def scrapeAffiliate(self, startDate=None, endDate=None):
98
        raise NotImplementedError
99
 
100
    def saveToAffiliate(self, offers):
101
        raise NotImplementedError
102
 
103
    def scrapeStoreOrders(self,):
104
        raise NotImplementedError
13610 amit.gupta 105
 
13690 amit.gupta 106
    def _saveToOrder(self, order):
107
        collection = self.db.merchantOrder
108
        try:
109
            order = collection.insert(order)
110
            #merchantOder 
111
        except Exception as e:
112
            traceback.print_exc()
14081 amit.gupta 113
 
114
    def _updateToOrder(self, order):
115
        collection = self.db.merchantOrder
116
        try:
117
            collection.update({"orderId":order['orderId']},{"$set":order}, upsert = True)
118
            #merchantOder 
119
        except Exception as e:
120
            traceback.print_exc()
13662 amit.gupta 121
 
122
    def getCashbackAmount(self, productCode, amount):
13995 amit.gupta 123
        alagvar = CASHBACK_URL % (productCode,self.store_id)
13662 amit.gupta 124
        filehandle = urllib2.Request(alagvar,headers=headers)
125
        x= urllib2.urlopen(filehandle)
13995 amit.gupta 126
        rmap = json.loads(str(x.read()))
127
        if len(rmap)==0:
13721 amit.gupta 128
            return (0,0)
13662 amit.gupta 129
        else:
13995 amit.gupta 130
            if rmap['cash_back_description'] == 'PERCENTAGE':
131
                return (math.floor((amount * rmap['cash_back_status'])/100), rmap['cash_back_status'])
13662 amit.gupta 132
            else:
13995 amit.gupta 133
                return (rmap['cash_back_status'], 0)
13721 amit.gupta 134
 
13662 amit.gupta 135
 
13569 amit.gupta 136
    '''
137
    Parses the order for specific store
138
 
139
    order id, total amount, created on(now() if could not parse
140
    suborder id, title, quantity, unit price, expected delivery date,
141
    status (default would be Order placed)
142
 
143
    once products are identified, each suborder can then be updated
144
    with respective cashback.
145
 
146
    Possible fields to display for Not yet delivered orders are 
147
    Product/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatus
148
    No need to show cancelled orders.
13610 amit.gupta 149
    CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWallet
13569 amit.gupta 150
    OrderStatus - Placed/Cancelled/Delivered
151
    '''
13576 amit.gupta 152
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13569 amit.gupta 153
 
154
        pass
155
 
13721 amit.gupta 156
    def _updateOrdersPayBackStatus(self, searchMap, updateMap):
13781 amit.gupta 157
        searchMap['subOrders.missingAff'] = False
158
        updateMap['subOrders.$.missingAff'] = True
13927 amit.gupta 159
        self.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)
13721 amit.gupta 160
 
13781 amit.gupta 161
    def _getActiveOrders(self, searchMap={}, collectionMap={}):
13721 amit.gupta 162
        collection = self.db.merchantOrder
13781 amit.gupta 163
        searchMap = dict(searchMap.items()+ {"closed": False, "storeId" : self.store_id}.items()) 
164
        collectionMap =  dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1}.items())
165
        stores = collection.find(searchMap, collectionMap)
13721 amit.gupta 166
        return [store for store in stores]
167
 
14081 amit.gupta 168
    def _getMissingOrders(self,searchMap={}):
169
        collection = self.db.merchantOrder
170
        searchMap = dict(searchMap.items()+ {"subOrders":{"$exists":False}}.items()) 
171
        orders = collection.find(searchMap)
172
        return list(orders)
173
 
174
 
13721 amit.gupta 175
    def _isSubOrderActive(self,order, merchantSubOrderId):
176
        subOrders = order.get("subOrders")
177
        for subOrder in subOrders:
178
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
179
                return subOrder
180
        return None
181
 
13868 amit.gupta 182
def settlePayBack():
13930 amit.gupta 183
        client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
13868 amit.gupta 184
        result = client.Dtr.merchantOrder\
185
            .aggregate([
13927 amit.gupta 186
                        {'$match':{'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}},
13868 amit.gupta 187
                        {'$unwind':"$subOrders"},
188
                        { 
189
                         '$group':{
190
                                   '_id':'$userId',
191
                                   'amount': { '$sum':'$subOrders.cashBackAmount'},
192
                                   }
193
                         }
194
                    ])['result']
195
 
196
        userAmountMap = {}
13953 amit.gupta 197
        print result
13868 amit.gupta 198
        for res in result:
13927 amit.gupta 199
            userAmountMap[res['_id']] = res['amount']
200
        datetimeNow = datetime.now() 
201
        batchId = int(time.mktime(datetimeNow.timetuple()))
202
        if refundToWallet(batchId, userAmountMap):
13930 amit.gupta 203
            client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDITED, 'subOrders.$.batchId':batchId}}, multi=True)
13951 amit.gupta 204
            for key, value in userAmountMap.iteritems():
13927 amit.gupta 205
                client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S")})
206
                client.Dtr.user.update({"userId":key}, {'$inc': { "credited": value}}, upsert=True)
13952 amit.gupta 207
            tprint("PayBack Settled")
13868 amit.gupta 208
        else:
13939 amit.gupta 209
            tprint("Error Occurred while running batch. Rolling Back")
13930 amit.gupta 210
            client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS},{'$set':{'subOrders.$.cashBackStatus':Store.CB_APPROVED}}, multi=True)    
13721 amit.gupta 211
 
13927 amit.gupta 212
def refundToWallet(batchId, userAmountMap):
213
    batchUpdateMap = {}
214
    try :
215
        saholicUserAmountMap = {}
216
        for key, value in userAmountMap.iteritems():
217
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
218
            try:
219
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 220
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 221
                saholicUserAmountMap[saholicUserId] = value
222
            except:
13934 amit.gupta 223
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 224
                continue
13939 amit.gupta 225
        if len(saholicUserAmountMap) > 0:
226
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
227
            batchUpdateMap['batchId'] = batchId
228
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
229
            data = urllib.urlencode(batchUpdateMap)
230
            response = urllib2.urlopen(request, data)
231
            return json.loads(response.read())['response']['credited']
232
        else:
233
            tprint("Nothing to Refund")
234
            return False
13927 amit.gupta 235
    except:
236
        traceback.print_exc()
237
        tprint("Could not batch refund")
238
        return False
13868 amit.gupta 239
 
13662 amit.gupta 240
def main():
13995 amit.gupta 241
    store = getStore(3)
242
    print store.getCashbackAmount('832308321', 100)
13868 amit.gupta 243
    #data = urllib.urlencode({'orderId':6000, 'amount':200})
244
    #request = urllib2.Request(WALLET_CREDIT_URL % (483649), headers=headers)
245
    #response = urllib2.urlopen(request, data)
246
    #print response.read()
13995 amit.gupta 247
    #settlePayBack()
13939 amit.gupta 248
 
13662 amit.gupta 249
 
13569 amit.gupta 250
 
251
 
13868 amit.gupta 252
###
253
#Settlement process is suposed to be a batch and run weekly
254
#It is should be running on first hour of mondays. As cron should
255
# Maintain a batch id.
256
 
13677 amit.gupta 257
 
13569 amit.gupta 258
def getBrowserObject():
259
    import cookielib
260
    br = mechanize.Browser(factory=mechanize.RobustFactory())
261
    cj = cookielib.LWPCookieJar()
262
    br.set_cookiejar(cj)
263
    br.set_handle_equiv(True)
264
    br.set_handle_redirect(True)
265
    br.set_handle_referer(True)
266
    br.set_handle_robots(False)
267
    br.set_debug_http(False)
268
    br.set_debug_redirects(False)
269
    br.set_debug_responses(False)
270
 
271
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
272
 
273
    br.addheaders = [('User-agent','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11'),
274
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
275
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
276
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
277
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 278
    return br
279
 
13690 amit.gupta 280
def ungzipResponse(r):
281
    headers = r.info()
282
    if headers['Content-Encoding']=='gzip':
283
        import gzip
284
        gz = gzip.GzipFile(fileobj=r, mode='rb')
285
        html = gz.read()
286
        gz.close()
287
        return html
13724 amit.gupta 288
 
289
def tprint(*msg):
13927 amit.gupta 290
    print datetime.now(), "-", msg
13939 amit.gupta 291
 
292
if __name__ == '__main__':
293
    main()