Subversion Repositories SmartDukaan

Rev

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