Subversion Repositories SmartDukaan

Rev

Rev 14447 | Rev 14459 | 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 = {
14350 amit.gupta 39
                      MStore.ORDER_PLACED : ['In Progress','N/A'],
13662 amit.gupta 40
                      MStore.ORDER_DELIVERED : ['Delivered'],
41
                      MStore.ORDER_SHIPPED : ['In Transit'],
13809 amit.gupta 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
13760 amit.gupta 129
        for subTr in subTrs:
130
            subTrString = str(subTr)
131
            if "Unit Price" in subTrString:
132
                unitPrice = re.findall(r'\d+', subTrString)[0]
133
            if "Quantity" in subTrString:
14458 amit.gupta 134
                qty = int(re.findall(r'\d+', subTrString)[0])
13760 amit.gupta 135
            elif "Offer Discount" in subTrString:
14458 amit.gupta 136
                offerDiscount +=   int(re.findall(r'\d+', subTrString)[0])
13760 amit.gupta 137
            elif "Delivery Charges" in subTrString:
138
                deliveryCharges =   re.findall(r'\d+', subTrString)[0]
139
            elif "Subtotal" in subTrString:
140
                if int(qty) > 0:
14458 amit.gupta 141
                    amountPaid =   str(int(re.findall(r'\d+', subTrString)[0]))
13760 amit.gupta 142
                else:
143
                    amountPaid =   "0"
14458 amit.gupta 144
        if qty>0:
145
            if offerDiscount == 0:
146
                amount = int(unitPrice)*int(qty)
147
            else:
148
                amount = int(unitPrice)*int(qty) - offerDiscount
13760 amit.gupta 149
 
150
        divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
151
        if len(divs)<=0:
152
            raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
153
 
154
        for div in divs:
155
            productTitle = str(subOrderElement.find("a").text)
156
            productUrl = "http://m.snapdeal.com/" + productUrl 
157
            subOrder = SubOrder(productTitle, productUrl, placedOn, amountPaid)
158
 
159
            subOrder.amountPaid = amountPaid
160
            subOrder.deliveryCharges = deliveryCharges
161
            subOrder.offerDiscount = offerDiscount
13809 amit.gupta 162
            subOrder.unitPrice = int(unitPrice)
13760 amit.gupta 163
            subOrder.productCode = re.findall(r'\d+$', productUrl)[0]
14415 amit.gupta 164
            subOrder.imgUrl = Mongo.getImgSrc(subOrder.productCode, self.store_id).get('thumbnail')
14458 amit.gupta 165
            cashbackStatus = Store.CB_NA
166
            cashbackAmount = 0
167
            percentage = 0
168
            if amount > 0:
169
                cashbackStatus = Store.CB_PENDING
170
                (cashbackAmount, percentage) = self.getCashbackAmount(subOrder.productCode, amount)
171
                if cashbackAmount <= 0:
172
                    cashbackStatus = Store.CB_NA
173
                    cashbackAmount = 0
13760 amit.gupta 174
            subOrder.cashBackStatus = cashbackStatus
175
            subOrder.cashBackAmount = cashbackAmount
14458 amit.gupta 176
            subOrder.cashBackPercentage = percentage
13760 amit.gupta 177
 
178
 
179
            trackAnchor = div.find("a")   
180
            if trackAnchor is not None:
181
                subOrder.tracingkUrl = str(trackAnchor['href'])
182
 
183
            divStr = str(div)
184
            divStr = divStr.replace("\n","").replace("\t", "")
185
 
186
            for line in divStr.split("<br />"):
187
                if "Suborder ID" in line:
188
                    subOrder.merchantSubOrderId = re.findall(r'\d+', line)[0]   
189
                elif "Status" in line:
14402 amit.gupta 190
                    print line
13760 amit.gupta 191
                    subOrder.detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
192
                elif "Est. Shipping Date" in line:
193
                    subOrder.estimatedShippingDate = line.split(":")[1].strip()
194
                elif "Est. Delivery Date" in line:
195
                    subOrder.estimatedDeliveryDate = line.split(":")[1].strip()
196
                elif "Courier Name" in line:
197
                    subOrder.courierName = line.split(":")[1].strip()
198
                elif "Tracking No" in line:
199
                    subOrder.trackingNumber = line.split(":")[1].strip()
13809 amit.gupta 200
            subOrders.append(subOrder)
201
        return subOrders
13760 amit.gupta 202
 
13576 amit.gupta 203
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13760 amit.gupta 204
                    #print merchantOrder
13796 amit.gupta 205
        resp = {}
13582 amit.gupta 206
        try:
207
            br = getBrowserObject()
208
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', orderSuccessUrl,re.IGNORECASE)[0]
13791 amit.gupta 209
            page = br.open(url)
210
            page = ungzipResponse(page)
14145 amit.gupta 211
            merchantOrder = self._parse(orderId, subTagId, userId, page, orderSuccessUrl)
14312 amit.gupta 212
            merchantOrder.orderTrackingUrl = url
213
 
214
            if self._saveToOrder(todict(merchantOrder)):
215
                resp['result'] = 'ORDER_CREATED'
216
            else:
217
                resp['result'] = 'ORDER_ALREADY_CREATED_IGNORED'
218
 
13796 amit.gupta 219
            return resp
13582 amit.gupta 220
        except:
221
            print "Error occurred"
13603 amit.gupta 222
            traceback.print_exc()
14312 amit.gupta 223
            resp['result'] = 'ORDER_NOT_CREATED'
13796 amit.gupta 224
            return resp
13781 amit.gupta 225
 
13569 amit.gupta 226
 
227
        #soup = BeautifulSoup(rawHtml,convertEntities=BeautifulSoup.HTML_ENTITIES)
228
        #soup.find(name, attrs, recursive, text)
13576 amit.gupta 229
 
230
    def _getStatusFromDetailedStatus(self, detailedStatus):
231
        for key, value in Store.OrderStatusMap.iteritems():
232
            if detailedStatus in value:
233
                return key
14289 amit.gupta 234
        print "Detailed Status need to be mapped", detailedStatus
13576 amit.gupta 235
        raise ParseException("_getStatusFromDetailedStatus", "Found new order status" + detailedStatus)
13569 amit.gupta 236
 
13610 amit.gupta 237
 
13569 amit.gupta 238
    def scrapeStoreOrders(self,):
13760 amit.gupta 239
        #collectionMap = {'palcedOn':1}
13576 amit.gupta 240
        orders = self._getActiveOrders()
241
        br = getBrowserObject()
242
        for order in orders:
14399 amit.gupta 243
            print "Found order ", order
14398 amit.gupta 244
            try:
245
                url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', order['orderSuccessUrl'],re.IGNORECASE)[0]
246
                response = br.open(url)
247
                page = ungzipResponse(response)
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()
260
                for subOrderElement in subOrders:
261
                    closed = True
262
                    divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
263
                    if len(divs)<=0:
264
                        raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
265
                    subOrder = None
266
                    breakFlag = False
267
                    for div in divs:
268
                        divStr = str(div)
269
                        divStr = divStr.replace("\n","").replace("\t", "")
270
                        updateMap = {}
271
                        for line in divStr.split("<br />"):
272
                            if "Suborder ID" in line:
273
                                merchantSubOrderId = re.findall(r'\d+', line)[0]
274
                                #break if suborder is inactive   
275
                                subOrder =  self._isSubOrderActive(order, merchantSubOrderId)
276
                                if subOrder is None:
277
                                    subOrders = self.parseSubOrder(subOrderElement, placedOn)
278
                                    self.db.merchantOrder.update({"orderId":order['orderId']},{'$push':{"subOrders":{"$each":todict(subOrders)}}})
279
                                    print "Added new suborders to Order id - " + order['orderId']
280
                                    closed = False
281
                                    breakFlag = True
282
                                    break
283
                                elif subOrder['closed']:
284
                                    breakFlag = True
285
                                    break
286
                                else: 
287
                                    findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": merchantSubOrderId}
14406 amit.gupta 288
                            elif "Status :" in line:
14398 amit.gupta 289
                                detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
290
                                updateMap["subOrders.$.detailedStatus"] = detailedStatus
291
                                status = self._getStatusFromDetailedStatus(detailedStatus) 
292
                                closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
293
                                updateMap["subOrders.$.status"] = status
294
                                if detailedStatus == 'Closed For Vendor Reallocation':
295
                                    #if it is more than 6hours mark closed.
296
                                    closeAt = subOrder.get("closeAt") 
297
                                    if closeAt is None:
298
                                        closeAt = datetime.now() + timedelta(hours=6)
299
                                        updateMap["subOrders.$.closeAt"] = datetime.strftime(closeAt,"%Y-%m-%d %H:%M:%S")
300
                                    else:
301
                                        closeAt = datetime.strptime(closeAt,"%Y-%m-%d %H:%M:%S")
302
                                        if datetime.now() > closeAt:
303
                                            closedStatus = True
304
 
305
 
306
                                if closedStatus:
307
                                    #if status is closed then change the paybackStatus accordingly
308
                                    updateMap["subOrders.$.closed"] = True
309
                                    if status == Store.ORDER_DELIVERED:
310
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
311
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
312
                                    elif status == Store.ORDER_CANCELLED:
313
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
314
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
315
 
13760 amit.gupta 316
                                else:
14398 amit.gupta 317
                                    closed = False
318
                            elif "Est. Shipping Date" in line:
319
                                estimatedShippingDate = line.split(":")[1].strip()
320
                                updateMap["subOrders.$.estimatedShippingDate"] = estimatedShippingDate
321
                            elif "Est. Delivery Date" in line:
322
                                estimatedDeliveryDate = line.split(":")[1].strip()
323
                                updateMap["subOrders.$.estimatedDeliveryDate"] = estimatedDeliveryDate
324
                            elif "Courier Name" in line:
325
                                courierName = line.split(":")[1].strip()
326
                                updateMap["subOrders.$.courierName"] = courierName
327
                            elif "Tracking No" in line:
328
                                trackingNumber = line.split(":")[1].strip()
329
                                updateMap["subOrders.$.trackingNumber"] = trackingNumber
330
 
331
                        if breakFlag:
332
                            break
333
 
334
                        bulk.find(findMap).update({'$set' : updateMap})
335
                    bulk.find({'orderId': order['orderId']}).update({'$set':{'closed': closed}})
336
                result = bulk.execute()
337
                pprint(result)
338
            except:
339
                tprint("Could not update " + str(order['orderId']))
340
                traceback.print_exc()                
13576 amit.gupta 341
 
342
 
13569 amit.gupta 343
    def _saveToAffiliate(self, offers):
13576 amit.gupta 344
        collection = self.db.snapdealOrderAffiliateInfo
14443 amit.gupta 345
        mcollection = self.db.merchantOrder
346
        for offer in offers:
347
            offer = self.covertToObj(offer)
348
            collection.update({"adId":offer.adId, "saleAmount":offer.saleAmount, "payOut":offer.payOut},{"$set":todict(offer)}, upsert=True)
14446 amit.gupta 349
            mcollection.update({"subTagId":offer.subTagId, "storeId":self.store_id, "subOrders.missingAff":True}, {"$set":{"subOrders.$.missingAff":False}})
13569 amit.gupta 350
 
351
 
352
    def _getAllOffers(self, br, token):
353
        allOffers = []
354
        nextPage = 1  
355
        while True:
356
            data = getPostData(token, nextPage)
357
            response = br.open(POST_URL, data)
13680 amit.gupta 358
            rmap = json.loads(ungzipResponse(response))
13569 amit.gupta 359
            if rmap is not None:
360
                rmap = rmap['response']
361
                if rmap is not None and len(rmap['errors'])==0:
362
                    allOffers += rmap['data']['data']
363
            nextPage += 1
364
            if rmap['data']['pageCount']<nextPage:
365
                break
366
 
367
        return allOffers
368
 
369
    def covertToObj(self,offer):
370
        offerData = offer['Stat']
371
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
372
                              offerData['datetime'], offerData['payout'], offer['Offer']['name'], offerData['ip'], offerData['conversion_sale_amount'])
14443 amit.gupta 373
        offer1.saleTime = int(time.mktime(datetime.strptime(offer1.saleDate, "%Y-%m-%d %H:%M:%S").timetuple()))
13569 amit.gupta 374
        return offer1
375
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
13680 amit.gupta 376
    endDate=date.today() + timedelta(days=1)
377
    startDate=endDate - timedelta(days=31)
13569 amit.gupta 378
 
379
    parameters = (
380
        ("page",str(page)),
381
        ("limit",str(limit)),
382
        ("fields[]","Stat.offer_id"),
383
        ("fields[]","Stat.datetime"),
384
        ("fields[]","Offer.name"),
385
        ("fields[]","Stat.conversion_status"),
386
        ("fields[]","Stat.conversion_sale_amount"),
387
        ("fields[]","Stat.payout"),
388
        ("fields[]","Stat.ip"),
389
        ("fields[]","Stat.ad_id"),
390
        ("fields[]","Stat.affiliate_info1"),
391
        ("sort[Stat.datetime]","desc"),
392
        ("filters[Stat.date][conditional]","BETWEEN"),
393
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
394
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
395
        ("data_start",startDate.strftime('%Y-%m-%d')),
396
        ("data_end",endDate.strftime('%Y-%m-%d')),
397
        ("Method","getConversions"),
398
        ("NetworkId","jasper"),
399
        ("SessionToken",token),
400
    )
401
    #Encode the parameters
402
    return urllib.urlencode(parameters)
403
 
404
def main():
14458 amit.gupta 405
    #print todict([1,2,"3"])
14402 amit.gupta 406
    store = getStore(3)
14443 amit.gupta 407
    #store.scrapeStoreOrders()
13662 amit.gupta 408
    #store._isSubOrderActive(8, "5970688907")
14458 amit.gupta 409
    #store.scrapeAffiliate()
410
    store.parseOrderRawHtml(123456, "subtagId111", 1223231,  "html", 'https://m.snapdeal.com/purchaseMobileComplete?code=0b7af8c6fd24bab5f8e76fe31e1d8953&order=5249356810')
13569 amit.gupta 411
 
412
 
13576 amit.gupta 413
 
414
def todict(obj, classkey=None):
415
    if isinstance(obj, dict):
416
        data = {}
417
        for (k, v) in obj.items():
418
            data[k] = todict(v, classkey)
419
        return data
420
    elif hasattr(obj, "_ast"):
421
        return todict(obj._ast())
422
    elif hasattr(obj, "__iter__"):
423
        return [todict(v, classkey) for v in obj]
424
    elif hasattr(obj, "__dict__"):
425
        data = dict([(key, todict(value, classkey)) 
426
            for key, value in obj.__dict__.iteritems() 
427
            if not callable(value) and not key.startswith('_')])
428
        if classkey is not None and hasattr(obj, "__class__"):
429
            data[classkey] = obj.__class__.__name__
430
        return data
431
    else:
432
        return obj
14239 amit.gupta 433
 
434
if __name__ == '__main__':
435
    main()