Subversion Repositories SmartDukaan

Rev

Rev 17179 | Rev 17388 | 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:
149
            order = collection.insert(order)
14273 amit.gupta 150
            return True 
13690 amit.gupta 151
        except Exception as e:
14273 amit.gupta 152
            return False
14081 amit.gupta 153
 
154
    def _updateToOrder(self, order):
155
        collection = self.db.merchantOrder
156
        try:
14959 amit.gupta 157
            print "************************************************************************"
158
            print "order"
159
            print order
160
            print "************************************************************************"
14081 amit.gupta 161
            collection.update({"orderId":order['orderId']},{"$set":order}, upsert = True)
162
            #merchantOder 
163
        except Exception as e:
164
            traceback.print_exc()
13662 amit.gupta 165
 
166
    def getCashbackAmount(self, productCode, amount):
13995 amit.gupta 167
        alagvar = CASHBACK_URL % (productCode,self.store_id)
13662 amit.gupta 168
        filehandle = urllib2.Request(alagvar,headers=headers)
169
        x= urllib2.urlopen(filehandle)
17179 amit.gupta 170
        cashBack = json.loads(str(x.read()))
171
        if len(cashBack)==0:
13721 amit.gupta 172
            return (0,0)
13662 amit.gupta 173
        else:
17179 amit.gupta 174
            maxCashBack = cashBack.get('maxCashBack') 
175
            if maxCashBack:
176
                if cashBack.get('cash_back_type') ==1 and (float(cashBack.get('cash_back'))*amount)/100 > maxCashBack:
177
                    cashBack['cash_back'] = cashBack['maxCashBack']
178
                    return (cashBack['cash_back'], 0)
179
                elif cashBack.get('cash_back_type') ==2 and cashBack.get('cash_back') > cashBack.get('maxCashBack'):
180
                    cashBack['cash_back'] = cashBack['maxCashBack']
181
                    return (cashBack['cash_back'], 0)
182
                else:
183
                    pass
184
            if cashBack['cash_back_description'] == 'PERCENTAGE':
185
                return (math.floor((amount * cashBack['cash_back'])/100), cashBack['cash_back'])
13662 amit.gupta 186
            else:
17179 amit.gupta 187
                return (cashBack['cash_back'], 0)
13721 amit.gupta 188
 
13662 amit.gupta 189
 
13569 amit.gupta 190
    '''
191
    Parses the order for specific store
192
 
193
    order id, total amount, created on(now() if could not parse
194
    suborder id, title, quantity, unit price, expected delivery date,
195
    status (default would be Order placed)
196
 
197
    once products are identified, each suborder can then be updated
198
    with respective cashback.
199
 
200
    Possible fields to display for Not yet delivered orders are 
201
    Product/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatus
202
    No need to show cancelled orders.
13610 amit.gupta 203
    CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWallet
13569 amit.gupta 204
    OrderStatus - Placed/Cancelled/Delivered
205
    '''
13576 amit.gupta 206
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13569 amit.gupta 207
 
208
        pass
209
 
13721 amit.gupta 210
    def _updateOrdersPayBackStatus(self, searchMap, updateMap):
13781 amit.gupta 211
        searchMap['subOrders.missingAff'] = False
212
        updateMap['subOrders.$.missingAff'] = True
13927 amit.gupta 213
        self.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)
13721 amit.gupta 214
 
13781 amit.gupta 215
    def _getActiveOrders(self, searchMap={}, collectionMap={}):
13721 amit.gupta 216
        collection = self.db.merchantOrder
17234 amit.gupta 217
        searchMap = dict(searchMap.items()+ {"subOrders.closed": False, "storeId" : self.store_id}.items()) 
14274 amit.gupta 218
        collectionMap =  dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1}.items())
219
        stores = collection.find(searchMap, collectionMap)
13721 amit.gupta 220
        return [store for store in stores]
221
 
14081 amit.gupta 222
    def _getMissingOrders(self,searchMap={}):
223
        collection = self.db.merchantOrder
14948 amit.gupta 224
        searchMap = dict(searchMap.items()+ {"requireDetail":True, "storeId":self.store_id}.items()) 
14081 amit.gupta 225
        orders = collection.find(searchMap)
226
        return list(orders)
227
 
228
 
13721 amit.gupta 229
    def _isSubOrderActive(self,order, merchantSubOrderId):
230
        subOrders = order.get("subOrders")
14867 amit.gupta 231
        if subOrders is None:
232
            return None
13721 amit.gupta 233
        for subOrder in subOrders:
234
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
235
                return subOrder
236
        return None
14663 amit.gupta 237
 
16371 amit.gupta 238
    def populateDerivedFields(self, order):
239
        closed=True
240
        for subOrder in order.subOrders:
241
            if subOrder.closed:
242
                continue
243
            cashbackAmount, cashbackPercent = self.getCashbackAmount(subOrder.productCode, subOrder.amountPaid)
244
            cashbackStatus = Store.CB_PENDING
245
            if cashbackAmount <= 0:
246
                cashbackStatus = Store.CB_NA
247
            subOrder.cashBackAmount = cashbackAmount
248
            subOrder.cashBackPercentage = cashbackPercent
249
            subOrder.cashBackStatus = cashbackStatus      
250
            subOrder.closed = subOrder.status in [self.ORDER_CANCELLED, self.ORDER_DELIVERED]
251
            closed = closed and subOrder.closed
16394 amit.gupta 252
            dealRank = getDealRank(subOrder.productCode, self.store_id, order.userId)
253
            subOrder.dealRank = dealRank.get('rank')
254
            subOrder.rankDesc = dealRank.get('description')
255
            subOrder.maxNlc = dealRank.get('maxNlc')
256
            subOrder.minNlc = dealRank.get('minNlc')
257
            subOrder.db = dealRank.get('dp')
258
            subOrder.itemStatus = dealRank.get('status')
16371 amit.gupta 259
        order.closed = closed
260
 
14618 amit.gupta 261
def settlePayBack(runtype='dry'):
14663 amit.gupta 262
    userAmountMap = {}
263
    searchMapList = []
14666 amit.gupta 264
    for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_APPROVED}):
14663 amit.gupta 265
        userId = mo.get("userId")
14664 amit.gupta 266
        if   mo.get('subOrders') is not None:
14663 amit.gupta 267
            for so in mo['subOrders']:
14666 amit.gupta 268
                if so.get('cashBackStatus') == Store.CB_APPROVED:
14663 amit.gupta 269
                    searchMapList.append({"orderId":mo.get("orderId"), "subOrders.merchantSubOrderId":so.get("merchantSubOrderId")})
270
                    if not userAmountMap.has_key(userId):
271
                        userAmountMap[userId] = so.get('cashBackAmount')
272
                    else:
273
                        userAmountMap[userId] += so.get('cashBackAmount')
274
                    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"))
275
                else:  
276
                    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"))
277
    for searchMap in searchMapList:
278
        print "%s\t%s"%(searchMap.get('orderId'),searchMap.get('subOrders.merchantSubOrderId'))
14664 amit.gupta 279
    for key,val in userAmountMap.iteritems():
280
        print "%s\t%s"%(key,val)
14663 amit.gupta 281
    if (runtype=='live'):
14666 amit.gupta 282
        bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
14663 amit.gupta 283
        if len(searchMapList) == 0:
14618 amit.gupta 284
            return
14666 amit.gupta 285
        for searchMap in searchMapList:
286
            bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}})
287
        bulk.execute()
14663 amit.gupta 288
 
13927 amit.gupta 289
        datetimeNow = datetime.now() 
290
        batchId = int(time.mktime(datetimeNow.timetuple()))
291
        if refundToWallet(batchId, userAmountMap):
14666 amit.gupta 292
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
293
            for searchMap in searchMapList:
294
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDITED, "subOrders.$.batchId":batchId}})
295
            print bulk.execute()
17234 amit.gupta 296
            sum=0
297
            creditedSubOrders=0
298
            message = []
14666 amit.gupta 299
            for key, value in userAmountMap.iteritems():
17234 amit.gupta 300
                sum += value
301
                creditedSubOrders += 1
16736 amit.gupta 302
                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})
303
                client.Dtr.user.update({"userId":key}, {"$inc": { "credited": value, utils.CREDIT_TYPE_ORDER:value}}, upsert=True)
17234 amit.gupta 304
            message.append("<b>Batch Id - %d</b><br><b>Total Amount Credited - %d</b><br><b>Total SubOrders Credited- %d</b>"%(batchId,sum, creditedSubOrders))
305
            utils.sendmail(['amit.gupta@shop2020.in', 'rajneesh.arora@saholic.com'], "".join(message), 'Cashback for Order Credited Successfully')
13952 amit.gupta 306
            tprint("PayBack Settled")
14666 amit.gupta 307
        else:
308
            tprint("Error Occurred while running batch. Rolling Back")
309
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
310
            for searchMap in searchMapList:
311
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_APPROVED}})
312
            bulk.execute()
14663 amit.gupta 313
 
314
 
315
def settlePayBack1(runtype='dry'):
316
        approvedUserMap = {}
317
        pendingUserMap = {}
318
        creditedToWalletUserMap = {}
319
        #client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
320
        for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_CREDITED}):
321
            userId = mo.get("userId")
322
            for so in mo['subOrders']:
323
                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"))
324
 
325
        for refund in client.Dtr.refund.find():
326
            print "%s\t%s\t%s\t%s"%(refund.get("userId") ,refund.get("timestamp") ,refund.get("userAmount"), refund.get("batch"))
327
 
328
 
13927 amit.gupta 329
def refundToWallet(batchId, userAmountMap):
14413 amit.gupta 330
    base64string = base64.encodestring('%s:%s' % ("dtr", "dtr18Feb2015")).replace('\n', '')
13927 amit.gupta 331
    batchUpdateMap = {}
332
    try :
333
        saholicUserAmountMap = {}
334
        for key, value in userAmountMap.iteritems():
335
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
14413 amit.gupta 336
            userLookupRequest.add_header("Authorization", "Basic %s" % base64string)
13927 amit.gupta 337
            try:
338
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 339
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 340
                saholicUserAmountMap[saholicUserId] = value
341
            except:
13934 amit.gupta 342
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 343
                continue
13939 amit.gupta 344
        if len(saholicUserAmountMap) > 0:
345
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
346
            batchUpdateMap['batchId'] = batchId
347
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
348
            data = urllib.urlencode(batchUpdateMap)
349
            response = urllib2.urlopen(request, data)
350
            return json.loads(response.read())['response']['credited']
351
        else:
352
            tprint("Nothing to Refund")
353
            return False
13927 amit.gupta 354
    except:
355
        traceback.print_exc()
356
        tprint("Could not batch refund")
357
        return False
13868 amit.gupta 358
 
13662 amit.gupta 359
def main():
14618 amit.gupta 360
#    client = MongoClient('mongodb://root:ecip$dtrMay2014@dtr:27017/')
361
#    s = Store(1)
362
#    orders = s.db.Dtr.find({"subOrders.imgUrl":{"$exists":1}}, {"merchantOrderId":1, "subOrders.productCode":1,"subOrders.imgUrl":1,"_id": 0})
363
#    db = client.Dtr
364
#    for order in orders:
365
#        for subOrder in order.get("subOrders"):
366
#            #db.merchantOrder.update({"merchantOrderId":order.getMerchantOrderId,"subOrders.imgUrl":{"$exists":0}, "subOrders.productCode":subOrder.get("productCode")}, {"$set":{"subOrders.$.imgUrl":subOrder.get("imgUrl")}})
367
#            db.merchantOrder.findOne()
14663 amit.gupta 368
    settlePayBack('dry')        
13569 amit.gupta 369
 
370
 
13868 amit.gupta 371
###
372
#Settlement process is suposed to be a batch and run weekly
373
#It is should be running on first hour of mondays. As cron should
374
# Maintain a batch id.
375
 
13677 amit.gupta 376
 
13569 amit.gupta 377
def getBrowserObject():
378
    import cookielib
379
    br = mechanize.Browser(factory=mechanize.RobustFactory())
380
    cj = cookielib.LWPCookieJar()
381
    br.set_cookiejar(cj)
382
    br.set_handle_equiv(True)
383
    br.set_handle_redirect(True)
384
    br.set_handle_referer(True)
385
    br.set_handle_robots(False)
386
    br.set_debug_http(False)
387
    br.set_debug_redirects(False)
388
    br.set_debug_responses(False)
389
 
390
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
391
 
392
    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'),
393
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
394
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
395
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
396
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 397
    return br
16371 amit.gupta 398
def todict(obj, classkey=None):
399
    if isinstance(obj, dict):
400
        data = {}
401
        for (k, v) in obj.items():
402
            data[k] = todict(v, classkey)
403
        return data
404
    elif hasattr(obj, "_ast"):
405
        return todict(obj._ast())
406
    elif hasattr(obj, "__iter__"):
407
        return [todict(v, classkey) for v in obj]
408
    elif hasattr(obj, "__dict__"):
409
        data = dict([(key, todict(value, classkey)) 
410
            for key, value in obj.__dict__.iteritems() 
411
            if not callable(value) and not key.startswith('_')])
412
        if classkey is not None and hasattr(obj, "__class__"):
413
            data[classkey] = obj.__class__.__name__
414
        return data
415
    else:
416
        return obj
13677 amit.gupta 417
 
13690 amit.gupta 418
def ungzipResponse(r):
419
    headers = r.info()
420
    if headers['Content-Encoding']=='gzip':
421
        import gzip
422
        gz = gzip.GzipFile(fileobj=r, mode='rb')
423
        html = gz.read()
424
        gz.close()
425
        return html
13724 amit.gupta 426
 
427
def tprint(*msg):
13927 amit.gupta 428
    print datetime.now(), "-", msg
13939 amit.gupta 429
 
430
if __name__ == '__main__':
14848 amit.gupta 431
    main()
432