Subversion Repositories SmartDukaan

Rev

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