Subversion Repositories SmartDukaan

Rev

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