Subversion Repositories SmartDukaan

Rev

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