Subversion Repositories SmartDukaan

Rev

Rev 19273 | Rev 20172 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
14220 amit.gupta 1
'''
2
Created on Jan 15, 2015
3
 
4
@author: amit
5
'''
6
from BeautifulSoup import BeautifulSoup
7
from bson.binary import Binary
8
from datetime import datetime, date, timedelta
9
from dtr import main
10
from dtr.config import PythonPropertyReader
11
from dtr.dao import AffiliateInfo, Order, SubOrder
12
from dtr.main import getBrowserObject, ScrapeException, getStore, ParseException, \
13
    Store as MStore, ungzipResponse, tprint
14
from pprint import pprint
15
from pymongo import MongoClient
16
import json
17
import pymongo
18
import re
19
import traceback
20
import urllib
21
import urllib2
16189 amit.gupta 22
from dtr.storage.Mongo import getDealRank
14220 amit.gupta 23
 
14222 amit.gupta 24
ORDERSTATUS  = {"PAYMENT_PENDING" : 0,
25
"PAYMENT_FAILED" : 1,
26
"COD_VERIFICATION_PENDING" : 2,
27
"SUBMITTED_FOR_PROCESSING" : 3,
28
"ACCEPTED" : 4,
29
"INVENTORY_LOW" : 5,
30
"REJECTED" : 6,
31
"BILLED" : 7,
32
"PAYMENT_FLAGGED" : 8,
33
"SHIPPED_FROM_WH" : 9,
34
"SHIPPED_TO_LOGST" : 10,
35
"PAYMENT_FLAGGED_DENIED" : 11,
36
"DELIVERY_SUCCESS" : 12,
37
"CANCEL_REQUEST_RECEIVED" : 13,
38
"CANCEL_REQUEST_CONFIRMED" : 14,
39
"CANCELLED_ON_CUSTOMER_REQUEST" : 15,
40
"SHIPPED_TO_DESTINATION_CITY" : 16,
41
"REACHED_DESTINATION_CITY" : 17,
42
"COD_VERIFICATION_FAILED" : 18,
43
"FAILED" : 19,
44
"RTO_IN_TRANSIT" : 20,
45
"RTO_RECEIVED_PRESTINE" : 21,
46
"DOA_PICKUP_REQUEST_RAISED" : 22,
47
"DOA_PICKUP_CONFIRMED" : 23,
48
"DOA_RETURN_IN_TRANSIT" : 24,
49
"DOA_RECEIVED_PRESTINE" : 25,
50
"DOA_CERT_INVALID" : 26,
51
"DOA_CERT_VALID" : 27,
52
"RTO_RESHIPPED" : 28,
53
"DOA_INVALID_RESHIPPED" : 29,
54
"DOA_VALID_RESHIPPED" : 30,
55
"RTO_REFUNDED" : 31,
56
"DOA_VALID_REFUNDED" : 32,
57
"DOA_INVALID_REFUNDED" : 33,
58
"CANCELLED_DUE_TO_LOW_INVENTORY" : 34,
59
"LOW_INV_PO_RAISED" : 35,
60
"LOW_INV_REVERSAL_IN_PROCESS" : 36,
61
"LOW_INV_NOT_AVAILABLE_AT_HOTSPOT" : 37,
62
"LOW_INV_PO_RAISED_TIMEOUT" : 38,
63
"LOW_INV_REVERSAL_TIMEOUT" : 39,
64
"FIRST_DELIVERY_ATTEMPT_MADE" : 40,
65
"CAPTURE_IN_PROCESS" : 41,
66
"DOA_REQUEST_RECEIVED" : 42,
67
"DOA_REQUEST_AUTHORIZED" : 43,
68
"DOA_PICKUP_DENIED" : 44,
69
"DOA_RECEIVED_DAMAGED" : 45,
70
"DOA_LOST_IN_TRANSIT" : 46,
71
"DOA_RESHIPPED_RCVD_DAMAGED" : 47,
72
"DOA_REFUNDED_RCVD_DAMAGED" : 48,
73
"DOA_RESHIPPED_LOST_IN_TRANSIT" : 49,
74
"DOA_REFUNDED_LOST_IN_TRANSIT" : 50,
75
"RTO_RECEIVED_DAMAGED" : 51,
76
"RTO_LOST_IN_TRANSIT" : 52,
77
"RTO_DAMAGED_RESHIPPED" : 53,
78
"RTO_DAMAGED_REFUNDED" : 54,
79
"RTO_LOST_IN_TRANSIT_RESHIPPED" : 55,
80
"RTO_LOST_IN_TRANSIT_REFUNDED" : 56,
81
"RTO_INVENTORY_REVERSED" : 57,
82
"RET_REQUEST_RECEIVED" : 58,
83
"RET_REQUEST_AUTHORIZED" : 59,
84
"RET_PICKUP_REQUEST_RAISED" : 60,
85
"RET_PICKUP_DENIED" : 61,
86
"RET_PICKUP_CONFIRMED" : 62,
87
"RET_RETURN_IN_TRANSIT" : 63,
88
"RET_RECEIVED_PRESTINE" : 64,
89
"RET_RECEIVED_DAMAGED" : 65,
90
"RET_LOST_IN_TRANSIT" : 66,
91
"RET_PRODUCT_USABLE" : 67,
92
"RET_PRODUCT_UNUSABLE" : 68,
93
"RET_PRODUCT_USABLE_RESHIPPED" : 69,
94
"RET_PRODUCT_USABLE_REFUNDED" : 70,
95
"RET_PRODUCT_UNUSABLE_RESHIPPED" : 71,
96
"RET_PRODUCT_UNUSABLE_REFUNDED" : 72,
97
"RET_RESHIPPED_RCVD_DAMAGED" : 73,
98
"RET_REFUNDED_RCVD_DAMAGED" : 74,
99
"RET_RESHIPPED_LOST_IN_TRANSIT" : 75,
100
"RET_REFUNDED_LOST_IN_TRANSIT" : 76,
101
"LOST_IN_TRANSIT" : 77,
102
"LOST_IN_TRANSIT_RESHIPPED" : 78,
103
"LOST_IN_TRANSIT_REFUNDED" : 79,
104
"DELIVERED_AT_STORE" : 80,
105
"RECEIVED_AT_STORE" : 81}
14220 amit.gupta 106
headers = { 
107
           'User-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
108
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',      
109
            'Accept-Language' : 'en-US,en;q=0.8',                     
110
            'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'
111
        }
112
SAHOLIC_ORDER_URL=PythonPropertyReader.getConfig('SAHOLIC_ORDER_URL')
14246 amit.gupta 113
SAHOLIC_ORDER_URL_TR=PythonPropertyReader.getConfig('SAHOLIC_ORDER_URL_TR')
14220 amit.gupta 114
class Store(MStore):
115
    '''
116
    This is to map order statuses of our system to order statuses of snapdeal.
117
    And our statuses will change accordingly.
118
 
119
    '''
120
    OrderStatusMap = {
121
                      MStore.ORDER_PLACED : [2,3,4,5,6,7,8,13],
122
                      MStore.ORDER_DELIVERED : [12],
19932 amit.gupta 123
                      MStore.ORDER_SHIPPED : [9,10,16,17,81,80, 40,20],
18998 amit.gupta 124
                      MStore.ORDER_CANCELLED : [1,6,11,14,15,18,19,31,34,28,53,54,55,56,20,21,77,79, 78]
14220 amit.gupta 125
                      }
126
 
127
 
128
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
14227 amit.gupta 129
        resp = {}
14237 amit.gupta 130
        paymentId = int(re.findall(r'=(.*)?',orderSuccessUrl)[0])
14220 amit.gupta 131
        orderRequest = urllib2.Request(SAHOLIC_ORDER_URL %(paymentId), headers=headers)
132
        try:
16189 amit.gupta 133
            self.userId = userId
14832 amit.gupta 134
            connection = urllib2.urlopen(orderRequest)
135
            response = connection.read()
136
            connection.close()
14220 amit.gupta 137
            response = json.loads(response)['response']
138
            payment = response['payment']
14246 amit.gupta 139
            subOrders = []
14220 amit.gupta 140
            orders = response['orders']
14228 amit.gupta 141
            items = response['itemsMap']
14220 amit.gupta 142
            merchantOrder = Order(orderId, userId, subTagId, self.store_id, orderSuccessUrl)
143
            merchantOrder.merchantOrderId = payment['merchantTxnId']
144
            merchantOrder.paidAmount = payment['amount']
14246 amit.gupta 145
 
14220 amit.gupta 146
            for o in orders:
14246 amit.gupta 147
                subOrders.append(self.createSubOrder(o, items))
14247 amit.gupta 148
            merchantOrder.subOrders = subOrders
14266 amit.gupta 149
            merchantOrder.placedOn = merchantOrder.subOrders[0].placedOn
14246 amit.gupta 150
            s = todict(merchantOrder)
151
            s['sUserId'] = orders[0]['customer_id']
18687 manish.sha 152
            if response.get('payment_option') is not None:
153
                s['payment_option'] = response ['payment_option']
14267 amit.gupta 154
            if self._saveToOrder(s):
155
                resp['result'] = 'ORDER_CREATED'
156
            else:
157
                #Order already created
14312 amit.gupta 158
                resp['result'] = 'ORDER_ALREADY_CREATED_IGNORED'
14220 amit.gupta 159
        except:
14227 amit.gupta 160
            traceback.print_exc()
14312 amit.gupta 161
            resp['result'] = 'ORDER_NOT_CREATED'
14227 amit.gupta 162
        return resp
14246 amit.gupta 163
 
164
    def createSubOrder(self, order, items):
165
            lineitem = order['lineitems'][0]
166
 
167
            item = items[str(lineitem['item_id'])]
168
            brand = lineitem.get('brand')
169
            modelNumber = lineitem.get('model_number')
170
            modelName = lineitem.get('model_name')
171
            color = lineitem.get('color')
172
            productTitle = brand + (" " + modelName if modelName else "") + (" "  + modelNumber if modelNumber is not None else "") + ("(" + color +")" if color else "")
18055 amit.gupta 173
            amountPaid = order['total_amount'] 
14246 amit.gupta 174
            subOrder = SubOrder(productTitle, None, datetime.strftime(datetime.fromtimestamp(order['created_timestamp']/1000),"%d %B %Y"), amountPaid)
18055 amit.gupta 175
            subOrder.amount = amountPaid - order['gvAmount']
14246 amit.gupta 176
            subOrder.merchantSubOrderId = str(order['id'])
177
            subOrder.orderDetailUrl = "http://m.saholic.com/order/" + subOrder.merchantSubOrderId 
17677 amit.gupta 178
            subOrder.quantity = int(lineitem['quantity'])
14246 amit.gupta 179
            subOrder.estimatedDeliveryDate = datetime.strftime(datetime.fromtimestamp(order['promised_delivery_time']/1000),"%d %B %Y")
18055 amit.gupta 180
            subOrder.imgUrl = item.get("imgUrl") 
14246 amit.gupta 181
            subOrder.productUrl = "http://m.saholic.com/" + item['url']
14301 amit.gupta 182
            subOrder.productCode = item['url'].split('-')[-1]
14246 amit.gupta 183
            subOrder.detailedStatus = order['statusDescription']
17677 amit.gupta 184
            (cashbackAmount, percentage) = self.getCashbackAmount(subOrder.productCode, amountPaid/subOrder.quantity)
16189 amit.gupta 185
            dealRank = getDealRank(subOrder.productCode, self.store_id, self.userId)
186
            subOrder.dealRank = dealRank.get('rank')
187
            subOrder.rankDesc = dealRank.get('description')
16283 amit.gupta 188
            subOrder.maxNlc = dealRank.get('maxNlc')
189
            subOrder.minNlc = dealRank.get('minNlc')
190
            subOrder.db = dealRank.get('dp')
191
            subOrder.itemStatus = dealRank.get('status')
14246 amit.gupta 192
            cashbackStatus = Store.CB_PENDING
193
            if cashbackAmount <= 0:
194
                cashbackStatus = Store.CB_NA
195
            subOrder.cashBackStatus = cashbackStatus
17677 amit.gupta 196
            subOrder.cashBackAmount = cashbackAmount*subOrder.quantity
14246 amit.gupta 197
            if percentage > 0:
198
                subOrder.cashBackPercentage = percentage
199
            return subOrder
14220 amit.gupta 200
    def _getStatusFromDetailedStatus(self, detailedStatus):
201
        for key, value in Store.OrderStatusMap.iteritems():
202
            if detailedStatus in value:
203
                return key
14349 amit.gupta 204
        print "Detailed Status need to be mapped", detailedStatus
16189 amit.gupta 205
        raise ParseException("_getStatusFromDetailedStatus", "Found new order status" + str(detailedStatus))
14220 amit.gupta 206
 
207
 
208
    def scrapeStoreOrders(self,):
18085 amit.gupta 209
        trs = self._getActiveOrders(collectionMap={'merchantOrderId':1, 'sUserId':1, 'userId':1})
14267 amit.gupta 210
        bulk = self.db.merchantOrder.initialize_ordered_bulk_op()
14246 amit.gupta 211
        for tr in trs:
18085 amit.gupta 212
            self.userId = tr['userId']
14246 amit.gupta 213
            try:
214
                orderRequest = urllib2.Request(SAHOLIC_ORDER_URL_TR %(tr['merchantOrderId'], tr['sUserId']), headers=headers)
215
                response = urllib2.urlopen(orderRequest).read()
19272 amit.gupta 216
                print "transaction_id----------", tr['merchantOrderId']
14246 amit.gupta 217
                response = json.loads(response)['response']
218
                items = response['itemsMap']
219
                orders = response['orders']
220
                closed = True
18254 amit.gupta 221
                splitSubOrdersMap={}
222
                ordersToClone = {}
14246 amit.gupta 223
                for order in orders:
19272 amit.gupta 224
                    #print "orderid---", order['id']
14269 amit.gupta 225
                    orderId = str(order['id'])
226
                    subOrder = self._isSubOrderActive(tr, orderId)
18254 amit.gupta 227
                    currentQty = int(order['lineitems'][0]['quantity'])
14246 amit.gupta 228
                    if subOrder:
229
                        if subOrder['closed']:
230
                            continue
231
                        else:
18254 amit.gupta 232
                            quantity = int(subOrder['quantity'])
14270 amit.gupta 233
                            findMap = {"orderId": tr['orderId'], "subOrders.merchantSubOrderId": orderId}
14246 amit.gupta 234
                            updateMap = {}
235
                            updateMap["subOrders.$.detailedStatus"] = order['statusDescription']
236
                            status = self._getStatusFromDetailedStatus(ORDERSTATUS[order['status']]) 
237
                            closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
238
                            updateMap["subOrders.$.status"] = status
18254 amit.gupta 239
                            #Check if split
240
                            if quantity != currentQty:
241
                                updateMap["subOrders.$.quantity"] = currentQty
242
                                updateMap["subOrders.$.amount"] = int((subOrder['amount']*currentQty)/quantity)
243
                                updateMap["subOrders.$.cashBackAmount"] = int((subOrder['cashBackAmount']*currentQty)/quantity)
244
                                updateMap["subOrders.$.amountPaid"] = int((subOrder['amountPaid']*currentQty)/quantity)
245
                                splitSubOrdersMap[order['id']]=subOrder
14246 amit.gupta 246
                            if closedStatus:
247
                                #if status is closed then change the paybackStatus accordingly
248
                                updateMap["subOrders.$.closed"] = True
249
                                if status == Store.ORDER_DELIVERED:
250
                                    deliveredOn = datetime.strftime(datetime.fromtimestamp(order['delivery_timestamp']/1000),"%A %d %B %Y")
251
                                    updateMap['subOrders.$.deliveredOn'] = deliveredOn
252
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
253
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
254
                                elif status == Store.ORDER_CANCELLED:
255
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
256
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
257
                            else:
258
                                expectedDelivery = datetime.strftime(datetime.fromtimestamp(order['promised_delivery_time']/1000),"%A %d %B %Y")
259
                                updateMap["subOrders.$.estimatedDeliveryDate"] = expectedDelivery
260
                                closed = False
261
                            bulk.find(findMap).update({'$set' : updateMap})
262
                    else:
18254 amit.gupta 263
                        if order['originalOrderId']:
264
                            ordersToClone[order['id']] = order
265
                        else:
266
                            subOrder = self.createSubOrder(order, items)
267
                            self.db.merchantOrder.update({"orderId":tr['orderId']},{'$push':{"subOrders":todict(subOrder)}})
268
                            print "Added new suborder with subOrder Id:", subOrder.merchantSubOrderId
269
                            closed = False
270
 
271
                for order in ordersToClone.values():
272
                    originalOrderId = self.getOriginalOrderId(order['id'], ordersToClone, splitSubOrdersMap)
273
                    subOrderToClone = splitSubOrdersMap[originalOrderId].copy()
19270 amit.gupta 274
                    subOrderToClone["merchantSubOrderId"] = str(order['id'])
18254 amit.gupta 275
                    currentQty = int(order['lineitems'][0]['quantity'])
276
                    quantity = int(subOrderToClone['quantity'])
277
                    subOrderToClone["detailedStatus"] = order['statusDescription']
278
                    status = self._getStatusFromDetailedStatus(ORDERSTATUS[order['status']]) 
279
                    closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
280
                    print "status---", status, order['status']
281
                    subOrderToClone["status"] = status
282
                    subOrderToClone["quantity"] = currentQty
283
                    subOrderToClone["amount"] = int((subOrderToClone['amount']*currentQty)/quantity)
284
                    subOrderToClone["cashBackAmount"] = int((subOrderToClone['cashBackAmount']*currentQty)/quantity)
285
                    subOrderToClone["amountPaid"] = int((subOrderToClone['amountPaid']*currentQty)/quantity)
286
                    if closedStatus:
287
                        #if status is closed then change the paybackStatus accordingly
288
                        subOrderToClone["closed"] = True
289
                        if status == Store.ORDER_DELIVERED:
290
                            deliveredOn = datetime.strftime(datetime.fromtimestamp(order['delivery_timestamp']/1000),"%A %d %B %Y")
291
                            subOrderToClone['deliveredOn'] = deliveredOn
292
                            if subOrderToClone.get("cashBackStatus") == Store.CB_PENDING:
293
                                subOrderToClone["cashBackStatus"] = Store.CB_APPROVED
294
                        elif status == Store.ORDER_CANCELLED:
295
                            if subOrderToClone.get("cashBackStatus") == Store.CB_PENDING:
296
                                subOrderToClone["cashBackStatus"] = Store.CB_CANCELLED
297
                    else:
298
                        expectedDelivery = datetime.strftime(datetime.fromtimestamp(order['promised_delivery_time']/1000),"%A %d %B %Y")
299
                        subOrderToClone["estimatedDeliveryDate"] = expectedDelivery
14246 amit.gupta 300
                        closed = False
18254 amit.gupta 301
 
302
                    self.db.merchantOrder.update({"orderId":tr['orderId']},{'$push':{"subOrders":subOrderToClone}})
303
 
304
 
14283 amit.gupta 305
                bulk.find({"orderId":tr['orderId']}).update({'$set' : {'closed':closed}})
14246 amit.gupta 306
            except:
14277 amit.gupta 307
                traceback.print_exc()
14870 amit.gupta 308
        try:
309
            bulk.execute()
310
        except:
311
            traceback.print_exc()
14246 amit.gupta 312
 
313
 
314
 
14220 amit.gupta 315
 
18254 amit.gupta 316
    def getOriginalOrderId(self, orderId, ordersToClone, splitSubOrdersMap): 
317
        if splitSubOrdersMap.has_key(orderId):
318
            return orderId
319
        else:
320
            originalOrderId = ordersToClone[orderId]['originalOrderId']
321
            return self.getOriginalOrderId(originalOrderId, ordersToClone, splitSubOrdersMap) 
322
 
14220 amit.gupta 323
    def _saveToAffiliate(self, offers):
324
        if offers is None or len(offers)==0:
325
            print "no affiliate have been pushed"
326
            return
327
        collection = self.db.snapdealOrderAffiliateInfo
328
        try:
329
            collection.insert(offers,continue_on_error=True)
330
        except pymongo.errors.DuplicateKeyError as e:
331
            print e.details
332
 
333
 
334
    def covertToObj(self,offer):
335
        offerData = offer['Stat']
336
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
337
                              offerData['datetime'], offerData['payout'], offer['Offer']['name'], offerData['ip'], offerData['conversion_sale_amount'])
338
 
339
        return offer1
340
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
341
    endDate=date.today() + timedelta(days=1)
342
    startDate=endDate - timedelta(days=31)
343
 
344
    parameters = (
345
        ("page",str(page)),
346
        ("limit",str(limit)),
347
        ("fields[]","Stat.offer_id"),
348
        ("fields[]","Stat.datetime"),
349
        ("fields[]","Offer.name"),
350
        ("fields[]","Stat.conversion_status"),
351
        ("fields[]","Stat.conversion_sale_amount"),
352
        ("fields[]","Stat.payout"),
353
        ("fields[]","Stat.ip"),
354
        ("fields[]","Stat.ad_id"),
355
        ("fields[]","Stat.affiliate_info1"),
356
        ("sort[Stat.datetime]","desc"),
357
        ("filters[Stat.date][conditional]","BETWEEN"),
358
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
359
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
360
        ("data_start",startDate.strftime('%Y-%m-%d')),
361
        ("data_end",endDate.strftime('%Y-%m-%d')),
362
        ("Method","getConversions"),
363
        ("NetworkId","jasper"),
364
        ("SessionToken",token),
365
    )
366
    #Encode the parameters
367
    return urllib.urlencode(parameters)
368
 
369
def main():
370
 
14301 amit.gupta 371
    store = getStore(4)
18254 amit.gupta 372
    store.scrapeStoreOrders()
14220 amit.gupta 373
    #store._isSubOrderActive(8, "5970688907")
374
    #store.scrapeAffiliate()
18254 amit.gupta 375
    #store.parseOrderRawHtml(112345, "subtagId", 1122323,  "html", 'http://www.saholic.com/pay-success?paymentId=1798123')
16967 amit.gupta 376
    #print store.getCashbackAmount('1011378', 500)
14220 amit.gupta 377
 
378
 
379
if __name__ == '__main__':
380
    main()
381
 
382
def todict(obj, classkey=None):
383
    if isinstance(obj, dict):
384
        data = {}
385
        for (k, v) in obj.items():
386
            data[k] = todict(v, classkey)
387
        return data
388
    elif hasattr(obj, "_ast"):
389
        return todict(obj._ast())
390
    elif hasattr(obj, "__iter__"):
391
        return [todict(v, classkey) for v in obj]
392
    elif hasattr(obj, "__dict__"):
393
        data = dict([(key, todict(value, classkey)) 
394
            for key, value in obj.__dict__.iteritems() 
395
            if not callable(value) and not key.startswith('_')])
396
        if classkey is not None and hasattr(obj, "__class__"):
397
            data[classkey] = obj.__class__.__name__
398
        return data
399
    else:
400
        return obj