Subversion Repositories SmartDukaan

Rev

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