Subversion Repositories SmartDukaan

Rev

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