Subversion Repositories SmartDukaan

Rev

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