Subversion Repositories SmartDukaan

Rev

Rev 14295 | Rev 14449 | 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
'''
13724 amit.gupta 6
from datetime import datetime
14413 amit.gupta 7
from dtr.config import PythonPropertyReader, PythonPropertyReader
13868 amit.gupta 8
from pprint import pprint
13569 amit.gupta 9
from pymongo.mongo_client import MongoClient
14413 amit.gupta 10
import base64
13569 amit.gupta 11
import importlib
13662 amit.gupta 12
import json
13
import math
13569 amit.gupta 14
import mechanize
14413 amit.gupta 15
import time
13631 amit.gupta 16
import traceback
13662 amit.gupta 17
import urllib
18
import urllib2
13582 amit.gupta 19
sourceMap = {1:"amazon", 2:"flipkart", 3:"snapdeal", 4:"spice", 5:"homeshop18"}
13662 amit.gupta 20
headers = { 
21
           'User-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
22
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',      
23
            'Accept-Language' : 'en-US,en;q=0.8',                     
24
            'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'
25
        }
14145 amit.gupta 26
CASHBACK_URL = PythonPropertyReader.getConfig('CASHBACK_URL')
27
USER_LOOKUP_URL = PythonPropertyReader.getConfig('USER_LOOKUP_URL')
28
WALLET_CREDIT_URL = PythonPropertyReader.getConfig('WALLET_CREDIT_URL')
13569 amit.gupta 29
 
30
def getStore(source_id):
31
    #module = sourceMap[source_id]
32
    store = Store(source_id)
13631 amit.gupta 33
    try:
34
        module = importlib.import_module("dtr.sources." + sourceMap[source_id])
35
        store = getattr(module, "Store")(source_id)
36
        return store
37
    except:
13781 amit.gupta 38
        traceback.print_exc()
13631 amit.gupta 39
        return None
13569 amit.gupta 40
 
41
class ScrapeException(Exception):
42
    """Exception raised for errors in the input.
43
 
44
    Attributes:
45
        expr -- input expression in which the error occurred
46
        msg  -- explanation of the error
47
    """
48
 
49
    def __init__(self, expr, msg):
50
        self.expr = expr
51
        self.msg = msg
52
 
53
class ParseException(Exception):
54
    """Exception raised for errors in the input.
55
 
56
    Attributes:
57
        expr -- input expression in which the error occurred
58
        msg  -- explanation of the error
59
    """
60
 
61
    def __init__(self, expr, msg):
62
        self.expr = expr
63
        self.msg = msg
64
 
13677 amit.gupta 65
client = MongoClient('mongodb://localhost:27017/') 
66
 
13569 amit.gupta 67
class Store(object):
68
 
69
    ORDER_PLACED = 'Order Placed'
70
    ORDER_DELIVERED = 'Delivered'
71
    ORDER_SHIPPED = 'Shipped' #Lets see if we can make use of it
72
    ORDER_CANCELLED = 'Cancelled'
73
 
13662 amit.gupta 74
    CB_INIT = 'Waiting Confirmation'
13610 amit.gupta 75
    CB_PENDING = 'Pending'
13955 amit.gupta 76
    CB_CREDIT_IN_PROCESS = 'Credit in process'
13610 amit.gupta 77
    CB_CREDITED = 'Credited to wallet'
78
    CB_NA = 'Not Applicable'
79
    CB_APPROVED = 'Approved'
80
    CB_CANCELLED = 'Cancelled'
13569 amit.gupta 81
 
13662 amit.gupta 82
    CONF_CB_SELLING_PRICE = 0
83
    CONF_CB_DISCOUNTED_PRICE = 1
13610 amit.gupta 84
 
13569 amit.gupta 85
    def __init__(self, store_id):
13662 amit.gupta 86
        self.db = client.Dtr
13569 amit.gupta 87
        self.store_id = store_id
13576 amit.gupta 88
        self.store_name = sourceMap[store_id]
13569 amit.gupta 89
 
13662 amit.gupta 90
    '''
91
    To Settle payback for respective stores.
92
    Also ensures that settlement happens only for approved orders
93
    '''
13569 amit.gupta 94
 
95
    def getName(self):
96
        raise NotImplementedError
97
 
98
    def scrapeAffiliate(self, startDate=None, endDate=None):
99
        raise NotImplementedError
100
 
101
    def saveToAffiliate(self, offers):
102
        raise NotImplementedError
103
 
104
    def scrapeStoreOrders(self,):
105
        raise NotImplementedError
13610 amit.gupta 106
 
13690 amit.gupta 107
    def _saveToOrder(self, order):
108
        collection = self.db.merchantOrder
109
        try:
110
            order = collection.insert(order)
14273 amit.gupta 111
            return True 
13690 amit.gupta 112
        except Exception as e:
14273 amit.gupta 113
            return False
14081 amit.gupta 114
 
115
    def _updateToOrder(self, order):
116
        collection = self.db.merchantOrder
117
        try:
118
            collection.update({"orderId":order['orderId']},{"$set":order}, upsert = True)
119
            #merchantOder 
120
        except Exception as e:
121
            traceback.print_exc()
13662 amit.gupta 122
 
123
    def getCashbackAmount(self, productCode, amount):
13995 amit.gupta 124
        alagvar = CASHBACK_URL % (productCode,self.store_id)
13662 amit.gupta 125
        filehandle = urllib2.Request(alagvar,headers=headers)
126
        x= urllib2.urlopen(filehandle)
13995 amit.gupta 127
        rmap = json.loads(str(x.read()))
128
        if len(rmap)==0:
13721 amit.gupta 129
            return (0,0)
13662 amit.gupta 130
        else:
13995 amit.gupta 131
            if rmap['cash_back_description'] == 'PERCENTAGE':
14295 amit.gupta 132
                return (math.floor((amount * rmap['cash_back'])/100), rmap['cash_back'])
13662 amit.gupta 133
            else:
14295 amit.gupta 134
                return (rmap['cash_back'], 0)
13721 amit.gupta 135
 
13662 amit.gupta 136
 
13569 amit.gupta 137
    '''
138
    Parses the order for specific store
139
 
140
    order id, total amount, created on(now() if could not parse
141
    suborder id, title, quantity, unit price, expected delivery date,
142
    status (default would be Order placed)
143
 
144
    once products are identified, each suborder can then be updated
145
    with respective cashback.
146
 
147
    Possible fields to display for Not yet delivered orders are 
148
    Product/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatus
149
    No need to show cancelled orders.
13610 amit.gupta 150
    CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWallet
13569 amit.gupta 151
    OrderStatus - Placed/Cancelled/Delivered
152
    '''
13576 amit.gupta 153
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13569 amit.gupta 154
 
155
        pass
156
 
13721 amit.gupta 157
    def _updateOrdersPayBackStatus(self, searchMap, updateMap):
13781 amit.gupta 158
        searchMap['subOrders.missingAff'] = False
159
        updateMap['subOrders.$.missingAff'] = True
13927 amit.gupta 160
        self.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)
13721 amit.gupta 161
 
13781 amit.gupta 162
    def _getActiveOrders(self, searchMap={}, collectionMap={}):
13721 amit.gupta 163
        collection = self.db.merchantOrder
13781 amit.gupta 164
        searchMap = dict(searchMap.items()+ {"closed": False, "storeId" : self.store_id}.items()) 
14274 amit.gupta 165
        collectionMap =  dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1}.items())
166
        stores = collection.find(searchMap, collectionMap)
13721 amit.gupta 167
        return [store for store in stores]
168
 
14081 amit.gupta 169
    def _getMissingOrders(self,searchMap={}):
170
        collection = self.db.merchantOrder
14292 amit.gupta 171
        searchMap = dict(searchMap.items()+ {"requireDetail":True}.items()) 
14081 amit.gupta 172
        orders = collection.find(searchMap)
173
        return list(orders)
174
 
175
 
13721 amit.gupta 176
    def _isSubOrderActive(self,order, merchantSubOrderId):
177
        subOrders = order.get("subOrders")
178
        for subOrder in subOrders:
179
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
180
                return subOrder
181
        return None
182
 
13868 amit.gupta 183
def settlePayBack():
13930 amit.gupta 184
        client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
13868 amit.gupta 185
        result = client.Dtr.merchantOrder\
186
            .aggregate([
13927 amit.gupta 187
                        {'$match':{'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}},
13868 amit.gupta 188
                        {'$unwind':"$subOrders"},
189
                        { 
190
                         '$group':{
191
                                   '_id':'$userId',
192
                                   'amount': { '$sum':'$subOrders.cashBackAmount'},
193
                                   }
194
                         }
195
                    ])['result']
196
 
197
        userAmountMap = {}
13953 amit.gupta 198
        print result
13868 amit.gupta 199
        for res in result:
13927 amit.gupta 200
            userAmountMap[res['_id']] = res['amount']
201
        datetimeNow = datetime.now() 
202
        batchId = int(time.mktime(datetimeNow.timetuple()))
203
        if refundToWallet(batchId, userAmountMap):
13930 amit.gupta 204
            client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDITED, 'subOrders.$.batchId':batchId}}, multi=True)
13951 amit.gupta 205
            for key, value in userAmountMap.iteritems():
13927 amit.gupta 206
                client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S")})
207
                client.Dtr.user.update({"userId":key}, {'$inc': { "credited": value}}, upsert=True)
13952 amit.gupta 208
            tprint("PayBack Settled")
13868 amit.gupta 209
        else:
13939 amit.gupta 210
            tprint("Error Occurred while running batch. Rolling Back")
13930 amit.gupta 211
            client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS},{'$set':{'subOrders.$.cashBackStatus':Store.CB_APPROVED}}, multi=True)    
13721 amit.gupta 212
 
13927 amit.gupta 213
def refundToWallet(batchId, userAmountMap):
14413 amit.gupta 214
 
215
    base64string = base64.encodestring('%s:%s' % ("dtr", "dtr18Feb2015")).replace('\n', '')
13927 amit.gupta 216
    batchUpdateMap = {}
217
    try :
218
        saholicUserAmountMap = {}
219
        for key, value in userAmountMap.iteritems():
220
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
14413 amit.gupta 221
            userLookupRequest.add_header("Authorization", "Basic %s" % base64string)
13927 amit.gupta 222
            try:
223
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 224
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 225
                saholicUserAmountMap[saholicUserId] = value
226
            except:
13934 amit.gupta 227
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 228
                continue
13939 amit.gupta 229
        if len(saholicUserAmountMap) > 0:
230
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
231
            batchUpdateMap['batchId'] = batchId
232
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
233
            data = urllib.urlencode(batchUpdateMap)
234
            response = urllib2.urlopen(request, data)
235
            return json.loads(response.read())['response']['credited']
236
        else:
237
            tprint("Nothing to Refund")
238
            return False
13927 amit.gupta 239
    except:
240
        traceback.print_exc()
241
        tprint("Could not batch refund")
242
        return False
13868 amit.gupta 243
 
13662 amit.gupta 244
def main():
14295 amit.gupta 245
    store = getStore(4)
246
    print store.getCashbackAmount('1010989', 500)
13868 amit.gupta 247
    #data = urllib.urlencode({'orderId':6000, 'amount':200})
248
    #request = urllib2.Request(WALLET_CREDIT_URL % (483649), headers=headers)
249
    #response = urllib2.urlopen(request, data)
250
    #print response.read()
13995 amit.gupta 251
    #settlePayBack()
13939 amit.gupta 252
 
13662 amit.gupta 253
 
13569 amit.gupta 254
 
255
 
13868 amit.gupta 256
###
257
#Settlement process is suposed to be a batch and run weekly
258
#It is should be running on first hour of mondays. As cron should
259
# Maintain a batch id.
260
 
13677 amit.gupta 261
 
13569 amit.gupta 262
def getBrowserObject():
263
    import cookielib
264
    br = mechanize.Browser(factory=mechanize.RobustFactory())
265
    cj = cookielib.LWPCookieJar()
266
    br.set_cookiejar(cj)
267
    br.set_handle_equiv(True)
268
    br.set_handle_redirect(True)
269
    br.set_handle_referer(True)
270
    br.set_handle_robots(False)
271
    br.set_debug_http(False)
272
    br.set_debug_redirects(False)
273
    br.set_debug_responses(False)
274
 
275
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
276
 
277
    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'),
278
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
279
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
280
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
281
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 282
    return br
283
 
13690 amit.gupta 284
def ungzipResponse(r):
285
    headers = r.info()
286
    if headers['Content-Encoding']=='gzip':
287
        import gzip
288
        gz = gzip.GzipFile(fileobj=r, mode='rb')
289
        html = gz.read()
290
        gz.close()
291
        return html
13724 amit.gupta 292
 
293
def tprint(*msg):
13927 amit.gupta 294
    print datetime.now(), "-", msg
13939 amit.gupta 295
 
296
if __name__ == '__main__':
297
    main()