Subversion Repositories SmartDukaan

Rev

Rev 17013 | Rev 17234 | 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
13781 amit.gupta 217
        searchMap = dict(searchMap.items()+ {"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()
296
            for key, value in userAmountMap.iteritems():
16736 amit.gupta 297
                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})
298
                client.Dtr.user.update({"userId":key}, {"$inc": { "credited": value, utils.CREDIT_TYPE_ORDER:value}}, upsert=True)
13952 amit.gupta 299
            tprint("PayBack Settled")
14666 amit.gupta 300
        else:
301
            tprint("Error Occurred while running batch. Rolling Back")
302
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
303
            for searchMap in searchMapList:
304
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_APPROVED}})
305
            bulk.execute()
14663 amit.gupta 306
 
307
 
308
def settlePayBack1(runtype='dry'):
309
        approvedUserMap = {}
310
        pendingUserMap = {}
311
        creditedToWalletUserMap = {}
312
        #client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
313
        for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_CREDITED}):
314
            userId = mo.get("userId")
315
            for so in mo['subOrders']:
316
                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"))
317
 
318
        for refund in client.Dtr.refund.find():
319
            print "%s\t%s\t%s\t%s"%(refund.get("userId") ,refund.get("timestamp") ,refund.get("userAmount"), refund.get("batch"))
320
 
321
 
13927 amit.gupta 322
def refundToWallet(batchId, userAmountMap):
14413 amit.gupta 323
    base64string = base64.encodestring('%s:%s' % ("dtr", "dtr18Feb2015")).replace('\n', '')
13927 amit.gupta 324
    batchUpdateMap = {}
325
    try :
326
        saholicUserAmountMap = {}
327
        for key, value in userAmountMap.iteritems():
328
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
14413 amit.gupta 329
            userLookupRequest.add_header("Authorization", "Basic %s" % base64string)
13927 amit.gupta 330
            try:
331
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 332
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 333
                saholicUserAmountMap[saholicUserId] = value
334
            except:
13934 amit.gupta 335
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 336
                continue
13939 amit.gupta 337
        if len(saholicUserAmountMap) > 0:
338
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
339
            batchUpdateMap['batchId'] = batchId
340
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
341
            data = urllib.urlencode(batchUpdateMap)
342
            response = urllib2.urlopen(request, data)
343
            return json.loads(response.read())['response']['credited']
344
        else:
345
            tprint("Nothing to Refund")
346
            return False
13927 amit.gupta 347
    except:
348
        traceback.print_exc()
349
        tprint("Could not batch refund")
350
        return False
13868 amit.gupta 351
 
13662 amit.gupta 352
def main():
14618 amit.gupta 353
#    client = MongoClient('mongodb://root:ecip$dtrMay2014@dtr:27017/')
354
#    s = Store(1)
355
#    orders = s.db.Dtr.find({"subOrders.imgUrl":{"$exists":1}}, {"merchantOrderId":1, "subOrders.productCode":1,"subOrders.imgUrl":1,"_id": 0})
356
#    db = client.Dtr
357
#    for order in orders:
358
#        for subOrder in order.get("subOrders"):
359
#            #db.merchantOrder.update({"merchantOrderId":order.getMerchantOrderId,"subOrders.imgUrl":{"$exists":0}, "subOrders.productCode":subOrder.get("productCode")}, {"$set":{"subOrders.$.imgUrl":subOrder.get("imgUrl")}})
360
#            db.merchantOrder.findOne()
14663 amit.gupta 361
    settlePayBack('dry')        
13569 amit.gupta 362
 
363
 
13868 amit.gupta 364
###
365
#Settlement process is suposed to be a batch and run weekly
366
#It is should be running on first hour of mondays. As cron should
367
# Maintain a batch id.
368
 
13677 amit.gupta 369
 
13569 amit.gupta 370
def getBrowserObject():
371
    import cookielib
372
    br = mechanize.Browser(factory=mechanize.RobustFactory())
373
    cj = cookielib.LWPCookieJar()
374
    br.set_cookiejar(cj)
375
    br.set_handle_equiv(True)
376
    br.set_handle_redirect(True)
377
    br.set_handle_referer(True)
378
    br.set_handle_robots(False)
379
    br.set_debug_http(False)
380
    br.set_debug_redirects(False)
381
    br.set_debug_responses(False)
382
 
383
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
384
 
385
    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'),
386
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
387
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
388
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
389
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 390
    return br
16371 amit.gupta 391
def todict(obj, classkey=None):
392
    if isinstance(obj, dict):
393
        data = {}
394
        for (k, v) in obj.items():
395
            data[k] = todict(v, classkey)
396
        return data
397
    elif hasattr(obj, "_ast"):
398
        return todict(obj._ast())
399
    elif hasattr(obj, "__iter__"):
400
        return [todict(v, classkey) for v in obj]
401
    elif hasattr(obj, "__dict__"):
402
        data = dict([(key, todict(value, classkey)) 
403
            for key, value in obj.__dict__.iteritems() 
404
            if not callable(value) and not key.startswith('_')])
405
        if classkey is not None and hasattr(obj, "__class__"):
406
            data[classkey] = obj.__class__.__name__
407
        return data
408
    else:
409
        return obj
13677 amit.gupta 410
 
13690 amit.gupta 411
def ungzipResponse(r):
412
    headers = r.info()
413
    if headers['Content-Encoding']=='gzip':
414
        import gzip
415
        gz = gzip.GzipFile(fileobj=r, mode='rb')
416
        html = gz.read()
417
        gz.close()
418
        return html
13724 amit.gupta 419
 
420
def tprint(*msg):
13927 amit.gupta 421
    print datetime.now(), "-", msg
13939 amit.gupta 422
 
423
if __name__ == '__main__':
14848 amit.gupta 424
    main()
425