Subversion Repositories SmartDukaan

Rev

Rev 13951 | Rev 13953 | 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
        }
24
CASHBACK_URL = 'http://api.profittill.com/cashbacks/index/%s/%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'
13927 amit.gupta 74
    CB_CREDIT_IN_PROCESS = 'Credited to wallet'
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()
13662 amit.gupta 112
 
113
    def getCashbackAmount(self, productCode, amount):
114
        alagvar = CASHBACK_URL % (self.store_id,productCode)
115
        filehandle = urllib2.Request(alagvar,headers=headers)
116
        x= urllib2.urlopen(filehandle)
117
        map = json.loads(x.read())
118
        if map['cashback']==0:
13721 amit.gupta 119
            return (0,0)
13662 amit.gupta 120
        else:
121
            if map['cashback_type'] == 'percentage':
13721 amit.gupta 122
                return (math.floor((amount * map['cashback'])/100), map['cashback'])
13662 amit.gupta 123
            else:
13721 amit.gupta 124
                return (map['cashback'], 0)
125
 
13662 amit.gupta 126
 
13569 amit.gupta 127
    '''
128
    Parses the order for specific store
129
 
130
    order id, total amount, created on(now() if could not parse
131
    suborder id, title, quantity, unit price, expected delivery date,
132
    status (default would be Order placed)
133
 
134
    once products are identified, each suborder can then be updated
135
    with respective cashback.
136
 
137
    Possible fields to display for Not yet delivered orders are 
138
    Product/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatus
139
    No need to show cancelled orders.
13610 amit.gupta 140
    CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWallet
13569 amit.gupta 141
    OrderStatus - Placed/Cancelled/Delivered
142
    '''
13576 amit.gupta 143
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13569 amit.gupta 144
 
145
        pass
146
 
13721 amit.gupta 147
    def _updateOrdersPayBackStatus(self, searchMap, updateMap):
13781 amit.gupta 148
        searchMap['subOrders.missingAff'] = False
149
        updateMap['subOrders.$.missingAff'] = True
13927 amit.gupta 150
        self.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)
13721 amit.gupta 151
 
13781 amit.gupta 152
    def _getActiveOrders(self, searchMap={}, collectionMap={}):
13721 amit.gupta 153
        collection = self.db.merchantOrder
13781 amit.gupta 154
        searchMap = dict(searchMap.items()+ {"closed": False, "storeId" : self.store_id}.items()) 
155
        collectionMap =  dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1}.items())
156
        stores = collection.find(searchMap, collectionMap)
13721 amit.gupta 157
        return [store for store in stores]
158
 
159
    def _isSubOrderActive(self,order, merchantSubOrderId):
160
        subOrders = order.get("subOrders")
161
        for subOrder in subOrders:
162
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
163
                return subOrder
164
        return None
165
 
13868 amit.gupta 166
def settlePayBack():
13930 amit.gupta 167
        client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
13868 amit.gupta 168
        result = client.Dtr.merchantOrder\
169
            .aggregate([
13927 amit.gupta 170
                        {'$match':{'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}},
13868 amit.gupta 171
                        {'$unwind':"$subOrders"},
172
                        { 
173
                         '$group':{
174
                                   '_id':'$userId',
175
                                   'amount': { '$sum':'$subOrders.cashBackAmount'},
176
                                   }
177
                         }
178
                    ])['result']
179
 
180
        userAmountMap = {}
181
        for res in result:
13927 amit.gupta 182
            userAmountMap[res['_id']] = res['amount']
183
        datetimeNow = datetime.now() 
184
        batchId = int(time.mktime(datetimeNow.timetuple()))
185
        if refundToWallet(batchId, userAmountMap):
13930 amit.gupta 186
            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 187
            for key, value in userAmountMap.iteritems():
13927 amit.gupta 188
                client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S")})
189
                client.Dtr.user.update({"userId":key}, {'$inc': { "credited": value}}, upsert=True)
13952 amit.gupta 190
            tprint("PayBack Settled")
13868 amit.gupta 191
        else:
13939 amit.gupta 192
            tprint("Error Occurred while running batch. Rolling Back")
13930 amit.gupta 193
            client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_CREDIT_IN_PROCESS},{'$set':{'subOrders.$.cashBackStatus':Store.CB_APPROVED}}, multi=True)    
13721 amit.gupta 194
 
13927 amit.gupta 195
def refundToWallet(batchId, userAmountMap):
196
    batchUpdateMap = {}
197
    try :
198
        saholicUserAmountMap = {}
199
        for key, value in userAmountMap.iteritems():
200
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
201
            try:
202
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 203
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 204
                saholicUserAmountMap[saholicUserId] = value
205
            except:
13934 amit.gupta 206
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 207
                continue
13939 amit.gupta 208
        if len(saholicUserAmountMap) > 0:
209
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
210
            batchUpdateMap['batchId'] = batchId
211
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
212
            data = urllib.urlencode(batchUpdateMap)
213
            response = urllib2.urlopen(request, data)
214
            return json.loads(response.read())['response']['credited']
215
        else:
216
            tprint("Nothing to Refund")
217
            return False
13927 amit.gupta 218
    except:
219
        traceback.print_exc()
220
        tprint("Could not batch refund")
221
        return False
13868 amit.gupta 222
 
13927 amit.gupta 223
 
224
    #return json.loads(response.read())['credited']
225
 
13662 amit.gupta 226
def main():
13677 amit.gupta 227
    #store = getStore(3)
228
    #print store.getCashbackAmount('864683341', 100)
13868 amit.gupta 229
    #data = urllib.urlencode({'orderId':6000, 'amount':200})
230
    #request = urllib2.Request(WALLET_CREDIT_URL % (483649), headers=headers)
231
    #response = urllib2.urlopen(request, data)
232
    #print response.read()
13939 amit.gupta 233
    settlePayBack()
234
 
13662 amit.gupta 235
 
13569 amit.gupta 236
 
237
 
13868 amit.gupta 238
###
239
#Settlement process is suposed to be a batch and run weekly
240
#It is should be running on first hour of mondays. As cron should
241
# Maintain a batch id.
242
 
13677 amit.gupta 243
 
13569 amit.gupta 244
def getBrowserObject():
245
    import cookielib
246
    br = mechanize.Browser(factory=mechanize.RobustFactory())
247
    cj = cookielib.LWPCookieJar()
248
    br.set_cookiejar(cj)
249
    br.set_handle_equiv(True)
250
    br.set_handle_redirect(True)
251
    br.set_handle_referer(True)
252
    br.set_handle_robots(False)
253
    br.set_debug_http(False)
254
    br.set_debug_redirects(False)
255
    br.set_debug_responses(False)
256
 
257
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
258
 
259
    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'),
260
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
261
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
262
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
263
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 264
    return br
265
 
13690 amit.gupta 266
def ungzipResponse(r):
267
    headers = r.info()
268
    if headers['Content-Encoding']=='gzip':
269
        import gzip
270
        gz = gzip.GzipFile(fileobj=r, mode='rb')
271
        html = gz.read()
272
        gz.close()
273
        return html
13724 amit.gupta 274
 
275
def tprint(*msg):
13927 amit.gupta 276
    print datetime.now(), "-", msg
13939 amit.gupta 277
 
278
if __name__ == '__main__':
279
    main()