Subversion Repositories SmartDukaan

Rev

Rev 14403 | Rev 14406 | 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 = {}
14404 amit.gupta 274
                        print divStr
14398 amit.gupta 275
                        for line in divStr.split("<br />"):
276
                            if "Suborder ID" in line:
277
                                merchantSubOrderId = re.findall(r'\d+', line)[0]
278
                                #break if suborder is inactive   
279
                                subOrder =  self._isSubOrderActive(order, merchantSubOrderId)
280
                                if subOrder is None:
281
                                    subOrders = self.parseSubOrder(subOrderElement, placedOn)
282
                                    self.db.merchantOrder.update({"orderId":order['orderId']},{'$push':{"subOrders":{"$each":todict(subOrders)}}})
283
                                    print "Added new suborders to Order id - " + order['orderId']
284
                                    closed = False
285
                                    breakFlag = True
286
                                    break
287
                                elif subOrder['closed']:
288
                                    breakFlag = True
289
                                    break
290
                                else: 
291
                                    findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": merchantSubOrderId}
292
                            elif "Status" in line:
14403 amit.gupta 293
                                print line
14398 amit.gupta 294
                                detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
295
                                updateMap["subOrders.$.detailedStatus"] = detailedStatus
296
                                status = self._getStatusFromDetailedStatus(detailedStatus) 
297
                                closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
298
                                updateMap["subOrders.$.status"] = status
299
                                if detailedStatus == 'Closed For Vendor Reallocation':
300
                                    #if it is more than 6hours mark closed.
301
                                    closeAt = subOrder.get("closeAt") 
302
                                    if closeAt is None:
303
                                        closeAt = datetime.now() + timedelta(hours=6)
304
                                        updateMap["subOrders.$.closeAt"] = datetime.strftime(closeAt,"%Y-%m-%d %H:%M:%S")
305
                                    else:
306
                                        closeAt = datetime.strptime(closeAt,"%Y-%m-%d %H:%M:%S")
307
                                        if datetime.now() > closeAt:
308
                                            closedStatus = True
309
 
310
 
311
                                if closedStatus:
312
                                    #if status is closed then change the paybackStatus accordingly
313
                                    updateMap["subOrders.$.closed"] = True
314
                                    if status == Store.ORDER_DELIVERED:
315
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
316
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
317
                                    elif status == Store.ORDER_CANCELLED:
318
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
319
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
320
 
13760 amit.gupta 321
                                else:
14398 amit.gupta 322
                                    closed = False
323
                            elif "Est. Shipping Date" in line:
324
                                estimatedShippingDate = line.split(":")[1].strip()
325
                                updateMap["subOrders.$.estimatedShippingDate"] = estimatedShippingDate
326
                            elif "Est. Delivery Date" in line:
327
                                estimatedDeliveryDate = line.split(":")[1].strip()
328
                                updateMap["subOrders.$.estimatedDeliveryDate"] = estimatedDeliveryDate
329
                            elif "Courier Name" in line:
330
                                courierName = line.split(":")[1].strip()
331
                                updateMap["subOrders.$.courierName"] = courierName
332
                            elif "Tracking No" in line:
333
                                trackingNumber = line.split(":")[1].strip()
334
                                updateMap["subOrders.$.trackingNumber"] = trackingNumber
335
 
336
                        if breakFlag:
337
                            break
338
 
339
                        bulk.find(findMap).update({'$set' : updateMap})
340
                    bulk.find({'orderId': order['orderId']}).update({'$set':{'closed': closed}})
341
                result = bulk.execute()
342
                pprint(result)
343
            except:
344
                tprint("Could not update " + str(order['orderId']))
345
                traceback.print_exc()                
13576 amit.gupta 346
 
347
 
13569 amit.gupta 348
    def _saveToAffiliate(self, offers):
13725 amit.gupta 349
        if offers is None or len(offers)==0:
350
            print "no affiliate have been pushed"
351
            return
13576 amit.gupta 352
        collection = self.db.snapdealOrderAffiliateInfo
13569 amit.gupta 353
        try:
354
            collection.insert(offers,continue_on_error=True)
355
        except pymongo.errors.DuplicateKeyError as e:
356
            print e.details
357
 
358
 
359
    def _getAllOffers(self, br, token):
360
        allOffers = []
361
        nextPage = 1  
362
        while True:
363
            data = getPostData(token, nextPage)
364
            response = br.open(POST_URL, data)
13680 amit.gupta 365
            rmap = json.loads(ungzipResponse(response))
13569 amit.gupta 366
            if rmap is not None:
367
                rmap = rmap['response']
368
                if rmap is not None and len(rmap['errors'])==0:
369
                    allOffers += rmap['data']['data']
370
            nextPage += 1
371
            if rmap['data']['pageCount']<nextPage:
372
                break
373
 
374
        return allOffers
375
 
376
    def covertToObj(self,offer):
377
        offerData = offer['Stat']
378
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
379
                              offerData['datetime'], offerData['payout'], offer['Offer']['name'], offerData['ip'], offerData['conversion_sale_amount'])
13680 amit.gupta 380
 
13569 amit.gupta 381
        return offer1
382
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
13680 amit.gupta 383
    endDate=date.today() + timedelta(days=1)
384
    startDate=endDate - timedelta(days=31)
13569 amit.gupta 385
 
386
    parameters = (
387
        ("page",str(page)),
388
        ("limit",str(limit)),
389
        ("fields[]","Stat.offer_id"),
390
        ("fields[]","Stat.datetime"),
391
        ("fields[]","Offer.name"),
392
        ("fields[]","Stat.conversion_status"),
393
        ("fields[]","Stat.conversion_sale_amount"),
394
        ("fields[]","Stat.payout"),
395
        ("fields[]","Stat.ip"),
396
        ("fields[]","Stat.ad_id"),
397
        ("fields[]","Stat.affiliate_info1"),
398
        ("sort[Stat.datetime]","desc"),
399
        ("filters[Stat.date][conditional]","BETWEEN"),
400
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
401
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
402
        ("data_start",startDate.strftime('%Y-%m-%d')),
403
        ("data_end",endDate.strftime('%Y-%m-%d')),
404
        ("Method","getConversions"),
405
        ("NetworkId","jasper"),
406
        ("SessionToken",token),
407
    )
408
    #Encode the parameters
409
    return urllib.urlencode(parameters)
410
 
411
def main():
14239 amit.gupta 412
    print todict([1,2,"3"])
14402 amit.gupta 413
    store = getStore(3)
414
    store.scrapeStoreOrders()
13662 amit.gupta 415
    #store._isSubOrderActive(8, "5970688907")
13760 amit.gupta 416
    #store.scrapeAffiliate()
13576 amit.gupta 417
    #store.parseOrderRawHtml(12345, "subtagId", 122323,  "html", 'https://m.snapdeal.com/purchaseMobileComplete?code=1f4166d13ea799b65aa9dea68b3e9e70&order=4509499363')
13569 amit.gupta 418
 
419
 
13576 amit.gupta 420
 
421
def todict(obj, classkey=None):
422
    if isinstance(obj, dict):
423
        data = {}
424
        for (k, v) in obj.items():
425
            data[k] = todict(v, classkey)
426
        return data
427
    elif hasattr(obj, "_ast"):
428
        return todict(obj._ast())
429
    elif hasattr(obj, "__iter__"):
430
        return [todict(v, classkey) for v in obj]
431
    elif hasattr(obj, "__dict__"):
432
        data = dict([(key, todict(value, classkey)) 
433
            for key, value in obj.__dict__.iteritems() 
434
            if not callable(value) and not key.startswith('_')])
435
        if classkey is not None and hasattr(obj, "__class__"):
436
            data[classkey] = obj.__class__.__name__
437
        return data
438
    else:
439
        return obj
14239 amit.gupta 440
 
441
if __name__ == '__main__':
442
    main()