Subversion Repositories SmartDukaan

Rev

Rev 14771 | Rev 14846 | 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
'''
13576 amit.gupta 6
from BeautifulSoup import BeautifulSoup
13569 amit.gupta 7
from bson.binary import Binary
13680 amit.gupta 8
from datetime import datetime, date, timedelta
13569 amit.gupta 9
from dtr import main
13576 amit.gupta 10
from dtr.dao import AffiliateInfo, Order, SubOrder
14398 amit.gupta 11
from dtr.main import getBrowserObject, ScrapeException, getStore, ParseException, \
12
    Store as MStore, ungzipResponse, tprint
14415 amit.gupta 13
from dtr.storage import Mongo
14
from dtr.storage.Mongo import getImgSrc
14758 amit.gupta 15
from dtr.utils.utils import fetchResponseUsingProxy
13662 amit.gupta 16
from pprint import pprint
13569 amit.gupta 17
from pymongo import MongoClient
18
import json
19
import pymongo
20
import re
14443 amit.gupta 21
import time
13662 amit.gupta 22
import traceback
13569 amit.gupta 23
import urllib
13603 amit.gupta 24
 
13721 amit.gupta 25
USERNAME='profittill2@gmail.com'
13569 amit.gupta 26
PASSWORD='spice@2020'
27
AFFILIATE_URL='http://affiliate.snapdeal.com'
28
POST_URL='https://api-p03.hasoffers.com/v3/Affiliate_Report.json'
29
ORDER_TRACK_URL='https://m.snapdeal.com/orderSummary'
30
CONFIG_URL='http://affiliate.snapdeal.com/publisher/js/config.php'
31
 
13662 amit.gupta 32
class Store(MStore):
13569 amit.gupta 33
 
34
    '''
35
    This is to map order statuses of our system to order statuses of snapdeal.
36
    And our statuses will change accordingly.
37
 
38
    '''
39
    OrderStatusMap = {
14830 amit.gupta 40
                      MStore.ORDER_PLACED : ['in progress', 'pending for verification', 'not available'],
14678 amit.gupta 41
                      MStore.ORDER_DELIVERED : ['delivered'],
42
                      MStore.ORDER_SHIPPED : ['in transit'],
14830 amit.gupta 43
                      MStore.ORDER_CANCELLED : ['closed for vendor reallocation', 'cancelled', 'product returned by courier', 'returned', 'n/a']
13569 amit.gupta 44
                      }
13662 amit.gupta 45
 
46
    CONF_CB_AMOUNT = MStore.CONF_CB_DISCOUNTED_PRICE
13569 amit.gupta 47
    def __init__(self,store_id):
48
        super(Store, self).__init__(store_id)
49
 
50
    def getName(self):
51
        return "snapdeal"
52
 
53
    def scrapeAffiliate(self, startDate=None, endDate=None):
54
        br = getBrowserObject()
55
        br.open(AFFILIATE_URL)
56
        br.select_form(nr=0)
57
        br.form['data[User][password]'] = PASSWORD 
58
        br.form['data[User][email]'] = USERNAME
59
        br.submit()
60
        response = br.open(CONFIG_URL)
61
 
13680 amit.gupta 62
        token =  re.findall('"session_token":"(.*?)"', ungzipResponse(response), re.IGNORECASE)[0]
14145 amit.gupta 63
        print token
13569 amit.gupta 64
        allOffers = self._getAllOffers(br, token)
14443 amit.gupta 65
        self._saveToAffiliate(allOffers)
13569 amit.gupta 66
 
13662 amit.gupta 67
    def _setLastSaleDate(self, saleDate):
13680 amit.gupta 68
        self.db.lastSaleDtate.update({'storeId':self.store_id}, {'$set':{'saleDate':saleDate}})
13569 amit.gupta 69
 
13662 amit.gupta 70
 
71
 
72
    def _getLastSaleDate(self,):
73
        lastDaySaleObj = self.db.lastDaySale.find_one({"storeId":self.store_id})
74
        if lastDaySaleObj is None:
75
            return datetime.min
76
 
13760 amit.gupta 77
    def _parse(self, orderId, subTagId, userId, page, orderSuccessUrl):
13662 amit.gupta 78
 
13760 amit.gupta 79
        #page=page.decode("utf-8")
80
        soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
81
        #orderHead = soup.find(name, attrs, recursive, text)
82
        sections = soup.findAll("section")
83
 
84
        #print sections
85
 
86
        order = sections[1]
87
        orderTrs = order.findAll("tr")
88
 
89
        placedOn = str(orderTrs[0].findAll("td")[1].text)
90
 
91
        #Pop two section elements
92
        sections.pop(0) 
93
        sections.pop(0)
94
        subOrders = sections
95
 
96
 
97
        merchantSubOrders = []
98
 
99
        merchantOrder = Order(orderId, userId, subTagId, self.store_id, orderSuccessUrl)
14447 amit.gupta 100
        merchantOrder.placedOn = placedOn
14023 amit.gupta 101
        merchantOrder.merchantOrderId = re.findall(r'\d+', str(soup.find("div", {"class":"deals_heading"})))[1]
13760 amit.gupta 102
        for orderTr in orderTrs:
103
            orderTrString = str(orderTr)
104
            if "Total Amount" in orderTrString:
105
                merchantOrder.totalAmount = re.findall(r'\d+', orderTrString)[0]
106
            elif "Delivery Charges" in orderTrString:
107
                merchantOrder.deliveryCharges = re.findall(r'\d+', orderTrString)[0]
108
            elif "Discount Applied" in orderTrString:
109
                merchantOrder.discountApplied = re.findall(r'\d+', orderTrString)[0]
110
            elif "Paid Amount" in orderTrString:
111
                merchantOrder.paidAmount = re.findall(r'\d+', orderTrString)[0]
112
 
113
        for subOrderElement in subOrders:
13809 amit.gupta 114
            subOrders = self.parseSubOrder(subOrderElement, placedOn)                           
115
            merchantSubOrders.extend(subOrders)   
13760 amit.gupta 116
 
117
        merchantOrder.subOrders = merchantSubOrders
118
        return merchantOrder
119
 
120
    def parseSubOrder(self, subOrderElement, placedOn):
13809 amit.gupta 121
        subOrders = []
13760 amit.gupta 122
        productUrl = str(subOrderElement.find("a")['href'])
123
        subTable = subOrderElement.find("table", {"class":"lrPad"})
124
        subTrs = subTable.findAll("tr")
125
        unitPrice=None
14458 amit.gupta 126
        offerDiscount = 0
13760 amit.gupta 127
        deliveryCharges = None
128
        amountPaid = None
14458 amit.gupta 129
        amount = 0
14473 amit.gupta 130
        sdCash = 0
131
        unitPrice = 0
13760 amit.gupta 132
        for subTr in subTrs:
133
            subTrString = str(subTr)
134
            if "Unit Price" in subTrString:
14473 amit.gupta 135
                unitPrice = int(re.findall(r'\d+', subTrString)[0])
13760 amit.gupta 136
            if "Quantity" in subTrString:
14458 amit.gupta 137
                qty = int(re.findall(r'\d+', subTrString)[0])
13760 amit.gupta 138
            elif "Offer Discount" in subTrString:
14458 amit.gupta 139
                offerDiscount +=   int(re.findall(r'\d+', subTrString)[0])
14473 amit.gupta 140
            elif "SD Cash:" in subTrString:
141
                sdCash =   int(re.findall(r'\d+', subTrString)[0])
13760 amit.gupta 142
            elif "Delivery Charges" in subTrString:
14473 amit.gupta 143
                deliveryCharges =   int(re.findall(r'\d+', subTrString)[0])
13760 amit.gupta 144
            elif "Subtotal" in subTrString:
145
                if int(qty) > 0:
14459 amit.gupta 146
                    amountPaid =   int(re.findall(r'\d+', subTrString)[0])/qty
13760 amit.gupta 147
                else:
14459 amit.gupta 148
                    amountPaid =   0
14458 amit.gupta 149
        if qty>0:
14473 amit.gupta 150
            amount = unitPrice - offerDiscount - sdCash
151
            amount = 0 if amount < 0 else amount
13760 amit.gupta 152
 
14459 amit.gupta 153
        div1 = subOrderElement.find("div", {"class": "blk lrPad subordrs"})
154
        if div1 is None:
13760 amit.gupta 155
            raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
156
 
14459 amit.gupta 157
        for strDiv in str(div1).split("<div class=\"seperator\"></div>"):
158
            div = BeautifulSoup(strDiv)
13760 amit.gupta 159
            productTitle = str(subOrderElement.find("a").text)
160
            productUrl = "http://m.snapdeal.com/" + productUrl 
161
            subOrder = SubOrder(productTitle, productUrl, placedOn, amountPaid)
162
 
163
            subOrder.amountPaid = amountPaid
164
            subOrder.deliveryCharges = deliveryCharges
165
            subOrder.offerDiscount = offerDiscount
13809 amit.gupta 166
            subOrder.unitPrice = int(unitPrice)
13760 amit.gupta 167
            subOrder.productCode = re.findall(r'\d+$', productUrl)[0]
14415 amit.gupta 168
            subOrder.imgUrl = Mongo.getImgSrc(subOrder.productCode, self.store_id).get('thumbnail')
14458 amit.gupta 169
            cashbackStatus = Store.CB_NA
170
            cashbackAmount = 0
171
            percentage = 0
172
            if amount > 0:
173
                (cashbackAmount, percentage) = self.getCashbackAmount(subOrder.productCode, amount)
14473 amit.gupta 174
                if cashbackAmount > 0:
14476 amit.gupta 175
                    cashbackStatus = Store.CB_PENDING
13760 amit.gupta 176
            subOrder.cashBackStatus = cashbackStatus
177
            subOrder.cashBackAmount = cashbackAmount
14458 amit.gupta 178
            subOrder.cashBackPercentage = percentage
13760 amit.gupta 179
 
180
 
181
            trackAnchor = div.find("a")   
182
            if trackAnchor is not None:
183
                subOrder.tracingkUrl = str(trackAnchor['href'])
184
 
185
            divStr = str(div)
186
            divStr = divStr.replace("\n","").replace("\t", "")
187
 
188
            for line in divStr.split("<br />"):
189
                if "Suborder ID" in line:
190
                    subOrder.merchantSubOrderId = re.findall(r'\d+', line)[0]   
191
                elif "Status" in line:
14402 amit.gupta 192
                    print line
13760 amit.gupta 193
                    subOrder.detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
194
                elif "Est. Shipping Date" in line:
195
                    subOrder.estimatedShippingDate = line.split(":")[1].strip()
196
                elif "Est. Delivery Date" in line:
197
                    subOrder.estimatedDeliveryDate = line.split(":")[1].strip()
198
                elif "Courier Name" in line:
199
                    subOrder.courierName = line.split(":")[1].strip()
200
                elif "Tracking No" in line:
201
                    subOrder.trackingNumber = line.split(":")[1].strip()
13809 amit.gupta 202
            subOrders.append(subOrder)
203
        return subOrders
13760 amit.gupta 204
 
13576 amit.gupta 205
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13760 amit.gupta 206
                    #print merchantOrder
13796 amit.gupta 207
        resp = {}
13582 amit.gupta 208
        try:
209
            br = getBrowserObject()
210
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', orderSuccessUrl,re.IGNORECASE)[0]
13791 amit.gupta 211
            page = br.open(url)
212
            page = ungzipResponse(page)
14145 amit.gupta 213
            merchantOrder = self._parse(orderId, subTagId, userId, page, orderSuccessUrl)
14312 amit.gupta 214
            merchantOrder.orderTrackingUrl = url
215
 
216
            if self._saveToOrder(todict(merchantOrder)):
217
                resp['result'] = 'ORDER_CREATED'
218
            else:
219
                resp['result'] = 'ORDER_ALREADY_CREATED_IGNORED'
220
 
13796 amit.gupta 221
            return resp
13582 amit.gupta 222
        except:
223
            print "Error occurred"
13603 amit.gupta 224
            traceback.print_exc()
14312 amit.gupta 225
            resp['result'] = 'ORDER_NOT_CREATED'
13796 amit.gupta 226
            return resp
13781 amit.gupta 227
 
13569 amit.gupta 228
 
229
        #soup = BeautifulSoup(rawHtml,convertEntities=BeautifulSoup.HTML_ENTITIES)
230
        #soup.find(name, attrs, recursive, text)
13576 amit.gupta 231
 
232
    def _getStatusFromDetailedStatus(self, detailedStatus):
233
        for key, value in Store.OrderStatusMap.iteritems():
14678 amit.gupta 234
            if detailedStatus.lower() in value:
13576 amit.gupta 235
                return key
14675 amit.gupta 236
        print "Detailed Status need to be mapped", detailedStatus, self.store_id
13576 amit.gupta 237
        raise ParseException("_getStatusFromDetailedStatus", "Found new order status" + detailedStatus)
13569 amit.gupta 238
 
13610 amit.gupta 239
 
13569 amit.gupta 240
    def scrapeStoreOrders(self,):
13760 amit.gupta 241
        #collectionMap = {'palcedOn':1}
13576 amit.gupta 242
        orders = self._getActiveOrders()
243
        for order in orders:
14696 amit.gupta 244
            print "Order", self.store_name, order['orderId']
14398 amit.gupta 245
            try:
246
                url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', order['orderSuccessUrl'],re.IGNORECASE)[0]
14758 amit.gupta 247
                page = fetchResponseUsingProxy(url)
14398 amit.gupta 248
                #page=page.decode("utf-8")
249
                soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
250
                sections = soup.findAll("section")
251
                orderEl = sections[1]
252
                orderTrs = orderEl.findAll("tr")
253
 
254
                placedOn = str(orderTrs[0].findAll("td")[1].text)
255
                sections.pop(0)
256
                sections.pop(0)
257
 
258
                subOrders = sections
259
                bulk = self.db.merchantOrder.initialize_ordered_bulk_op()
14463 amit.gupta 260
                closed = True
14398 amit.gupta 261
                for subOrderElement in subOrders:
14463 amit.gupta 262
                    div1 = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
263
                    if len(div1)<=0:
14398 amit.gupta 264
                        raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
265
                    subOrder = None
266
                    breakFlag = False
14463 amit.gupta 267
                    for strDiv in str(div1).split("<div class=\"seperator\"></div>"):
268
                        div = BeautifulSoup(strDiv)
14398 amit.gupta 269
                        divStr = str(div)
270
                        divStr = divStr.replace("\n","").replace("\t", "")
271
                        updateMap = {}
272
                        for line in divStr.split("<br />"):
273
                            if "Suborder ID" in line:
274
                                merchantSubOrderId = re.findall(r'\d+', line)[0]
275
                                #break if suborder is inactive   
276
                                subOrder =  self._isSubOrderActive(order, merchantSubOrderId)
277
                                if subOrder is None:
278
                                    subOrders = self.parseSubOrder(subOrderElement, placedOn)
279
                                    self.db.merchantOrder.update({"orderId":order['orderId']},{'$push':{"subOrders":{"$each":todict(subOrders)}}})
280
                                    print "Added new suborders to Order id - " + order['orderId']
281
                                    closed = False
282
                                    breakFlag = True
283
                                    break
284
                                elif subOrder['closed']:
285
                                    breakFlag = True
286
                                    break
287
                                else: 
288
                                    findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": merchantSubOrderId}
14406 amit.gupta 289
                            elif "Status :" in line:
14398 amit.gupta 290
                                detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
291
                                updateMap["subOrders.$.detailedStatus"] = detailedStatus
292
                                status = self._getStatusFromDetailedStatus(detailedStatus) 
293
                                closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
294
                                updateMap["subOrders.$.status"] = status
295
                                if detailedStatus == 'Closed For Vendor Reallocation':
296
                                    #if it is more than 6hours mark closed.
297
                                    closeAt = subOrder.get("closeAt") 
298
                                    if closeAt is None:
299
                                        closeAt = datetime.now() + timedelta(hours=6)
300
                                        updateMap["subOrders.$.closeAt"] = datetime.strftime(closeAt,"%Y-%m-%d %H:%M:%S")
301
                                    else:
302
                                        closeAt = datetime.strptime(closeAt,"%Y-%m-%d %H:%M:%S")
303
                                        if datetime.now() > closeAt:
304
                                            closedStatus = True
305
 
306
 
307
                                if closedStatus:
308
                                    #if status is closed then change the paybackStatus accordingly
309
                                    updateMap["subOrders.$.closed"] = True
310
                                    if status == Store.ORDER_DELIVERED:
311
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
312
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
313
                                    elif status == Store.ORDER_CANCELLED:
314
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
315
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
316
 
13760 amit.gupta 317
                                else:
14398 amit.gupta 318
                                    closed = False
319
                            elif "Est. Shipping Date" in line:
320
                                estimatedShippingDate = line.split(":")[1].strip()
321
                                updateMap["subOrders.$.estimatedShippingDate"] = estimatedShippingDate
322
                            elif "Est. Delivery Date" in line:
323
                                estimatedDeliveryDate = line.split(":")[1].strip()
324
                                updateMap["subOrders.$.estimatedDeliveryDate"] = estimatedDeliveryDate
325
                            elif "Courier Name" in line:
326
                                courierName = line.split(":")[1].strip()
327
                                updateMap["subOrders.$.courierName"] = courierName
328
                            elif "Tracking No" in line:
329
                                trackingNumber = line.split(":")[1].strip()
330
                                updateMap["subOrders.$.trackingNumber"] = trackingNumber
331
 
332
                        if breakFlag:
14653 amit.gupta 333
                            continue
14398 amit.gupta 334
 
335
                        bulk.find(findMap).update({'$set' : updateMap})
14696 amit.gupta 336
                    bulk.find({'orderId': order['orderId']}).update({'$set':{'closed': closed,"parseError":False}})
14398 amit.gupta 337
                result = bulk.execute()
14625 amit.gupta 338
                tprint(result)
14398 amit.gupta 339
            except:
340
                tprint("Could not update " + str(order['orderId']))
14692 amit.gupta 341
                self.db.merchantOrder.update({"orderId":order['orderId']}, {"$set":{"parseError":True}})
14398 amit.gupta 342
                traceback.print_exc()                
13576 amit.gupta 343
 
344
 
13569 amit.gupta 345
    def _saveToAffiliate(self, offers):
13576 amit.gupta 346
        collection = self.db.snapdealOrderAffiliateInfo
14443 amit.gupta 347
        mcollection = self.db.merchantOrder
348
        for offer in offers:
349
            offer = self.covertToObj(offer)
350
            collection.update({"adId":offer.adId, "saleAmount":offer.saleAmount, "payOut":offer.payOut},{"$set":todict(offer)}, upsert=True)
14446 amit.gupta 351
            mcollection.update({"subTagId":offer.subTagId, "storeId":self.store_id, "subOrders.missingAff":True}, {"$set":{"subOrders.$.missingAff":False}})
13569 amit.gupta 352
 
353
 
354
    def _getAllOffers(self, br, token):
355
        allOffers = []
356
        nextPage = 1  
357
        while True:
358
            data = getPostData(token, nextPage)
359
            response = br.open(POST_URL, data)
13680 amit.gupta 360
            rmap = json.loads(ungzipResponse(response))
13569 amit.gupta 361
            if rmap is not None:
362
                rmap = rmap['response']
14492 amit.gupta 363
                print rmap
13569 amit.gupta 364
                if rmap is not None and len(rmap['errors'])==0:
365
                    allOffers += rmap['data']['data']
366
            nextPage += 1
367
            if rmap['data']['pageCount']<nextPage:
368
                break
369
 
370
        return allOffers
371
 
372
    def covertToObj(self,offer):
373
        offerData = offer['Stat']
374
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
14576 amit.gupta 375
                              offerData['datetime'], int(float(offerData['payout'])), offer['Offer']['name'], offerData['ip'], int(float(offerData['conversion_sale_amount'])))
14443 amit.gupta 376
        offer1.saleTime = int(time.mktime(datetime.strptime(offer1.saleDate, "%Y-%m-%d %H:%M:%S").timetuple()))
13569 amit.gupta 377
        return offer1
378
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
13680 amit.gupta 379
    endDate=date.today() + timedelta(days=1)
380
    startDate=endDate - timedelta(days=31)
13569 amit.gupta 381
 
382
    parameters = (
383
        ("page",str(page)),
384
        ("limit",str(limit)),
385
        ("fields[]","Stat.offer_id"),
386
        ("fields[]","Stat.datetime"),
387
        ("fields[]","Offer.name"),
388
        ("fields[]","Stat.conversion_status"),
389
        ("fields[]","Stat.conversion_sale_amount"),
390
        ("fields[]","Stat.payout"),
391
        ("fields[]","Stat.ip"),
392
        ("fields[]","Stat.ad_id"),
393
        ("fields[]","Stat.affiliate_info1"),
394
        ("sort[Stat.datetime]","desc"),
395
        ("filters[Stat.date][conditional]","BETWEEN"),
396
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
397
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
398
        ("data_start",startDate.strftime('%Y-%m-%d')),
399
        ("data_end",endDate.strftime('%Y-%m-%d')),
400
        ("Method","getConversions"),
401
        ("NetworkId","jasper"),
402
        ("SessionToken",token),
403
    )
404
    #Encode the parameters
405
    return urllib.urlencode(parameters)
406
 
407
def main():
14458 amit.gupta 408
    #print todict([1,2,"3"])
14402 amit.gupta 409
    store = getStore(3)
14771 amit.gupta 410
    print store.parseOrderRawHtml(3221, "32323", 2, "323243", "https://m.snapdeal.com/purchaseMobileComplete?code=b11f577434a63e15c369862f7ea13054&order=5677589847")
14443 amit.gupta 411
    #store.scrapeStoreOrders()
13662 amit.gupta 412
    #store._isSubOrderActive(8, "5970688907")
14653 amit.gupta 413
    #store.scrapeAffiliate()
14758 amit.gupta 414
    #store.scrapeStoreOrders()
13569 amit.gupta 415
 
416
 
14459 amit.gupta 417
 
13576 amit.gupta 418
def todict(obj, classkey=None):
419
    if isinstance(obj, dict):
420
        data = {}
421
        for (k, v) in obj.items():
422
            data[k] = todict(v, classkey)
423
        return data
424
    elif hasattr(obj, "_ast"):
425
        return todict(obj._ast())
426
    elif hasattr(obj, "__iter__"):
427
        return [todict(v, classkey) for v in obj]
428
    elif hasattr(obj, "__dict__"):
429
        data = dict([(key, todict(value, classkey)) 
430
            for key, value in obj.__dict__.iteritems() 
431
            if not callable(value) and not key.startswith('_')])
432
        if classkey is not None and hasattr(obj, "__class__"):
433
            data[classkey] = obj.__class__.__name__
434
        return data
435
    else:
436
        return obj
14239 amit.gupta 437
 
438
if __name__ == '__main__':
439
    main()