Subversion Repositories SmartDukaan

Rev

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