Subversion Repositories SmartDukaan

Rev

Rev 14959 | Rev 15791 | 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
14848 amit.gupta 20
sourceMap = {1:"amazon", 2:"flipkart", 3:"snapdeal", 4:"spice", 5:"homeshop18", 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
 
14624 amit.gupta 107
    def getUpdateMap(self, subOrder):
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
115
                    if subOrder['cashBackStatus'] == Store.CB_PENDING:
116
                        updateMap['subOrders.$.cashBackStatus'] = Store.CB_CANCELLED
117
                if value==Store.ORDER_DELIVERED:
118
                    closed = True
119
                    updateMap['subOrders.$.closed'] = True
120
                    if subOrder['cashBackStatus'] == Store.CB_PENDING:
121
                        updateMap['subOrders.$.cashBackStatus'] = Store.CB_APPROVED
122
            if key=='cashBackStatus':
123
                continue
124
            updateMap['subOrders.$.' + key] = value
125
        subOrder['closed'] = closed
126
        return updateMap
127
 
14618 amit.gupta 128
    def saveOrder(self, merchantOrder):
129
        #merchantOrder = Order()
130
        #merchantOrder.status
131
        #mercha
132
        pass
133
 
134
 
13569 amit.gupta 135
    def scrapeStoreOrders(self,):
136
        raise NotImplementedError
13610 amit.gupta 137
 
14618 amit.gupta 138
    def updateSubOrder(self, subOrder):
139
        pass
140
        #if subOrder.get
141
 
13690 amit.gupta 142
    def _saveToOrder(self, order):
143
        collection = self.db.merchantOrder
144
        try:
145
            order = collection.insert(order)
14273 amit.gupta 146
            return True 
13690 amit.gupta 147
        except Exception as e:
14273 amit.gupta 148
            return False
14081 amit.gupta 149
 
150
    def _updateToOrder(self, order):
151
        collection = self.db.merchantOrder
152
        try:
14959 amit.gupta 153
            print "************************************************************************"
154
            print "order"
155
            print order
156
            print "************************************************************************"
14081 amit.gupta 157
            collection.update({"orderId":order['orderId']},{"$set":order}, upsert = True)
158
            #merchantOder 
159
        except Exception as e:
160
            traceback.print_exc()
13662 amit.gupta 161
 
162
    def getCashbackAmount(self, productCode, amount):
13995 amit.gupta 163
        alagvar = CASHBACK_URL % (productCode,self.store_id)
13662 amit.gupta 164
        filehandle = urllib2.Request(alagvar,headers=headers)
165
        x= urllib2.urlopen(filehandle)
13995 amit.gupta 166
        rmap = json.loads(str(x.read()))
167
        if len(rmap)==0:
13721 amit.gupta 168
            return (0,0)
13662 amit.gupta 169
        else:
13995 amit.gupta 170
            if rmap['cash_back_description'] == 'PERCENTAGE':
14295 amit.gupta 171
                return (math.floor((amount * rmap['cash_back'])/100), rmap['cash_back'])
13662 amit.gupta 172
            else:
14295 amit.gupta 173
                return (rmap['cash_back'], 0)
13721 amit.gupta 174
 
13662 amit.gupta 175
 
13569 amit.gupta 176
    '''
177
    Parses the order for specific store
178
 
179
    order id, total amount, created on(now() if could not parse
180
    suborder id, title, quantity, unit price, expected delivery date,
181
    status (default would be Order placed)
182
 
183
    once products are identified, each suborder can then be updated
184
    with respective cashback.
185
 
186
    Possible fields to display for Not yet delivered orders are 
187
    Product/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatus
188
    No need to show cancelled orders.
13610 amit.gupta 189
    CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWallet
13569 amit.gupta 190
    OrderStatus - Placed/Cancelled/Delivered
191
    '''
13576 amit.gupta 192
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13569 amit.gupta 193
 
194
        pass
195
 
13721 amit.gupta 196
    def _updateOrdersPayBackStatus(self, searchMap, updateMap):
13781 amit.gupta 197
        searchMap['subOrders.missingAff'] = False
198
        updateMap['subOrders.$.missingAff'] = True
13927 amit.gupta 199
        self.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)
13721 amit.gupta 200
 
13781 amit.gupta 201
    def _getActiveOrders(self, searchMap={}, collectionMap={}):
13721 amit.gupta 202
        collection = self.db.merchantOrder
13781 amit.gupta 203
        searchMap = dict(searchMap.items()+ {"closed": False, "storeId" : self.store_id}.items()) 
14274 amit.gupta 204
        collectionMap =  dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1}.items())
205
        stores = collection.find(searchMap, collectionMap)
13721 amit.gupta 206
        return [store for store in stores]
207
 
14081 amit.gupta 208
    def _getMissingOrders(self,searchMap={}):
209
        collection = self.db.merchantOrder
14948 amit.gupta 210
        searchMap = dict(searchMap.items()+ {"requireDetail":True, "storeId":self.store_id}.items()) 
14081 amit.gupta 211
        orders = collection.find(searchMap)
212
        return list(orders)
213
 
214
 
13721 amit.gupta 215
    def _isSubOrderActive(self,order, merchantSubOrderId):
216
        subOrders = order.get("subOrders")
14867 amit.gupta 217
        if subOrders is None:
218
            return None
13721 amit.gupta 219
        for subOrder in subOrders:
220
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
221
                return subOrder
222
        return None
14663 amit.gupta 223
 
14618 amit.gupta 224
def settlePayBack(runtype='dry'):
14663 amit.gupta 225
    userAmountMap = {}
226
    searchMapList = []
14666 amit.gupta 227
    for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_APPROVED}):
14663 amit.gupta 228
        userId = mo.get("userId")
14664 amit.gupta 229
        if   mo.get('subOrders') is not None:
14663 amit.gupta 230
            for so in mo['subOrders']:
14666 amit.gupta 231
                if so.get('cashBackStatus') == Store.CB_APPROVED:
14663 amit.gupta 232
                    searchMapList.append({"orderId":mo.get("orderId"), "subOrders.merchantSubOrderId":so.get("merchantSubOrderId")})
233
                    if not userAmountMap.has_key(userId):
234
                        userAmountMap[userId] = so.get('cashBackAmount')
235
                    else:
236
                        userAmountMap[userId] += so.get('cashBackAmount')
237
                    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"))
238
                else:  
239
                    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"))
240
    for searchMap in searchMapList:
241
        print "%s\t%s"%(searchMap.get('orderId'),searchMap.get('subOrders.merchantSubOrderId'))
14664 amit.gupta 242
    for key,val in userAmountMap.iteritems():
243
        print "%s\t%s"%(key,val)
14663 amit.gupta 244
    if (runtype=='live'):
14666 amit.gupta 245
        bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
14663 amit.gupta 246
        if len(searchMapList) == 0:
14618 amit.gupta 247
            return
14666 amit.gupta 248
        for searchMap in searchMapList:
249
            bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}})
250
        bulk.execute()
14663 amit.gupta 251
 
13927 amit.gupta 252
        datetimeNow = datetime.now() 
253
        batchId = int(time.mktime(datetimeNow.timetuple()))
254
        if refundToWallet(batchId, userAmountMap):
14666 amit.gupta 255
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
256
            for searchMap in searchMapList:
257
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDITED, "subOrders.$.batchId":batchId}})
258
            print bulk.execute()
259
            for key, value in userAmountMap.iteritems():
260
                client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S")})
261
                client.Dtr.user.update({"userId":key}, {'$inc': { "credited": value}}, upsert=True)
13952 amit.gupta 262
            tprint("PayBack Settled")
14666 amit.gupta 263
        else:
264
            tprint("Error Occurred while running batch. Rolling Back")
265
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
266
            for searchMap in searchMapList:
267
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_APPROVED}})
268
            bulk.execute()
14663 amit.gupta 269
 
270
 
271
def settlePayBack1(runtype='dry'):
272
        approvedUserMap = {}
273
        pendingUserMap = {}
274
        creditedToWalletUserMap = {}
275
        #client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
276
        for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_CREDITED}):
277
            userId = mo.get("userId")
278
            for so in mo['subOrders']:
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
 
281
        for refund in client.Dtr.refund.find():
282
            print "%s\t%s\t%s\t%s"%(refund.get("userId") ,refund.get("timestamp") ,refund.get("userAmount"), refund.get("batch"))
283
 
284
 
13927 amit.gupta 285
def refundToWallet(batchId, userAmountMap):
14413 amit.gupta 286
    base64string = base64.encodestring('%s:%s' % ("dtr", "dtr18Feb2015")).replace('\n', '')
13927 amit.gupta 287
    batchUpdateMap = {}
288
    try :
289
        saholicUserAmountMap = {}
290
        for key, value in userAmountMap.iteritems():
291
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
14413 amit.gupta 292
            userLookupRequest.add_header("Authorization", "Basic %s" % base64string)
13927 amit.gupta 293
            try:
294
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 295
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 296
                saholicUserAmountMap[saholicUserId] = value
297
            except:
13934 amit.gupta 298
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 299
                continue
13939 amit.gupta 300
        if len(saholicUserAmountMap) > 0:
301
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
302
            batchUpdateMap['batchId'] = batchId
303
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
304
            data = urllib.urlencode(batchUpdateMap)
305
            response = urllib2.urlopen(request, data)
306
            return json.loads(response.read())['response']['credited']
307
        else:
308
            tprint("Nothing to Refund")
309
            return False
13927 amit.gupta 310
    except:
311
        traceback.print_exc()
312
        tprint("Could not batch refund")
313
        return False
13868 amit.gupta 314
 
13662 amit.gupta 315
def main():
14618 amit.gupta 316
#    client = MongoClient('mongodb://root:ecip$dtrMay2014@dtr:27017/')
317
#    s = Store(1)
318
#    orders = s.db.Dtr.find({"subOrders.imgUrl":{"$exists":1}}, {"merchantOrderId":1, "subOrders.productCode":1,"subOrders.imgUrl":1,"_id": 0})
319
#    db = client.Dtr
320
#    for order in orders:
321
#        for subOrder in order.get("subOrders"):
322
#            #db.merchantOrder.update({"merchantOrderId":order.getMerchantOrderId,"subOrders.imgUrl":{"$exists":0}, "subOrders.productCode":subOrder.get("productCode")}, {"$set":{"subOrders.$.imgUrl":subOrder.get("imgUrl")}})
323
#            db.merchantOrder.findOne()
14663 amit.gupta 324
    settlePayBack('dry')        
13569 amit.gupta 325
 
326
 
13868 amit.gupta 327
###
328
#Settlement process is suposed to be a batch and run weekly
329
#It is should be running on first hour of mondays. As cron should
330
# Maintain a batch id.
331
 
13677 amit.gupta 332
 
13569 amit.gupta 333
def getBrowserObject():
334
    import cookielib
335
    br = mechanize.Browser(factory=mechanize.RobustFactory())
336
    cj = cookielib.LWPCookieJar()
337
    br.set_cookiejar(cj)
338
    br.set_handle_equiv(True)
339
    br.set_handle_redirect(True)
340
    br.set_handle_referer(True)
341
    br.set_handle_robots(False)
342
    br.set_debug_http(False)
343
    br.set_debug_redirects(False)
344
    br.set_debug_responses(False)
345
 
346
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
347
 
348
    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'),
349
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
350
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
351
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
352
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 353
    return br
354
 
13690 amit.gupta 355
def ungzipResponse(r):
356
    headers = r.info()
357
    if headers['Content-Encoding']=='gzip':
358
        import gzip
359
        gz = gzip.GzipFile(fileobj=r, mode='rb')
360
        html = gz.read()
361
        gz.close()
362
        return html
13724 amit.gupta 363
 
364
def tprint(*msg):
13927 amit.gupta 365
    print datetime.now(), "-", msg
13939 amit.gupta 366
 
367
if __name__ == '__main__':
14848 amit.gupta 368
    main()
369