Subversion Repositories SmartDukaan

Rev

Rev 16390 | Rev 16736 | 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
'''
14618 amit.gupta 6
from datetime import datetime, timedelta
15078 amit.gupta 7
from dtr.config import PythonPropertyReader
16394 amit.gupta 8
from dtr.storage.Mongo import getDealRank
14618 amit.gupta 9
from dtr.storage.Mysql import getOrdersAfterDate1, getOrdersAfterDate
13868 amit.gupta 10
from pprint import pprint
13569 amit.gupta 11
from pymongo.mongo_client import MongoClient
14413 amit.gupta 12
import base64
13569 amit.gupta 13
import importlib
13662 amit.gupta 14
import json
15
import math
13569 amit.gupta 16
import mechanize
14413 amit.gupta 17
import time
13631 amit.gupta 18
import traceback
13662 amit.gupta 19
import urllib
20
import urllib2
16371 amit.gupta 21
sourceMap = {1:"amazon", 2:"flipkart", 3:"snapdeal", 4:"spice", 5:"shopclues", 6:"paytm"}
13662 amit.gupta 22
headers = { 
23
           'User-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
24
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',      
25
            'Accept-Language' : 'en-US,en;q=0.8',                     
26
            'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'
27
        }
14145 amit.gupta 28
CASHBACK_URL = PythonPropertyReader.getConfig('CASHBACK_URL')
29
USER_LOOKUP_URL = PythonPropertyReader.getConfig('USER_LOOKUP_URL')
30
WALLET_CREDIT_URL = PythonPropertyReader.getConfig('WALLET_CREDIT_URL')
13569 amit.gupta 31
 
32
def getStore(source_id):
33
    #module = sourceMap[source_id]
34
    store = Store(source_id)
13631 amit.gupta 35
    try:
36
        module = importlib.import_module("dtr.sources." + sourceMap[source_id])
37
        store = getattr(module, "Store")(source_id)
38
        return store
39
    except:
13781 amit.gupta 40
        traceback.print_exc()
13631 amit.gupta 41
        return None
13569 amit.gupta 42
 
43
class ScrapeException(Exception):
44
    """Exception raised for errors in the input.
45
 
46
    Attributes:
47
        expr -- input expression in which the error occurred
48
        msg  -- explanation of the error
49
    """
50
 
51
    def __init__(self, expr, msg):
52
        self.expr = expr
53
        self.msg = msg
54
 
55
class ParseException(Exception):
56
    """Exception raised for errors in the input.
57
 
58
    Attributes:
59
        expr -- input expression in which the error occurred
60
        msg  -- explanation of the error
61
    """
62
 
63
    def __init__(self, expr, msg):
64
        self.expr = expr
65
        self.msg = msg
66
 
13677 amit.gupta 67
client = MongoClient('mongodb://localhost:27017/') 
68
 
13569 amit.gupta 69
class Store(object):
70
 
71
    ORDER_PLACED = 'Order Placed'
72
    ORDER_DELIVERED = 'Delivered'
73
    ORDER_SHIPPED = 'Shipped' #Lets see if we can make use of it
74
    ORDER_CANCELLED = 'Cancelled'
75
 
13662 amit.gupta 76
    CB_INIT = 'Waiting Confirmation'
13610 amit.gupta 77
    CB_PENDING = 'Pending'
13955 amit.gupta 78
    CB_CREDIT_IN_PROCESS = 'Credit in process'
13610 amit.gupta 79
    CB_CREDITED = 'Credited to wallet'
80
    CB_NA = 'Not Applicable'
81
    CB_APPROVED = 'Approved'
14618 amit.gupta 82
    CB_REJECTED = 'Rejected'
83
    CB_ONHOLD = 'On hold'
13610 amit.gupta 84
    CB_CANCELLED = 'Cancelled'
13569 amit.gupta 85
 
13662 amit.gupta 86
    CONF_CB_SELLING_PRICE = 0
87
    CONF_CB_DISCOUNTED_PRICE = 1
13610 amit.gupta 88
 
13569 amit.gupta 89
    def __init__(self, store_id):
13662 amit.gupta 90
        self.db = client.Dtr
13569 amit.gupta 91
        self.store_id = store_id
13576 amit.gupta 92
        self.store_name = sourceMap[store_id]
13569 amit.gupta 93
 
13662 amit.gupta 94
    '''
95
    To Settle payback for respective stores.
96
    Also ensures that settlement happens only for approved orders
97
    '''
13569 amit.gupta 98
 
99
    def getName(self):
100
        raise NotImplementedError
101
 
102
    def scrapeAffiliate(self, startDate=None, endDate=None):
103
        raise NotImplementedError
104
 
16390 amit.gupta 105
    def getTrackingUrls(self, userId):
106
        raise NotImplementedError
107
 
108
 
13569 amit.gupta 109
    def saveToAffiliate(self, offers):
110
        raise NotImplementedError
111
 
16371 amit.gupta 112
    def getUpdateMap(self, subOrder, cashBackStatus):
14624 amit.gupta 113
        updateMap = {}
114
        closed = False
115
        for (key,value) in subOrder.iteritems():
116
            if key=="status":
117
                if value==Store.ORDER_CANCELLED:
118
                    closed = True
119
                    updateMap['subOrders.$.closed'] = True
16371 amit.gupta 120
                    if cashBackStatus == Store.CB_PENDING:
14624 amit.gupta 121
                        updateMap['subOrders.$.cashBackStatus'] = Store.CB_CANCELLED
122
                if value==Store.ORDER_DELIVERED:
123
                    closed = True
124
                    updateMap['subOrders.$.closed'] = True
16371 amit.gupta 125
                    if cashBackStatus == Store.CB_PENDING:
14624 amit.gupta 126
                        updateMap['subOrders.$.cashBackStatus'] = Store.CB_APPROVED
127
            updateMap['subOrders.$.' + key] = value
128
        subOrder['closed'] = closed
129
        return updateMap
130
 
14618 amit.gupta 131
    def saveOrder(self, merchantOrder):
132
        #merchantOrder = Order()
133
        #merchantOrder.status
134
        #mercha
135
        pass
136
 
137
 
13569 amit.gupta 138
    def scrapeStoreOrders(self,):
139
        raise NotImplementedError
13610 amit.gupta 140
 
14618 amit.gupta 141
    def updateSubOrder(self, subOrder):
142
        pass
143
        #if subOrder.get
144
 
13690 amit.gupta 145
    def _saveToOrder(self, order):
146
        collection = self.db.merchantOrder
147
        try:
148
            order = collection.insert(order)
14273 amit.gupta 149
            return True 
13690 amit.gupta 150
        except Exception as e:
14273 amit.gupta 151
            return False
14081 amit.gupta 152
 
153
    def _updateToOrder(self, order):
154
        collection = self.db.merchantOrder
155
        try:
14959 amit.gupta 156
            print "************************************************************************"
157
            print "order"
158
            print order
159
            print "************************************************************************"
14081 amit.gupta 160
            collection.update({"orderId":order['orderId']},{"$set":order}, upsert = True)
161
            #merchantOder 
162
        except Exception as e:
163
            traceback.print_exc()
13662 amit.gupta 164
 
165
    def getCashbackAmount(self, productCode, amount):
13995 amit.gupta 166
        alagvar = CASHBACK_URL % (productCode,self.store_id)
13662 amit.gupta 167
        filehandle = urllib2.Request(alagvar,headers=headers)
168
        x= urllib2.urlopen(filehandle)
13995 amit.gupta 169
        rmap = json.loads(str(x.read()))
170
        if len(rmap)==0:
13721 amit.gupta 171
            return (0,0)
13662 amit.gupta 172
        else:
13995 amit.gupta 173
            if rmap['cash_back_description'] == 'PERCENTAGE':
14295 amit.gupta 174
                return (math.floor((amount * rmap['cash_back'])/100), rmap['cash_back'])
13662 amit.gupta 175
            else:
14295 amit.gupta 176
                return (rmap['cash_back'], 0)
13721 amit.gupta 177
 
13662 amit.gupta 178
 
13569 amit.gupta 179
    '''
180
    Parses the order for specific store
181
 
182
    order id, total amount, created on(now() if could not parse
183
    suborder id, title, quantity, unit price, expected delivery date,
184
    status (default would be Order placed)
185
 
186
    once products are identified, each suborder can then be updated
187
    with respective cashback.
188
 
189
    Possible fields to display for Not yet delivered orders are 
190
    Product/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatus
191
    No need to show cancelled orders.
13610 amit.gupta 192
    CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWallet
13569 amit.gupta 193
    OrderStatus - Placed/Cancelled/Delivered
194
    '''
13576 amit.gupta 195
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13569 amit.gupta 196
 
197
        pass
198
 
13721 amit.gupta 199
    def _updateOrdersPayBackStatus(self, searchMap, updateMap):
13781 amit.gupta 200
        searchMap['subOrders.missingAff'] = False
201
        updateMap['subOrders.$.missingAff'] = True
13927 amit.gupta 202
        self.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)
13721 amit.gupta 203
 
13781 amit.gupta 204
    def _getActiveOrders(self, searchMap={}, collectionMap={}):
13721 amit.gupta 205
        collection = self.db.merchantOrder
13781 amit.gupta 206
        searchMap = dict(searchMap.items()+ {"closed": False, "storeId" : self.store_id}.items()) 
14274 amit.gupta 207
        collectionMap =  dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1}.items())
208
        stores = collection.find(searchMap, collectionMap)
13721 amit.gupta 209
        return [store for store in stores]
210
 
14081 amit.gupta 211
    def _getMissingOrders(self,searchMap={}):
212
        collection = self.db.merchantOrder
14948 amit.gupta 213
        searchMap = dict(searchMap.items()+ {"requireDetail":True, "storeId":self.store_id}.items()) 
14081 amit.gupta 214
        orders = collection.find(searchMap)
215
        return list(orders)
216
 
217
 
13721 amit.gupta 218
    def _isSubOrderActive(self,order, merchantSubOrderId):
219
        subOrders = order.get("subOrders")
14867 amit.gupta 220
        if subOrders is None:
221
            return None
13721 amit.gupta 222
        for subOrder in subOrders:
223
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
224
                return subOrder
225
        return None
14663 amit.gupta 226
 
16371 amit.gupta 227
    def populateDerivedFields(self, order):
228
        closed=True
229
        for subOrder in order.subOrders:
230
            if subOrder.closed:
231
                continue
232
            cashbackAmount, cashbackPercent = self.getCashbackAmount(subOrder.productCode, subOrder.amountPaid)
233
            cashbackStatus = Store.CB_PENDING
234
            if cashbackAmount <= 0:
235
                cashbackStatus = Store.CB_NA
236
            subOrder.cashBackAmount = cashbackAmount
237
            subOrder.cashBackPercentage = cashbackPercent
238
            subOrder.cashBackStatus = cashbackStatus      
239
            subOrder.closed = subOrder.status in [self.ORDER_CANCELLED, self.ORDER_DELIVERED]
240
            closed = closed and subOrder.closed
16394 amit.gupta 241
            dealRank = getDealRank(subOrder.productCode, self.store_id, order.userId)
242
            subOrder.dealRank = dealRank.get('rank')
243
            subOrder.rankDesc = dealRank.get('description')
244
            subOrder.maxNlc = dealRank.get('maxNlc')
245
            subOrder.minNlc = dealRank.get('minNlc')
246
            subOrder.db = dealRank.get('dp')
247
            subOrder.itemStatus = dealRank.get('status')
16371 amit.gupta 248
        order.closed = closed
249
 
14618 amit.gupta 250
def settlePayBack(runtype='dry'):
14663 amit.gupta 251
    userAmountMap = {}
252
    searchMapList = []
14666 amit.gupta 253
    for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_APPROVED}):
14663 amit.gupta 254
        userId = mo.get("userId")
14664 amit.gupta 255
        if   mo.get('subOrders') is not None:
14663 amit.gupta 256
            for so in mo['subOrders']:
14666 amit.gupta 257
                if so.get('cashBackStatus') == Store.CB_APPROVED:
14663 amit.gupta 258
                    searchMapList.append({"orderId":mo.get("orderId"), "subOrders.merchantSubOrderId":so.get("merchantSubOrderId")})
259
                    if not userAmountMap.has_key(userId):
260
                        userAmountMap[userId] = so.get('cashBackAmount')
261
                    else:
262
                        userAmountMap[userId] += so.get('cashBackAmount')
263
                    print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))
264
                else:  
265
                    print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))
266
    for searchMap in searchMapList:
267
        print "%s\t%s"%(searchMap.get('orderId'),searchMap.get('subOrders.merchantSubOrderId'))
14664 amit.gupta 268
    for key,val in userAmountMap.iteritems():
269
        print "%s\t%s"%(key,val)
14663 amit.gupta 270
    if (runtype=='live'):
14666 amit.gupta 271
        bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
14663 amit.gupta 272
        if len(searchMapList) == 0:
14618 amit.gupta 273
            return
14666 amit.gupta 274
        for searchMap in searchMapList:
275
            bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}})
276
        bulk.execute()
14663 amit.gupta 277
 
13927 amit.gupta 278
        datetimeNow = datetime.now() 
279
        batchId = int(time.mktime(datetimeNow.timetuple()))
280
        if refundToWallet(batchId, userAmountMap):
14666 amit.gupta 281
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
282
            for searchMap in searchMapList:
283
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDITED, "subOrders.$.batchId":batchId}})
284
            print bulk.execute()
285
            for key, value in userAmountMap.iteritems():
286
                client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S")})
287
                client.Dtr.user.update({"userId":key}, {'$inc': { "credited": value}}, upsert=True)
13952 amit.gupta 288
            tprint("PayBack Settled")
14666 amit.gupta 289
        else:
290
            tprint("Error Occurred while running batch. Rolling Back")
291
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
292
            for searchMap in searchMapList:
293
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_APPROVED}})
294
            bulk.execute()
14663 amit.gupta 295
 
296
 
297
def settlePayBack1(runtype='dry'):
298
        approvedUserMap = {}
299
        pendingUserMap = {}
300
        creditedToWalletUserMap = {}
301
        #client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
302
        for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_CREDITED}):
303
            userId = mo.get("userId")
304
            for so in mo['subOrders']:
305
                print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))
306
 
307
        for refund in client.Dtr.refund.find():
308
            print "%s\t%s\t%s\t%s"%(refund.get("userId") ,refund.get("timestamp") ,refund.get("userAmount"), refund.get("batch"))
309
 
310
 
13927 amit.gupta 311
def refundToWallet(batchId, userAmountMap):
14413 amit.gupta 312
    base64string = base64.encodestring('%s:%s' % ("dtr", "dtr18Feb2015")).replace('\n', '')
13927 amit.gupta 313
    batchUpdateMap = {}
314
    try :
315
        saholicUserAmountMap = {}
316
        for key, value in userAmountMap.iteritems():
317
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
14413 amit.gupta 318
            userLookupRequest.add_header("Authorization", "Basic %s" % base64string)
13927 amit.gupta 319
            try:
320
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 321
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 322
                saholicUserAmountMap[saholicUserId] = value
323
            except:
13934 amit.gupta 324
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 325
                continue
13939 amit.gupta 326
        if len(saholicUserAmountMap) > 0:
327
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
328
            batchUpdateMap['batchId'] = batchId
329
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
330
            data = urllib.urlencode(batchUpdateMap)
331
            response = urllib2.urlopen(request, data)
332
            return json.loads(response.read())['response']['credited']
333
        else:
334
            tprint("Nothing to Refund")
335
            return False
13927 amit.gupta 336
    except:
337
        traceback.print_exc()
338
        tprint("Could not batch refund")
339
        return False
13868 amit.gupta 340
 
13662 amit.gupta 341
def main():
14618 amit.gupta 342
#    client = MongoClient('mongodb://root:ecip$dtrMay2014@dtr:27017/')
343
#    s = Store(1)
344
#    orders = s.db.Dtr.find({"subOrders.imgUrl":{"$exists":1}}, {"merchantOrderId":1, "subOrders.productCode":1,"subOrders.imgUrl":1,"_id": 0})
345
#    db = client.Dtr
346
#    for order in orders:
347
#        for subOrder in order.get("subOrders"):
348
#            #db.merchantOrder.update({"merchantOrderId":order.getMerchantOrderId,"subOrders.imgUrl":{"$exists":0}, "subOrders.productCode":subOrder.get("productCode")}, {"$set":{"subOrders.$.imgUrl":subOrder.get("imgUrl")}})
349
#            db.merchantOrder.findOne()
14663 amit.gupta 350
    settlePayBack('dry')        
13569 amit.gupta 351
 
352
 
13868 amit.gupta 353
###
354
#Settlement process is suposed to be a batch and run weekly
355
#It is should be running on first hour of mondays. As cron should
356
# Maintain a batch id.
357
 
13677 amit.gupta 358
 
13569 amit.gupta 359
def getBrowserObject():
360
    import cookielib
361
    br = mechanize.Browser(factory=mechanize.RobustFactory())
362
    cj = cookielib.LWPCookieJar()
363
    br.set_cookiejar(cj)
364
    br.set_handle_equiv(True)
365
    br.set_handle_redirect(True)
366
    br.set_handle_referer(True)
367
    br.set_handle_robots(False)
368
    br.set_debug_http(False)
369
    br.set_debug_redirects(False)
370
    br.set_debug_responses(False)
371
 
372
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
373
 
374
    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'),
375
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
376
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
377
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
378
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 379
    return br
16371 amit.gupta 380
def todict(obj, classkey=None):
381
    if isinstance(obj, dict):
382
        data = {}
383
        for (k, v) in obj.items():
384
            data[k] = todict(v, classkey)
385
        return data
386
    elif hasattr(obj, "_ast"):
387
        return todict(obj._ast())
388
    elif hasattr(obj, "__iter__"):
389
        return [todict(v, classkey) for v in obj]
390
    elif hasattr(obj, "__dict__"):
391
        data = dict([(key, todict(value, classkey)) 
392
            for key, value in obj.__dict__.iteritems() 
393
            if not callable(value) and not key.startswith('_')])
394
        if classkey is not None and hasattr(obj, "__class__"):
395
            data[classkey] = obj.__class__.__name__
396
        return data
397
    else:
398
        return obj
13677 amit.gupta 399
 
13690 amit.gupta 400
def ungzipResponse(r):
401
    headers = r.info()
402
    if headers['Content-Encoding']=='gzip':
403
        import gzip
404
        gz = gzip.GzipFile(fileobj=r, mode='rb')
405
        html = gz.read()
406
        gz.close()
407
        return html
13724 amit.gupta 408
 
409
def tprint(*msg):
13927 amit.gupta 410
    print datetime.now(), "-", msg
13939 amit.gupta 411
 
412
if __name__ == '__main__':
14848 amit.gupta 413
    main()
414